KEY-WP-0043: per-run agent identity assessment and proposed workplan

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: sonnet
Assistant-Process: 328925@bnt-lap001
Assistant-Session: 14b795fd-c632-4c01-8d28-e7e490c7b0ed
This commit is contained in:
tegwick 2026-10-24 23:49:33 +02:00
parent 41cd07c784
commit 3acc86fdef
3 changed files with 214 additions and 0 deletions

View file

@ -53,6 +53,7 @@
| workplan | KEY-WP-0040 | finished | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md |
| workplan | KEY-WP-0041 | finished | — | workplans/KEY-WP-0041-sign-in-diagnosis-page.md |
| workplan | KEY-WP-0042 | proposed | — | workplans/KEY-WP-0042-online-access-token-status.md |
| workplan | KEY-WP-0043 | proposed | — | workplans/KEY-WP-0043-per-run-agent-identity.md |
| task | ADHOC-2026-09-05-T01 | done | — | workplans/ADHOC-2026-09-05.md |
| task | ADHOC-2026-09-07-T01 | done | — | workplans/ADHOC-2026-09-07.md |
| task | ADHOC-2026-09-27-T01 | done | — | workplans/ADHOC-2026-09-27.md |
@ -226,4 +227,8 @@
| task | KEY-WP-0042-T02 | done | — | workplans/KEY-WP-0042-online-access-token-status.md |
| task | KEY-WP-0042-T03 | todo | — | workplans/KEY-WP-0042-online-access-token-status.md |
| task | KEY-WP-0042-T04 | done | — | workplans/KEY-WP-0042-online-access-token-status.md |
| task | KEY-WP-0043-T01 | todo | — | workplans/KEY-WP-0043-per-run-agent-identity.md |
| task | KEY-WP-0043-T02 | wait | — | workplans/KEY-WP-0043-per-run-agent-identity.md |
| task | KEY-WP-0043-T03 | wait | — | workplans/KEY-WP-0043-per-run-agent-identity.md |
| task | KEY-WP-0043-T04 | wait | — | workplans/KEY-WP-0043-per-run-agent-identity.md |
| intake | KEY-IN-0001 | closed | — | intakes/intakes.md |

View file

@ -0,0 +1,134 @@
# Per-run agent identity: KeyCape feasibility assessment
Date: 2026-10-24. Status: assessment only, no code or issuance changed.
Request: net-kingdom hub message b4291a84 (SEC-DSM DSM-AGENT-002/003, SI-14 of
service-identity-privilege_v0.1 draft-3). Work proposal: KEY-WP-0043.
Sources read: net-kingdom canon/standards/iam-profile_v0.3.md ("Agent Principal
Flow", "Core Claims"; read-only), src/internal/server/oidc/{token,discovery,
token_status,token_status_http}.go, server/policy/policy.go, authclient/,
config/validate.go, domain/model.go, docs/openbao-service-auth-contract.md,
SCOPE.md. flex-auth and OpenBao were not inspected; consumer statements below
are derived from KeyCape's side and must be confirmed by those owners.
## Verdict
**Feasible with changes. Not feasible today.** KeyCape has no agent principal,
no token exchange, no `act`/`actor_sub`, and no per-run input. The building
blocks (RS256 signer, per-client lifetime 1m-1h, jti + online status/revocation,
tenant and assurance claim builders, static confidential clients) exist, so the
change is an extension of the existing token endpoint, not a new service.
## What exists
| Need | Today |
| --- | --- |
| Grants | `authorization_code` (PKCE) and `client_credentials` only; discovery lists exactly those. No RFC 8693 token exchange (`grant_type` other than the two is refused, `token.go`). |
| Service token | `serveClientCredentials`: confidential client, `client_secret_basic`, registration supplies `sub` (ServiceSubject), `tenant`, `roles`, `aud`; `principal_type` is the literal `"service"`; `assurance` = aal1, methods `client_secret`, source `key-cape`. |
| Lifetime | Per-client `tokenLifetime`, validated 1m..1h for client_credentials clients (`config/validate.go`). 15 minutes and the 60-minute ceiling are already expressible and enforced at startup. |
| Tenant | Service: registration. Human: directory, with fail-closed conflict handling and a `tenant_source` provenance claim (GH-DEC-2026-013). |
| Human assurance | `assuranceClaim(mfaVerified, authTime, ...)` gives aal1/aal2 with `at` = authentication time. |
| Status/revocation | `TokenStatusStore` (jti, registration commitment, revoked flag) behind introspection/revocation when enabled. In-memory, bounded capacity. |
| Agent claims | None. `principal_type` is only `human` or `service`. `agent`, `act`, `actor_sub` do not occur in `src/`. |
Relevant constraint: `TestPrincipalTypeIsNeverSuppliedByConfiguration` pins
`principal_type` to string literals so humanity (and by extension any type) never
becomes configuration data (GH-DEC-2026-016 section 5, A-16). A new `"agent"`
path must be a literal in its own code path; this test will and should force a
conscious edit. Client registrations are static and deployment-owned
(dynamic registration is excluded by SCOPE), loaded at startup.
## Answer to the claim-by-claim question
| IAM Profile v0.3 requirement | Verdict |
| --- | --- |
| `principal_type: agent` | Add a third literal path. Small. |
| `agent` object with `id`, `mode` | New claim. `mode` comes from the registration class (a fixed property of the client, not request input); `id` is class plus run id. |
| Delegating person in `act.sub` / `actor_sub` for delegated | Needs a way to authenticate the person. Only token exchange (below) provides it; client_credentials cannot. |
| Tenant | Autonomous: registration. Delegated: must equal the person's tenant, else refuse (reuse the humanTenant conflict rule). |
| Assurance for agent credential and for delegated human authority | Agent credential: aal1 / `client_secret` (existing builder). Delegated authority: copy the verified subject token's assurance as a nested object; this nesting is not in the profile and needs an amendment (see risks). |
| 15 min target, 60 min ceiling | Already supported by per-client lifetime and its 1h validation bound; add a request-time `min(requested, client cap)` and a hard 3600 s cap in code. |
## Mechanism options
**A. client_credentials with an agent client class (autonomous mode).**
New `principalType: agent` registration (config enum, emitted only via a
literal in code), `agentClass`, `agentMode: autonomous`, `boundaryPolicy`
reference, `tokenLifetime` default 15m, cap 60m. Per-run identity: optional
`run_id` request parameter (validated charset/length) becomes part of
`agent.id` and `jti` linkage. Cannot carry a delegating person. Effort about
2-3 days including tests, discovery, status-store and verifier updates.
**B. RFC 8693 token exchange (delegated mode) - recommended for delegated.**
`grant_type=urn:ietf:params:oauth:grant-type:token-exchange`. The agent
launcher authenticates as the agent client (`client_secret_basic`) and presents
the person's KeyCape access token as `subject_token`. KeyCape verifies
signature, issuer, audience, expiry, online status (jti not revoked), the
person not suspended, and subject `principal_type=human`; requires a minimum
assurance (policy: aal2 for any privileged scope); then issues
`principal_type=agent`, `sub` = agent id, `act.sub` (and `actor_sub`) = the
person, `agent={id,mode:delegated,class}`, tenant equal to the person's,
scopes = intersection of request, client allow-list and subject scope, and
`exp = min(requested, agent cap, 3600 s, subject exp)`. Effort about 1-1.5
weeks: handler, claims, validation, status registration, negative tests.
**C. Other.** An authorization-code flow naming the agent as client (the person
logs in and consents per run) fits delegation without a new grant, but needs a
browser per run and is poor for headless sessions. Dynamic client registration
per run is rejected by SCOPE and by the A-16/static-registration reasoning.
## What KeyCape cannot provide alone
1. **Per-run is only as real as the caller.** KeyCape can attest "client X
authenticated and asked for run R". If the session itself holds the class
secret it can mint tokens for any R, and ambient authority has merely moved.
The launcher (glas-harness) must hold the class credential, mint one token
per run, and hand the session only that token. This is the central design
dependency on glas-harness.
2. **Registrations are static, restart-bound.** Adding a class is a deployment
change owned by NetKingdom/Railiance; runs are not registrations.
3. **Revocation survives only while the process does** (in-memory status store):
a restart forgets revocations, bounded by the short lifetime. Acceptable at
15 minutes; note it for 60.
4. **Delegation outliving the person's session** is a policy choice
(exp capped at subject exp is the conservative default).
## Risks
- Profile gap: nested delegated assurance, `agent.class`, `run_id` and the
boundary-policy reference are not in IAM Profile v0.3; needs a v0.4
amendment (already a proposed amendment elsewhere) or an explicit extension note.
- Confused deputy / privilege escalation in exchange: scope intersection, never
union, plus refusal when the person's token is itself an agent or service token.
- A-16 provenance: the `agent` type must not be reachable by data; keep the
literal and update the guard test deliberately.
- A downstream consumer that tests only `principal_type != "human"` could treat
agents as services. `token_status_http.go` currently rejects unknown types
(fail closed), as does `policy.Actor` (human only); both must be reviewed, not
loosened by accident.
- Secret custody for the launcher's class client goes through OpenBao routing,
not through KeyCape or chat.
## Consumer changes (to be confirmed by owners)
- **OpenBao JWT roles** (railiance-platform): current roles bind exact `iss`,
`aud`, `sub`, `principal_type`, `tenant`, role. Per-run `sub` cannot be exact;
use a stable subject (class) with the run id in a separate claim, or a glob on
`sub`. Bind `principal_type=agent`, `agent.mode`, and `tenant`; cap role token
TTL at or below the agent token lifetime; whether nested claims
(`agent.mode`, `act.sub`) are bindable or need flattened mirror claims is to be
verified by the platform owner. No agent should reach a role built for a service.
- **flex-auth**: must accept `principal_type=agent`, read `agent.*`, `act.sub`
and nested assurance, authorize on the pair (agent, delegator), and apply the
boundary policy; deny by default for unknown shapes. Not inspected here.
- **KeyCape verifiers** (`authclient`): add agent to the expected types and
checks; discovery `claims_supported` adds `act`, `agent`; `grant_types_supported`
adds token exchange when B ships.
- **glas-harness**: hold the class credential, perform the exchange, pass only
the short token, never the person's token or class secret, to the session.
## Recommended sequence
1. Owner agreement on claim shape (profile amendment) and mechanism B for
delegated, A for autonomous. 2. Phase 1: A. 3. Phase 2: B. 4. Consumer
changes, then a NetKingdom-gated live registration and rollout (no live step
is proposed here). See KEY-WP-0043.

View file

@ -0,0 +1,75 @@
---
id: KEY-WP-0043
type: workplan
title: "Issue short-lived per-run agent tokens (autonomous and delegated)"
domain: infotech
repo: key-cape
status: proposed
owner: claude-code
topic_slug: custodian
created: "2026-10-24"
updated: "2026-10-24"
flavor: extension
depends_on: []
---
Proposed from docs/proposals/2026-10-24-per-run-agent-identity-assessment.md
(verdict: feasible with changes). Serves SEC-DSM DSM-AGENT-002/003 and SI-14
of net-kingdom's Service Identity and Privilege standard. Proposal only: no
task starts before owner agreement, and nothing here deploys or registers a
client live; registration and rollout stay NetKingdom/Railiance-gated.
## Agree the agent claim shape and mechanism
```task
id: KEY-WP-0043-T01
status: todo
priority: high
```
With net-kingdom, glas-harness, flex-auth and railiance-platform: fix `agent`
(`id`, `mode`, `class`, `run_id`), `act.sub`/`actor_sub`, nested delegated
assurance, boundary-policy reference, the 15-minute default and 3600 s cap, and
the launcher-holds-the-credential model. Output: an IAM Profile amendment note.
## Autonomous agent tokens via client_credentials
```task
id: KEY-WP-0043-T02
status: wait
priority: high
```
Add `principal_type: agent` as a literal on its own path, agent client class
registration with validation (confidential, env secretRef, tenant, mode,
cap at most 1h), validated `run_id` request parameter, assurance for the
agent credential, deliberate update of the principal-type guard test,
discovery and token-status/verifier support. Tests: claims, cap, refusals.
Depends on T01.
## Delegated agent tokens via token exchange
```task
id: KEY-WP-0043-T03
status: wait
priority: high
```
Implement RFC 8693 exchange: authenticate the agent client, verify the human
subject token (signature, status, suspension, human type, minimum assurance),
tenant equality, scope intersection, `act.sub`, nested assurance, expiry at
most the subject expiry and the cap. Negative tests: agent or service subject
token, tenant mismatch, expired or revoked subject, scope widening. Depends on T02.
## Consumer coordination and rollout packet
```task
id: KEY-WP-0043-T04
status: wait
priority: medium
```
Hand OpenBao role bindings (including nested-claim bindability) to
railiance-platform, agent authorization input to flex-auth, and the launcher
flow to glas-harness. Prepare a registration and rollout packet for
NetKingdom; perform no live change.