Skip to content

Add a skill covering keysets and client keys — which client can read whose data, and what stash env actually creates #757

Description

@coderdan

No shipped skill explains the keyset model: what a keyset is, how a client key relates to it, which keyset a given credential ends up bound to, and what that means for data written by one client and read by another.

stash-encryption mentions keysets only as a multi-tenancy feature:

const client = await EncryptionV3({
  schemas: [users],
  config: { keyset: { name: "Company A" } },
})

— "Each keyset provides full cryptographic isolation between tenants." True, but it never states the corollary that matters in practice: which keyset you get when you don't ask for one, and that two credentials in the same workspace can therefore end up on different sides of that isolation boundary without anything saying so.

The concrete gap

  • stash auth login binds the device to the workspace's default keyset (auth login's own description says "the device is bound to the workspace's default keyset").
  • stash env --name <name> creates a fresh ZeroKMS client with a new keyset — it does not add the new client to the default keyset.

That second behaviour is a defensible default. But nothing in the skills says it happens, and the two are routinely used together: stash-cli recommends stash env as "the local-dev path for runtimes that can't reach ~/.cipherstash" (Supabase Edge Functions, Workers), while stash-encryption recommends stash auth login as the credential-free local path. Follow both — which is the documented setup for an edge surface — and your Node-side tooling and your deployed function are on different keysets.

The runtime consequence is tracked in #735 (keyset mismatch produces valid-looking but un-searchable ciphertext); the documentation gap is that a developer has no way to know this from the skills.

What the skill should cover

  1. The model — workspace → keysets → clients. What a keyset owns, what a client key is, and which of the two is the isolation boundary.
  2. Default bindingstash auth login binds to the workspace default keyset; stash env mints a new keyset. State this plainly at both call sites.
  3. How to put a client on an existing keyset — including the default one. This appears to be the missing capability as much as the missing doc: there are legitimate cases for granting a new client access to the default keyset (or another named one) rather than isolating it, and stash env currently offers no flag for that. If the capability doesn't exist, the skill should say so and give the workaround.
  4. What crosses the boundary and what doesn't — specifically whether decryption, equality terms, match terms, and ORE terms are each keyset-scoped. See Keyset mismatch produces valid-looking ciphertext that is silently un-searchable (rc.3 F4) #735: an eval observed decryption succeeding while index terms diverged, which suggests these are not uniformly scoped, and that asymmetry is exactly what a skill needs to spell out.
  5. Multi-tenant guidance — the existing keyset: { name } material folds in here rather than sitting alone in stash-encryption.

Where it came from

An integration eval against stash@1.0.0-rc.4 on a Supabase Edge surface, following the documented setup for that surface exactly. The developer had no way to discover the keyset split from the skills, and diagnosed it only by dumping index terms from two clients side by side.

Related: #753, #754, #756.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions