seekrit
Docs/Vault trust model

Vault trust model

Vault is custody of your end users' credentials for an agent that acts on their behalf: a Gmail token, an API key they pasted in, a username and password. This page states what it guarantees, how that follows from where each piece runs, and the limits you should put in your own documentation.

The one-sentence version, which is the promise the product makes:

seekrit never sees your users' credentials, and your systems never store them — they are decrypted just in time inside an executor in your own Cloudflare account and applied without being handed to your agent.

Who holds what

PartyAt restIn useWhy
seekrit (API, database, staff)Cannot readCannot readStores ciphertext and metadata only — the same posture as every secret in seekrit.
Your backend, database, logsHold nothingHold nothingYour backend holds a platform key that opens connect links and reads metadata. It cannot release a credential.
Your agent (the LLM runtime)—Never receives a valueThe executor applies the credential to the outbound request and redacts any echo from the response.
Your executorHolds the only private keyDecryptsA Worker in your own Cloudflare account. Trusted by construction — it is your code, in your account.

Two breaches are therefore each insufficient on their own. A seekrit breach yields ciphertext nobody can open. A breach of your backend yields a platform key that cannot release anything. Both together still lack the executor's key, which exists only in Durable Object storage and is never exported.

How the pieces fit

  1. Capture. Your backend opens a connect session and sends the user a link to the hosted Connect page. Everything sensitive rides in the link's fragment, which browsers never send to a server — so it is not in seekrit's access logs either.
  2. Consent. The page shows your name and logo, which provider, what the agent will be able to do, and the sentence above. Consent is recorded before anything is captured, with the version of the copy the person saw.
  3. Encryption on the device. What the person types is serialized, encrypted with a fresh data key, and that key is wrapped to your executor's public key — in the browser, before anything leaves the page. The hosted API receives ciphertext. For an OAuth provider the capture happens on your executor's origin instead: it receives the provider's redirect, exchanges the code, encrypts the tokens to its own key, and files the same kind of ciphertext. Neither route puts a plaintext in front of seekrit.
  4. Release and apply. When your agent needs the credential, your backend asks your executor to make the request. The executor fetches the ciphertext from seekrit, opens it, checks the request against the provider's allow rules, injects the credential, forwards the request, and redacts every echo of the value from the response.
  5. Revoke. You or the user withdraw access; seekrit refuses every further release immediately and deletes the ciphertext.

A Connect link is https://connect.seekrit.dev/#t=…&x=…&k=…: the connect token, your executor's origin, and the thumbprint of its wrapping key. Your SDK builds that link from your own executor's identity, not from anything seekrit returns. The Connect page talks to the executor at x and refuses to continue unless the key it presents has thumbprint k. So an attacker who controls seekrit's API or database still cannot point your users at a different executor or key — the pin is built from a source they do not control.

Ciphertext is bound to its place

Every ciphertext is authenticated against where it lives: the project, the connection, the slot, the provider, and the revision. A database attacker who relabels a Gmail connection as some other provider — so the executor would inject its token into a host they control — produces a ciphertext that fails to decrypt. A stale revision replayed as the current one fails the same way.

The hosted API cannot change behaviour

Which hosts a credential may be sent to, how it is injected, and where the OAuth endpoints are all live in your executor's vault.config.ts, compiled into the Worker. seekrit stores only a display copy — names and the sentence on the consent screen. Serving the executable half from an API that could be compromised would hand that API authority over where plaintext goes, which is the same class of harm as seeing it; this is the same rule the agent access policy follows.

A browser session is a credential too

For a site with no API, the person signs in inside a browser the executor opened — a Browser Run session in your Cloudflare account, fenced to the site's domains — through a Live View link on their own phone. The executor captures the cookies and storage, encrypts them to its key, and that ciphertext is the session slot. When the agent needs the site, the executor restores the session into a fresh fenced browser and hands the agent the session id; the cookies never pass through your backend or the agent's context. The browser the agent drives can reach only the site's domains, a saved password is typed only into an input on the site's login origins, and the rotated session is written back by compare-and-swap so a revocation during a lease wins. Live View links grant control of a page: the executor returns one only to the person's own Connect page, or to you to forward to that person.

Refresh is a single writer with a journal

An OAuth access token expires; the executor refreshes it just before use and writes the new revision back. One Durable Object per connection does this, so two requests arriving with the same expiring token share one refresh. For a provider whose refresh tokens are single-use (GitHub App user tokens), the object writes a journal entry before calling the token endpoint; a journal found later with no recorded outcome means the old token may already be spent, and using it again would be the replay the provider is watching for. The executor stops, marks the connection reauth_required, and a reconnect link repairs it. Retention stays at the current revision plus one previous, so refresh tokens never accumulate.

How this relates to seekrit's zero-knowledge model

seekrit's security model is that plaintext never reaches the API and decryption happens only in clients. The executor is a client in exactly the sense seekrit run, the proxy, and the SDKs are: customer-side code decrypting in the customer's runtime. Vault adds no exception to that model. No Vault code path in seekrit's API decrypts, exchanges an OAuth code, or sees a password, a cookie, or a token.

The Connect page is JavaScript served by seekrit that sees what the person types before encrypting it. That is the same trust the dashboard already asks for (browser-side crypto served by seekrit). The defences above are aimed at a compromise of the database or API.

Limits

Put these in your own documentation too.

  • A malicious platform can modify its executor. Vault protects you from holding your users' credentials; it does not protect your users from you.
  • The agent sees what the account contains. Vault protects credentials, not content.
  • A prompt-injected agent can misuse access within scope. The executor enforces structural guardrails — allowed hosts, methods and paths — and cannot judge intent. Keep provider rules narrow.
  • A dispatched request cannot be recalled. Revocation stops the next release, not a request already in flight.

What this release covers

API-key providers and username/password logins (with an optional authenticator secret), captured on the Connect page; OAuth providers (Google, Microsoft, GitHub presets, or any authorization-code provider), captured by the executor with refresh handled there; browser sessions, captured through Live View and driven through leases; manage links; signed webhooks. The data model and the trust boundary above are the same for all of them.