HATCH bulk agent-key issuance ("A")
HATCH bulk agent-key issuance (“A”)
Section titled “HATCH bulk agent-key issuance (“A”)”Date: 2026-05-24 Status: open Supersedes: none Superseded-by: none — current
Context
Section titled “Context”HERMES Phase 1f closed cleanly (registry entry pinned at 4dd1b98dde8f per 2026-05-24-contract-sha-hash-method-fix-hermes.md), but its CAIRNET pipeline is blocked at the only manual step in onboarding: provisioning the six per-role agent keys. The operator must visit hatch.devarno.cloud/keys, toggle agentMode, type the same producer six times with different roles, then collect six reveal-once keys and paste them into the project’s .petrova/cairnet.env. For HERMES alone that is six form submits; across the registry’s roster (≈30 governed children × 6 roles = ≈180 issuances) it is roughly 90 minutes of pure tedium plus a non-trivial mis-paste failure rate.
The airlock SDK already exposes the primitive (createAgentKey({producer, role})) and the /agents page already lists AdminAgent[] read-only, so the gap is purely UX + a thin batch wrapper — not a missing capability. This is the minimum change that removes the tedium without changing substrate shape, and it is reversible.
A separate decision (2026-05-24-petrova-cairnet-onboard-wire-keys.md, “D”) covers the eventual substrate-side closure that lets petrova cairnet-onboard --wire-keys provision automatically. A and D are complementary: A unblocks today’s operator flow; D removes the UI step from future bootstraps. We are landing both because A ships in ~1 day and protects every already-bootstrapped repo while D matures.
Decision
Section titled “Decision”Add bulk agent-key issuance to HATCH, paired with a thin airlock createAgentKeys wrapper.
- Airlock — add
createAgentKeys(inputs: Array<{producer, role}>): Promise<Array<{agent, key}>>that loopscreateAgentKeyserver-side inside a single admin-session call, returning all minted credentials in one response. No new persistence semantics; reuses existing per-role<producer>-<role>-NNNagent-id generation and reveal-once key contract. - HATCH
/keys— extendIssueKeyForm.tsx’sagentModetoggle with a “Bulk” sub-mode. Input is a single textarea, one<producer>:<role>per line, validated against the CAIRNET role enum (fleet|coordinator|reviewer|planner|bridge|other). Submission callsissueAgentKeys(inputs)(new server action) which delegates toairlock.createAgentKeys. Result view renders a ready-to-paste.envblock plus a “Download as .env” button. - No
/agentspage changes in this decision. That surface remains read-only here; restructuring it is out of scope (deferred until D ships and we re-evaluate whether/keysshould fold into/agentsentirely).
Scope explicitly excludes: project-aware wizards, integration presets beyond CAIRNET, persistence of unrevealed keys, retrieval of previously-issued keys. Those belong to D or to a later HATCH restructure.
Alternatives considered
Section titled “Alternatives considered”- Move issuance to
/agentsand drop/keysagent-mode entirely — right placement long-term, but per-agent (doesn’t solve bulk) and requires/agentsto grow write affordances we’d rebuild once D lands. Reject as the next step; revisit post-D. - Project-aware “Provision keyset” wizard on
/agents— collapses six form submits to one click, but hardcodes CAIRNET’s role enum into HATCH product code. The role enum is a substrate fact (lives inschemas/agentic-emission.v1.json), so this baking is misplaced. Reject; the right home is D. - Skip the UI change entirely, only ship D — leaves every already-bootstrapped repo (HERMES today, future repos before D ships) stuck behind the manual flow. Reject; the cost of A is ~1 day and it derisks D’s rollout.
- Allow free-form
agent_idinstead of auto-generated<producer>-<role>-NNN— would let operators name agents, but breaks CAIRNET’sagent_idregex^[a-z0-9-]+-(fleet|coordinator|reviewer|planner|bridge|other)-\d{3}$and makes the agent→role mapping non-mechanical. Reject; the auto-generated shape is load-bearing.
Consequences
Section titled “Consequences”For code:
airlock: newcreateAgentKeysSDK method + admin route handler; thin loop over existing primitive.hatch:IssueKeyForm.tsxgains Bulk sub-mode (textarea + parser + multi-row result view); newissueAgentKeysserver action insrc/app/keys/actions.ts; new client SDK method insrc/lib/airlock.ts.- No DB changes; no schema migrations.
For docs:
hatch/README.md(or newhatch/docs/operator-guide.mdif it warrants its own surface) gets a Bulk-issuance section with theproducer:role-per-line format.- This ADR is referenced from D as its complement.
For in-flight phases:
- Unblocks HERMES CAIRNET emission (envelope at
hermes/.petrova/cairnet-buffer/2026-05-24T08:18:25Z-verify.jsonis still buffered awaiting key). - Removes the “PREREQUISITE A (operator, manual)” footnote from
petrova cairnet-onboardoutput for the duration before D lands.
For invariants:
- None added/removed/modified. Stays inside MR-7 (this is a new decision, not an edit of a closed one) and respects the push-down policy (substrate role enum stays in substrate; HATCH only validates against it).
References
Section titled “References”docs/decisions/2026-05-24-hatch-bulk-agent-key-issuance.mdcompanion:docs/decisions/2026-05-24-petrova-cairnet-onboard-wire-keys.md(“D”)docs/decisions/2026-05-24-contract-sha-hash-method-fix-hermes.md(preceding work that closed HERMES Phase 1f and surfaced the key-provisioning bottleneck)- Schema:
petrova-codes/contracts/vendor/agentic-emission.v1.json(role enum + agent_id regex) - Code surfaces:
hatch/src/app/keys/IssueKeyForm.tsx,hatch/src/app/keys/actions.ts,hatch/src/lib/airlock.ts:349,393
Sign-off
Section titled “Sign-off”- Subagent: planner (this session)
- Human: alex@devarno.com 2026-05-24