Goal
Let the CLI register an agent's public key with the Defang agent-identity registry (DefangLabs/openauth PR 8 — OpenAuth as a per-tenant OIDC key registry), so AI agents can federate into clouds (AWS AssumeRoleWithWebIdentity, etc.) with locally-held keys instead of long-lived cloud credentials.
Registry contract (from the openauth PR)
POST https://<tenant>.<auth-domain>/keys — bearer-authenticated; body {project_id, stack_id, jwk, pop_jwt, ttl_seconds?}; pop_jwt is a short-lived JWT {iat, jwk_thumb} self-signed by the key being registered (proof of possession); kid is derived server-side from the RFC 7638 thumbprint; idempotent re-registration; 409 if the same JWK is used for a different (project, stack).
GET /keys — list registered keys.
DELETE /keys/:kid — revoke.
Proposed CLI design
New defang identity command group (not defang agent — pkg/agent is already the AI chat agent):
defang identity register [--ttl …] — generate an RS256 keypair if absent (private key never leaves the machine), sign the PoP JWT, POST to the tenant registry, print the kid.
defang identity list
defang identity revoke <kid>
- (follow-up)
defang identity mint --aud sts.amazonaws.com for the runtime half.
Integration points (all plumbing already exists):
- Bearer token: the stored
defang login token (pkg/tokenstore, GetExistingToken).
- Tenant URL: issuer apex from
DEFANG_ISSUER (default https://auth.defang.io) + tenant label from WhoAmI (the tenant is the subdomain).
- Project/stack:
LoadProjectNameWithFallback + stacks.Manager, same as other commands.
- Key storage:
~/.local/state/defang/identity/<tenant>/<project>/<stack>/private.pem (0600), one key per (project, stack) since the registry enforces stack isolation via 409.
- Crypto:
golang-jwt/jwt/v5 (already direct) for the PoP JWT; go-jose/v4 (already indirect → promote) for JWK marshaling + RFC 7638 thumbprint; stdlib crypto/rsa keygen. RS256 because AWS OIDC federation does not support Ed25519. No new dependencies.
- Transport: direct REST to the issuer (like the login flow in
pkg/auth/client.go), not a Fabric RPC — the registry lives in OpenAuth, not Fabric.
New code: pkg/identity/ (keygen/PoP/HTTP client) + thin src/cmd/cli/command/identity.go.
Blockers / open questions
- Gated on DefangLabs/openauth PR 8 landing and the tenant-enabled issuer being deployed at
auth.defang.io (production tenant onboarding was explicitly out of scope there).
- Should
/keys accept plain defang login access tokens, or require a scoped token from defang token (pkg/scope)? A dedicated identity:write scope would be tighter.
- Key rotation UX:
register on an existing key is idempotent; rotating means keygen + register + revoke old — worth a defang identity rotate convenience?
Draft implementation to follow as a PR from the defangdevs fork.
Goal
Let the CLI register an agent's public key with the Defang agent-identity registry (DefangLabs/openauth PR 8 — OpenAuth as a per-tenant OIDC key registry), so AI agents can federate into clouds (AWS
AssumeRoleWithWebIdentity, etc.) with locally-held keys instead of long-lived cloud credentials.Registry contract (from the openauth PR)
POST https://<tenant>.<auth-domain>/keys— bearer-authenticated; body{project_id, stack_id, jwk, pop_jwt, ttl_seconds?};pop_jwtis a short-lived JWT{iat, jwk_thumb}self-signed by the key being registered (proof of possession);kidis derived server-side from the RFC 7638 thumbprint; idempotent re-registration; 409 if the same JWK is used for a different(project, stack).GET /keys— list registered keys.DELETE /keys/:kid— revoke.Proposed CLI design
New
defang identitycommand group (notdefang agent—pkg/agentis already the AI chat agent):defang identity register [--ttl …]— generate an RS256 keypair if absent (private key never leaves the machine), sign the PoP JWT, POST to the tenant registry, print thekid.defang identity listdefang identity revoke <kid>defang identity mint --aud sts.amazonaws.comfor the runtime half.Integration points (all plumbing already exists):
defang logintoken (pkg/tokenstore,GetExistingToken).DEFANG_ISSUER(defaulthttps://auth.defang.io) + tenant label fromWhoAmI(the tenant is the subdomain).LoadProjectNameWithFallback+stacks.Manager, same as other commands.~/.local/state/defang/identity/<tenant>/<project>/<stack>/private.pem(0600), one key per(project, stack)since the registry enforces stack isolation via 409.golang-jwt/jwt/v5(already direct) for the PoP JWT;go-jose/v4(already indirect → promote) for JWK marshaling + RFC 7638 thumbprint; stdlibcrypto/rsakeygen. RS256 because AWS OIDC federation does not support Ed25519. No new dependencies.pkg/auth/client.go), not a Fabric RPC — the registry lives in OpenAuth, not Fabric.New code:
pkg/identity/(keygen/PoP/HTTP client) + thinsrc/cmd/cli/command/identity.go.Blockers / open questions
auth.defang.io(production tenant onboarding was explicitly out of scope there)./keysaccept plaindefang loginaccess tokens, or require a scoped token fromdefang token(pkg/scope)? A dedicatedidentity:writescope would be tighter.registeron an existing key is idempotent; rotating means keygen + register + revoke old — worth adefang identity rotateconvenience?Draft implementation to follow as a PR from the defangdevs fork.