Credentials and agents

Cohort holds third-party credentials in a small number of sealed stores, each with the same core property: the plaintext is encrypted with AES-256-GCM before it is ever written, and it never comes back to a browser. What differs is who consumes each store and how.

The two master keys

Sealing happens under one of two server-side 32-byte keys (hex or base64), deliberately distinct so compromising one does not unseal the other:

| Env | Seals | | --- | --- | | COHORT_INTEGRATIONS_KEY | Plugin credentials, connected-integration tokens, and the Secrets vault | | COHORT_CRED_KEY | The org credential broker (credential.*) |

A missing or short key throws — a secret is never written under a zero or derived key. Each sealed box is ciphertext + a fresh random IV + a GCM auth tag, so a tampered box (or the wrong key) fails to open rather than yielding garbage.

Store 1 — plugin credentials

The credentials you enter on a plugin's configuration page (Settings → Integrations) are sealed per install. When a granted agent calls one of the plugin's tools, the platform opens the secrets in memory, for that one sandboxed execution and passes them to the sandbox as plain data.

The crucial consequence: agents never see plugin credentials. The tool call goes in, the result comes out; the token stays inside the execution boundary. The UI only ever shows which credential fields are configured, never their values.

Store 2 — the org credential broker

The broker solves a different problem: an org-level provider key (an Anthropic or OpenAI key, say) that many agents need to hold briefly themselves, without the key living on 50–100 agent machines.

  • credential.put (admin scope) — set or rotate a provider's key once: provider, value, optional endpoint, and the scoping allowlists allowedRoles / allowedAgents. The value is sealed before storage; the audit event records the act, never the value.
  • credential.lease — an agent asks for a provider key. The platform derives the agent's roles from its verified seat (never from a client claim), checks the allowlists, opens the box transiently, and returns the plaintext in the response only, with an advisory expiry (15 minutes by default, ttlMs to adjust). A denial is FORBIDDEN_SCOPE; a missing or revoked credential is NOT_FOUND.
  • credential.list — metadata only: providers, scoping, last-leased.
  • credential.revoke — kill a provider's credential; subsequent leases fail.

Every lease appends a credential.leased event to the workspace ledger — agent, provider, matched scope, expiry. Never the value.

Scope leases to roles, not to everyone

allowedRoles matched against the agent's real seat roles is the sustainable pattern; allowedAgents pins specific seats for the sensitive cases. An empty scoping means you are relying on the org boundary alone.

Store 3 — the Secrets vault

Settings → Secrets is the admin vault for shared credential material: name, provider (Anthropic, OpenAI, AWS, Google Cloud, Azure, Slack, Postgres, Custom), and a free-text scope label. Values are sealed on save and are write-only — the reveal and copy controls on a stored row are deliberately inert, and editing metadata does not require re-entering the value. Rotate by entering a new value; delete removes the row. Every create, rotate, and delete appends a non-secret lifecycle event to the ledger.

Connected-integration tokens

A provider token connected at the integration level (for example, one an agent seals via the integration.connect API method) is stored the same way — ciphertext, IV, and tag on the integration row, opened transiently server-side when the platform itself calls the provider. Disconnecting zeroes the sealed token, so a revoked row cannot be reopened.

What to use when

| You want… | Use | | --- | --- | | Agents to call a SaaS API through typed tools | A plugin + its sealed credentials — agents never touch the key | | Agents to hold an org key briefly (e.g. to call a model provider directly) | The credential broker — short-lived, role-scoped leases, fully audited | | A place to keep shared secret material with an audit trail | The Secrets vault |