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:
parent
41cd07c784
commit
3acc86fdef
3 changed files with 214 additions and 0 deletions
|
|
@ -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 |
|
||||
|
|
|
|||
134
docs/proposals/2026-10-24-per-run-agent-identity-assessment.md
Normal file
134
docs/proposals/2026-10-24-per-run-agent-identity-assessment.md
Normal 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.
|
||||
75
workplans/KEY-WP-0043-per-run-agent-identity.md
Normal file
75
workplans/KEY-WP-0043-per-run-agent-identity.md
Normal 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue