Per-job credential scoping¶
A workflow or a Commander often needs a credential to do its job — an API key, a token, a database role. The credential-scoping subsystem ensures that credential is minted narrow, held server-side, never shown to the model, and revoked when the job ends. A leaked or misused credential is scoped to one job's action-set and dies with the job.
Every seam this rests on — sealing secrets at rest, server-side injection, minting and
revoking short-lived agent keys, and the fail-closed key resolver — already existed in
the platform. The subsystem adds the per-job lifecycle ledger and the resolver that
ties them together. It ships behind a flag (HIVEMIND_CRED_SCOPING_ENABLED) that
defaults OFF.
The cred_ref seam¶
Workflows and deployments reference credentials by a cred_ref — a reference,
never an inline secret. At job start the reference resolves to an actual credential;
the model that runs the job only ever names actions, never credentials. This
indirection is what lets the resolver be swapped from "look up a stored secret" to
"mint a fresh scoped one" with no change to the runtime — the seam stays the same.
Two credential classes, one resolver¶
Mintable credentials (the goal)¶
Where the Hub can mint a scoped, short-lived credential, it does. The first backend is a minted agent key: the job authenticates to the Hub's own API as a short-lived, scoped, expiring agent principal — not as the operator's standing key. The minted key is:
- scoped — to the job's action allow-set and tier,
- short-lived — a TTL no longer than the run's budget window, and clamped to a hard ceiling regardless of what a caller requests,
- job-bound — recorded against the run/deployment job id,
- sealed at rest — stored encrypted (authenticated encryption) for cross-step server-side injection, and
- revoked — at the terminal run marks (done / failed / denied), plus a watchdog sweep as a backstop.
Opaque credentials (the fallback)¶
A third-party key the Hub cannot mint-scope — an external SaaS token, for instance — stays in the encrypted store and is injected server-side, never shown to the model. For these, the action allow-set is the capability boundary: what the credential can do is bounded by the workflow's allowed actions plus per-run rate and budget caps. Least-privilege by action-scoping when it cannot be by credential-scoping.
The lifecycle¶
flowchart LR
S[Job starts] --> RC[resolve_cred:<br/>mint scoped key<br/>TTL ≤ budget window, clamped]
RC --> SEAL[Seal into the ledger row<br/>authenticated encryption]
SEAL --> INJ[Inject server-side per action]
INJ --> USE[Run uses the credential]
USE --> END{Run ends}
END -->|done / failed / denied| REV[Revoke + stamp revoked_at]
RC -.->|TTL elapses| SWEEP[watchdog sweep:<br/>revoke expired / orphaned]
A per-job ledger row records the tenant and owner, the job id, which reference resolved to it, the credential kind, the granted scope, the sealed secret, and the mint / expiry / revoke timestamps. Expired or revoked means dead: the platform's key resolver refuses a key unless it is active and unexpired, so a revoked or timed-out credential simply stops resolving — fail-closed, at the resolution layer.
Revoke happens at the terminal run marks; a watchdog tick on the existing periodic loop sweeps expired or orphaned mints as a backstop, so a credential does not outlive its job even if the terminal path is missed.
Why this is confused-deputy-safe and least-privilege¶
- Confused-deputy-safe — injection is server-side over the model-supplied params. The model proposes the action; the server binds the credential. The model can never name, read, or exfiltrate a secret, because no tool-call schema carries a credential field. (See Security → Confused-deputy defense.)
- Least-privilege — minted credentials are scoped, short-lived, job-bound, and revoked; opaque credentials are action-bounded and rate/budget-capped. There is no standing all-powerful credential handed to a job.
Fail-closed throughout¶
- A credential mint failure means the job does not run — there is no fallback to a broader credential.
- A revoked or expired credential means the action is refused.
- Reads of the credential ledger are
(tenant, owner_id)-scoped from the verified principal, so one job can never resolve or see another owner's or tenant's minted credential.
What is designed vs. built¶
The first slice builds the minted-agent-key backend end-to-end behind the flag: mint → seal → inject → use → revoke, with the acceptance test proving a minted key resolves while live and stops resolving after revoke, that the model never sees the secret, that an expired mint is refused, and that cross-owner/cross-tenant reads are denied. Scoped external-service tokens and the opaque-credential class are designed and are follow-on work.