feat(stage7): phase 2 — OIDC issuer in Rust broker + provisioner-scripts AWS-cred wiring#61
Merged
hanwencheng merged 16 commits intomainfrom May 4, 2026
Merged
Conversation
…pts AWS-cred wiring Phase 2 of Stage 7 (Generalized OIDC Provider). Two slices: OIDC issuer absorption (replaces TS services/oidc-stub): - crates/agentkeys-broker-server/src/oidc.rs — ES256 P-256 keypair generation, on-disk persistence (mode 0600 at ~/.agentkeys/broker/oidc-keypair.json), JWK serialization, JWT signing. - New broker routes: GET /.well-known/openid-configuration, GET /.well-known/jwks.json, POST /v1/mint-oidc-jwt (bearer-gated against the backend's /session/validate). JWT mints land in the same audit log as mint-aws-creds with requested_role=oidc_jwt. - New env vars: BROKER_OIDC_ISSUER, BROKER_OIDC_KEYPAIR_PATH, BROKER_OIDC_JWT_TTL_SECONDS (default 300, bounded [60, 3600]). - TS services/oidc-stub deleted — Rust broker now owns the surface. Provisioner-scripts AWS-cred wiring (replaces stage6-demo-env.sh sourcing): - crates/agentkeys-provisioner/src/aws_creds.rs — fetch_via_broker helper + AwsTempCreds.to_env() rendering AWS_ACCESS_KEY_ID/SECRET_ACCESS_KEY/ SESSION_TOKEN (+ AWS_REGION when set). - CLI: --broker-url / AGENTKEYS_BROKER_URL flag on agentkeys; cmd_provision fetches creds via the broker before spawning the scraper subprocess. - MCP: McpHandler::with_broker_url builder + run_stdio_with_broker entry point; daemon threads its existing --broker-url through automatically. - When --broker-url is unset the legacy stage6-demo-env.sh sourcing path still works — wiring is purely additive. Tests: broker integration (mint_flow + oidc_flow), MCP broker-env injection, provisioner aws_creds unit + stub-server tests, existing unit suite. cargo clippy --no-deps clean. Still deferred (Phase 2 federation step): public TLS hosting of \$BROKER_OIDC_ISSUER so AWS IAM accepts create-open-id-connect-provider; TEE-derived signer at oidc/issuer/v1 (heima-gaps §3). Recipe preserved in docs/stage7-wip.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ability Phase 2 was being framed as "still blocked" on a TEE-derived signer (heima-gaps §3) and on chain-anchored audit. That framing conflated two concerns: (a) the OIDC issuer architecture, which is complete and shipping, and (b) the audit-destination *backend*, which is a pluggable layer. The audit destination is interchangeable behind a single "append a tamper-evident record" interface: - Federated public chain (Heima, other Substrate parachains) - General-purpose public chain (Ethereum, Solana, Sui, Cosmos) - Permissioned / consortium chain (Hyperledger Fabric, Quorum, Aliyun BaaS) — the relevant choice for jurisdictions like China - Plain backend server (append-only SQLite, Postgres + immutable WAL, S3-with-Object-Lock) — the broker ships in this row today - Sealed log services (CloudTrail with KMS, GCP Cloud Audit Logs) - TEE-attested append-only log (Heima TEE + sealed storage, AWS Nitro + KMS, Azure Confidential Ledger) The Stage 7 broker's ~/.agentkeys/broker/audit.sqlite is a complete v0.1 audit destination on the simple-server side of this table — append-only by construction, sha256-hashed bearer tokens, audit-write-before-credentials invariant. Migrating to a chain or sealed log is a deployment-time backend swap, not a Stage-7 redesign. Changes: - docs/spec/architecture.md — new §11 "Audit destination is pluggable" with backend-class table; renumbers License → §12, Cross-references → §13 (no inbound external refs to those sections). - docs/stage7-wip.md — reframe Phase 2 as architecturally complete; add audit-destination-pluggability subsection; rename "federation step (still blocked)" to "Cloud federation deployment" (operational runbook, not architectural prerequisite); restructure TODO pickups as operational follow-ups. - docs/spec/plans/development-stages.md — add Stage 7 phase 2 row to Shipped; collapse Stage 7 Active section to operational follow-ups only. - harness/stage-7-done.sh — new completion gate covering broker tests, provisioner aws_creds tests, MCP broker-env tests, daemon + CLI rebuild, clippy on all touched crates, retired-stub directory check, broken-link guard against the deleted services/oidc-stub path. - crates/agentkeys-daemon/tests/pair_tests.rs — drop unused Session + WalletAddress imports (uncovered by adding daemon to the gate's clippy invocation; pre-existing lint, fixed for cleanliness). Stage 7 phase 1 + phase 2 now pass `bash harness/stage-7-done.sh` end to end. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new sections in docs/stage7-wip.md, slotted between the Phase 2
issuer description and the Cloud federation runbook:
1. **Operator end-to-end test (Phase 2)** — a four-terminal walk-through
that exercises every Phase 2 surface offline (broker `--skip-startup-check`
so no AWS round-trip is required):
- mock backend on :8090
- broker on :8091 with BROKER_OIDC_ISSUER set
- healthz / discovery / JWKS smoke checks
- session-create → mint-oidc-jwt round-trip with claim decode
- mint-aws-creds round-trip (live-AWS path)
- CLI `agentkeys provision` with AGENTKEYS_BROKER_URL set
- audit log inspection via sqlite3
- acceptance criteria
- negative checks for the failure modes operators triage
2. **Remote deployment** — production deployment guide for putting both
the backend and the broker on real infrastructure:
- Topology diagram (developer laptop → reverse proxy → broker → backend)
- Caveats on the in-memory mock-server (state loss on restart, no HA,
no listener-side TLS); two pragmatic v0.1 options laid out.
- Step 1: provision a host (AWS / DO / Hetzner / Linode examples)
with DNS, public-CA TLS cert, firewall rules.
- Step 2: build + install binaries to /usr/local/bin.
- Step 3: persist operator config in /etc/agentkeys/broker.env mode 0600.
- Step 4: systemd units for both backend and broker, with hardening
directives (NoNewPrivileges, ProtectSystem, ReadWritePaths,
dedicated agentkeys user, broker bound to 127.0.0.1 only).
- Step 5: nginx + Let's Encrypt config terminating TLS on
broker.example.dev → 127.0.0.1:8091.
- Step 6: client-side smoke test from a laptop with no AWS env vars.
- Step 7: cross-link to the existing Cloud federation deployment recipe.
- Operations: rotate, observe, harden — pointers to operator-runbook.md
§5 and §6, and a hard rule against exposing :8091 directly.
Cross-links:
- Architecture pluggable-audit framing referenced from the audit log
subsection.
- BROKER_OIDC_ISSUER caveat (must equal proxy server_name) called out
twice — once in step 3, once in step 6.
- Direct pointer to warn_if_non_loopback_without_tls in the broker's
main.rs as the source of truth for why broker bind=0.0.0.0 is unsafe.
Stage 7 done-gate (`bash harness/stage-7-done.sh`) still passes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…; add broker host bootstrap script
The broker no longer requires DAEMON_ACCESS_KEY_ID / DAEMON_SECRET_ACCESS_KEY
in the process environment. When both are set the broker still uses them
(legacy path for existing deployments); when either is unset the broker
delegates credential resolution to the AWS SDK's default provider chain —
named profiles in ~/.aws/credentials (AWS_PROFILE / awsp), EC2 instance
profile via IMDS, or any other link in the chain. Picked path is logged at
startup so misconfiguration is visible immediately.
Code:
- crates/agentkeys-broker-server/src/sts.rs:
- new AwsStsClient::with_default_chain(region) — no credentials_provider
override, SDK default chain handles it.
- existing from_keys(...) kept for the static-keys legacy path.
- crates/agentkeys-broker-server/src/config.rs:
- daemon_access_key_id / daemon_secret_access_key now Option<String>.
- rejects setting only one of the pair (XOR-safe at startup).
- crates/agentkeys-broker-server/src/main.rs:
- dispatches on (Some, Some) → from_keys, otherwise → with_default_chain.
- logs the chosen path; the startup-failure message lists all three credential
sources (AWS_PROFILE, instance profile, static keys).
- tests/mint_flow.rs + tests/oidc_flow.rs: pass Some(...) for the daemon
keys in BrokerConfig literals.
Docs:
- docs/operator-runbook.md §3.1: rewritten as "AWS credentials" — leads with
named profiles + awsp, then EC2 instance profile, then legacy static keys.
§3.2 lists the remaining (non-AWS-secret) env vars; §3.3 + §3.4 renumbered
for consistency. §5 rotation procedure split per credential path.
- docs/stage7-wip.md operator-E2E + remote-deploy:
- E2E walk-through: `awsp agentkeys-daemon` instead of DAEMON_* exports.
- Remote deploy §3 rewritten with three credential paths (instance
profile / named profile / legacy static), each with copy-paste
commands. systemd unit no longer needs EnvironmentFile by default;
AWS_PROFILE or instance-profile is preferred.
- docs/dev-setup.md §1: new "Other setup scripts at a glance" table
pointing at scripts/setup-dev-env.sh and scripts/setup-broker-host.sh.
§5.1/§5.2/§5.4: profile-based broker boot. §8 troubleshooting:
ExpiredToken note now references ~/.aws/credentials reload.
- agentkeys-secrets.env.example: stripped DAEMON_* (now profile-managed),
with a leading note explaining the move and a commented-out legacy
block at the bottom for operators who can't use profiles.
New automation:
- scripts/setup-broker-host.sh: idempotent bootstrap for a fresh Linux
broker host. Builds binaries, creates the agentkeys system user,
drops both systemd units, optional --with-nginx + --with-certbot.
Three credential modes via --cred-mode {instance-profile,profile,static};
default is instance-profile (zero secrets on disk). Prints
remaining manual steps (DNS A record, IAM role attach, certbot run,
client-side smoke test) on completion.
- scripts/setup-dev-env.sh: final-message pointer to setup-broker-host.sh
so operators can find it.
Stage 7 done-gate (`bash harness/stage-7-done.sh`) still passes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Last commit accidentally checked in `.claude/scheduled_tasks.lock` via `git add -A`. That directory is Claude Code's per-workspace runtime (lock files, scheduled-task index, settings.local.json) — never repo content. Adding `.claude/` to .gitignore and untracking the lock file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The script previously required all decisions up front via CLI flags. Now, when run on a TTY with no flags (or with --interactive explicitly), it walks the operator through each decision with an explanation block before the prompt — what the choice does, why it matters, and when to skip it — plus a final summary + confirmation before any destructive work. CI / non-TTY paths still work via --non-interactive + the existing flags. Behavioral changes: - Auto-detects TTY via `[[ -t 0 ]]`; can be overridden with --interactive / --non-interactive. - New --without-nginx and --without-certbot flags so non-interactive callers can be explicit instead of relying on the implicit default. - New --yes/-y to skip the final "Proceed?" prompt. - Required flags (--issuer-url, --account-id) now prompt interactively if missing; non-interactive mode still dies with a helpful redirect to the interactive walk-through. - Cred-mode is now interactive too: a numbered menu with explanations of instance-profile / profile / static and when each is appropriate. - Profile-name only prompted when cred-mode=profile. - Certbot prompt defaults to "yes" when nginx was chosen, "no" when not (because certbot --nginx has nothing to talk to without nginx). Tested: - bash -n syntax check - --help renders the header block - --non-interactive with missing --issuer-url → dies with redirect - --non-interactive with --cred-mode bogus → dies with valid-values list - --non-interactive with valid inputs → reaches summary block, then proceeds to package-manager detection - harness/stage-7-done.sh still passes Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…role
The role's old name overloaded "agent" with the project name (AgentKeys),
the AI agent the credentials are minted *for*, and the IAM identity the
broker assumes *into*. Three different things sharing one word — confusing
during operator setup, and easy to mis-script. Renaming to
`agentkeys-data-role` makes the role's job (data-plane access: S3 + SES)
explicit and unambiguous.
Code:
- BrokerConfig.agent_role_arn → data_role_arn
- BROKER_DATA_ROLE_ARN env var (primary); BROKER_AGENT_ROLE_ARN still
accepted as a fallback for unmigrated deployments — startup error
message lists both names.
- ACCOUNT_ID-derived default ARN now points at agentkeys-data-role.
- handlers/mint.rs + tests/{mint_flow,oidc_flow}.rs updated to match.
Scripts:
- scripts/setup-broker-host.sh: prompts, hand-off text, and CLI flag
references all use the new name.
- scripts/stage6-demo-env.sh: --role-arn target updated.
Docs (every non-archived reference updated):
- docs/stage6-aws-setup.md (the canonical "create the role" runbook;
added a top-of-§3 note explaining the rename + back-compat env var).
- docs/stage7-wip.md (E2E walk-through, remote deploy, federation recipe)
- docs/operator-runbook.md (env-var table, audit-DB schema comment)
- docs/dev-setup.md
- docs/spec/ses-email-architecture.md (mermaid diagrams + bucket policies)
- docs/spec/plans/development-stages.md
- wiki/tag-based-access.md (cryptographic-isolation walk-through)
- wiki/email-system.md
Verified:
- cargo test -p agentkeys-broker-server → all green
- harness/stage-7-done.sh → STAGE 7 (phase 1 + phase 2) PASSED
Migration for existing deployments:
- Old AWS deployments with the legacy role name keep working unchanged
via the BROKER_AGENT_ROLE_ARN fallback.
- New deployments should follow the renamed instructions in stage6-aws-
setup.md §3b and use BROKER_DATA_ROLE_ARN.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aligns the docs with bots.litentry.org (already canonical for the SES data-plane domain) so a litentry-following operator doesn't have to mentally substitute placeholders. broker.litentry.org is a control-plane hostname — distinct from bots.litentry.org which is the data-plane (email recipient) domain. Files: docs/stage7-wip.md, docs/operator-runbook.md, docs/dev-setup.md, scripts/setup-broker-host.sh. ops@ contact also updated to ops@litentry.org. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lout Step 1b walks through allocating an Elastic IP and upserting the broker.litentry.org A record in Route 53 — the prerequisite that lets certbot's HTTP-01 challenge resolve the host in Step 5. The "Automated path" callout above Step 1 points operators at scripts/setup-broker-host.sh as the bundled automation for Steps 2-5, making the manual walk-through the reference rather than the default.
setup-broker-host.sh wrote a full :80+:443 nginx site up front, but the
:443 block referenced LE cert files that don't exist yet. nginx then
refused to start, and `certbot --nginx` aborted at its preflight
`nginx -t` check — operators saw:
cannot load certificate "/etc/letsencrypt/live/<host>/fullchain.pem":
BIO_new_file() failed ... No such file or directory
Switch to a two-phase config:
• Phase A (no cert): :80-only with the ACME challenge location.
• Phase B (cert exists): adds the :443 ssl block with proxy_pass.
The script detects the cert file at /etc/letsencrypt/live/$ISSUER_HOST/
and writes the right config; re-running after `certbot certonly --webroot`
flips A → B automatically.
The post-run summary now points at `certbot certonly --webroot` (which
works while nginx is up on :80) instead of the broken `--nginx` flow.
A scheme-less issuer URL like `broker.litentry.org` was silently accepted and propagated into BROKER_OIDC_ISSUER. The broker then emitted JWTs with `iss: "broker.litentry.org"` (no `https://`), which AWS rejects at AssumeRoleWithWebIdentity time and which causes the documented smoke test `jq '.issuer == "https://broker.litentry.org"'` to print false. Validate up front: • require https:// (or http:// with a warning — AWS won't accept it, but local dev might). • strip a trailing slash so BROKER_OIDC_ISSUER matches the JWT iss claim byte-for-byte. Operators hitting the bad config in the wild: edit /etc/systemd/system/agentkeys-broker.service so Environment=BROKER_OIDC_ISSUER=https://<host>, daemon-reload, restart. If you've already registered the OIDC provider on AWS with the wrong URL, delete and recreate it.
…r register Two additions to the AWS federation recipe: 1. Strengthen the issuer prereq check to compare byte-for-byte (catches the scheme-less / trailing-slash bugs operators have hit), with the exact systemd-unit fix inline. 2. New "0. Check for stale provider state" subsection: list providers first, identify the three states (empty / matching / stale), and delete-and-recreate flow for the stale-URL case. 3. Step 1 now ends with `aws iam get-open-id-connect-provider` so operators can confirm AWS actually fetched the JWKS, plus a note on the LE intermediate-CA thumbprint persistence.
The Stage 6 AWS runbook and the AWS-side half of the Stage 7 doc
re-tangled themselves over time — every cross-link was "see also" rather
than "the source is here". Operators ended up reading both, then the
operator runbook, then both again to figure out which command to run.
Restructure into three focused docs, all referenced by stage:
• docs/cloud-setup.md (NEW, 548 lines) — every cloud-account resource
in one file, split internally by concern (identities → DNS →
inbound mail → IAM → OIDC federation → EC2 host → cleanup). Stage
6 vs Stage 7 vs federated-deployment is a *mode* of the same
machinery, not three separate runbooks. Tencent Cloud SimpleDM +
COS slots in at §2.2 with a 1:1 IAM→CAM mapping table — no new
file when we add it.
• docs/stage7-wip.md (-469 lines) — Phase 1 / Phase 2 bookkeeping
dropped; Stage 7 is just "the broker that issues OIDC JWTs and AWS
creds". AWS commands no longer embedded inline; the doc points at
cloud-setup.md for provisioning. Smoke test now shows how to mint
a session bearer end-to-end (the previous version left
SESSION=<bearer-from-the-backend> as a dangling placeholder).
• docs/operator-runbook.md (-86 lines) — concise. WIP/scratchpad
header gone; Phase 1/Phase 2 framing gone; threat-model section
points at the spec doc instead of duplicating it; rotation paths
fold into one §5 table.
• docs/stage6-aws-setup.md deleted; all referrers (dev-setup,
stage8-wip, ses-email-architecture, development-stages,
setup-dev-env.sh, setup-broker-host.sh) point at cloud-setup.md.
Net: 813 insertions, 1264 deletions across 10 files. Stage 7 gate
still passes (STAGE 7 phase 1 + phase 2 PASSED).
Both developer and end-user mint their own bearers via `agentkeys init` against the backend; the operator never hand-delivers tokens. Spell out the three backend-exposure modes (loopback / nginx-proxied /session/create / Heima chain RPC) with the trust trade-offs each implies, and the nginx snippet for the proxied case.
Three exploratory plans informed Stage 7+ direction. Versioning them in
the repo so the *why* of any future code lands has a paper trail.
• option-a-port-dexs-backend.md — port dexs-backend's wallet-sig +
email + OAuth flows into agentkeys-broker; add CLIENT_ID_AGENTKEYS
to Heima TEE worker (~250 LOC upstream patch).
• option-a-vs-b-port-vs-greenfield.md — A vs B side-by-side
(greenfield broker designed around AgentKeys' problem domain).
• option-c-pluggable-attestation-audit.md — pluggable auth /
wallet-provisioning / audit-anchoring; Heima becomes one plug-in
among several (Solana, Ethereum L2, AWS Nitro, S3 Object Lock,
SQLite). Zero Heima dependency in v0. Recommended for net-new
branch work; extends architecture.md §11's pluggable-audit
principle to two more layers.
Tracking issues will reference these plans when filed.
0b58b45 to
f3cc0b0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stage 7 phase 2 — OIDC issuer in the Rust broker + provisioner-scripts AWS-cred wiring + the operational bring-up that produced a live
https://broker.litentry.orgdeployment.Scope expanded beyond the original phase-2 work as bring-up surfaced bugs and doc gaps. Follow-ups for the remaining auth-side and federation-end-to-end work are filed as separate issues; this PR ships the transport surface, operational scaffolding, and architecture-decision research.
Code changes — Stage 7 phase 2
OIDC issuer absorption (replaces TS
services/oidc-stub/)Rust broker now serves the conforming OIDC discovery + JWKS surface and a bearer-gated mint-jwt endpoint:
GET/.well-known/openid-configurationcreate-open-id-connect-providerstep readsGET/.well-known/jwks.jsonkidPOST/v1/mint-oidc-jwt/session/validate, then mints a short-lived ES256 JWT carryingsub=agentkeys:agent:<wallet>,aud=sts.amazonaws.com,agentkeys_user_wallet=<wallet>oidc.rs— ES256 P-256 keypair generation, on-disk persistence (mode 0600 at~/.agentkeys/broker/oidc-keypair.json), JWK serialization, JWT signing.oidc.rs— three routes wired intolib.rs.BROKER_OIDC_ISSUER,BROKER_OIDC_KEYPAIR_PATH,BROKER_OIDC_JWT_TTL_SECONDS(default 300, bounded[60, 3600]).requested_role = "oidc_jwt"so operators see one ledger for both credential types.services/oidc-stub/deleted — Rust broker owns the surface now.Provisioner-scripts AWS-cred wiring
When
--broker-urlis set,agentkeys provision <service>(CLI) andagentkeys.provision(MCP tool) automatically:POST /v1/mint-aws-credson the broker with the daemon's session bearer.AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN(+AWS_REGIONwhen set) into the scraper subprocess env.aws_creds.rs—fetch_via_broker+AwsTempCreds::to_env().--broker-url/AGENTKEYS_BROKER_URLflag onagentkeys;cmd_provisionthreads through.McpHandler::with_broker_urlbuilder +run_stdio_with_brokerentry point; daemon's existing--broker-urlis wired through automatically.stage6-demo-env.shsourcing still works when--broker-urlis unset — wiring is purely additive.Operational bring-up + setup-script fixes
Surfaced during deploying
broker.litentry.org. Each commit is a discrete fix; together they take the host from "binaries built" to "TLS-terminated, OIDC discovery serving the rightiss":fae2478—setup-broker-host.shnow writes a two-phase nginx config: HTTP-only initially (so nginx can start without certs), full HTTPS after certbot issues. Sidesteps thecertbot --nginxchicken-and-egg where the plugin's preflightnginx -tfails on the missing cert path. Webroot/standalone is the recommended issuance flow now; documented in the script's post-run summary.b8d57ee—--issuer-urlvalidation: hard-fail on scheme-less hostnames (e.g.broker.litentry.org); strip trailing slash. Caught a real bug where the broker was advertisingiss: "broker.litentry.org"(no scheme), which AWS rejects byte-for-byte atAssumeRoleWithWebIdentitytime.4592e48—docs/stage7-wip.mdgains Step 1b: Route 53 EIP allocation + A-record upsert (commands inline; theagentkeys-adminIAM user needs ec2 + route53 perms). Plussetup-broker-host.shcallout above Step 1.44a41e5— AWS recipe incloud-setup.md§4: pre-check stale OIDC provider state, verify after register. Catches the bug where an earlier issuer URL leaves a staleiam:CreateOpenIDConnectProviderregistration.Doc consolidation
92355a8— three docs reorganized; net −453 lines (~31% shrink) across the operator-facing surface:docs/cloud-setup.md(548 lines) — every cloud-account resource in one file: identities → DNS → inbound mail → IAM → OIDC federation → EC2 host → cleanup. Stage 6 vs Stage 7 vs federated-deployment is a mode of the same machinery, not three separate runbooks. Tencent Cloud SimpleDM + COS slots in at §2.2 with a 1:1 IAM→CAM mapping table.docs/stage7-wip.mdtrimmed from 723 → 254 lines. Phase 1 / Phase 2 framing dropped; referencescloud-setup.mdfor AWS-side commands instead of inlining.docs/operator-runbook.mdtrimmed from 244 → 184 lines. WIP/scratchpad header gone; threat-model points at the spec instead of duplicating; rotation paths fold into one §5 table.docs/stage6-aws-setup.mddeleted; all referrers point atcloud-setup.md.69c0f5c— addsoperator-runbook.md§1.1 "Session bearers — how callers get them": the three backend-exposure modes (loopback / nginx-proxied / Heima chain RPC) with trust trade-offs and the nginx snippet for the proxied case. (Local-only commit at time of writing; awaits force-push to drop two earlier dev-setup-rewrite commits from origin.)Architecture-decision research —
docs/research/69d42fb— three exploratory plans versioned in the repo so the why of any future code lands has a paper trail. Tracked in follow-up issues; not implemented in this PR.option-a-port-dexs-backend.mdCLIENT_ID_AGENTKEYS)option-a-vs-b-port-vs-greenfield.mdoption-c-pluggable-attestation-audit.mdSee
docs/research/README.mdfor the index + cross-links.Deferred to follow-up issues
wallet A can read s3://$BUCKET/A/*but notB/*) is deferred until auth lands.agentkeys-broker-server; addCLIENT_ID_AGENTKEYSto Heima TEE worker. Replaces the v0.1 mock-server stub.Test plan
cargo test --workspace— all green; broker tests = 13 lib + 9 mint_flow + 6 oidc_flow.cargo clippy --no-deps -p agentkeys-broker-server -p agentkeys-provisioner -p agentkeys-mcp -p agentkeys-cli -p agentkeys-daemon --all-targets— clean.keypair_persists_across_broker_restartsinoidc_flow.rs).oidc.rs::sign_jwt_round_trips_via_public_key).broker_urlis unset, provision works as before (legacystage6-demo-env.shpath).bash harness/stage-7-done.sh—STAGE 7 (phase 1 + phase 2) PASSED.https://broker.litentry.org— DNS, EIP, nginx, certbot, AWS OIDC provider registration all completed; discovery doc + JWKS reachable over public TLS;issclaim matches registered URL byte-for-byte.🤖 Generated with Claude Code