Add protected headless lifecycle application service
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a10780-6d41-7952-9718-dfbc1f41236f
This commit is contained in:
parent
f7094f8313
commit
b2a4b863a8
7 changed files with 439 additions and 12 deletions
90
docs/specs/LifecycleServiceContract.md
Normal file
90
docs/specs/LifecycleServiceContract.md
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# Protected lifecycle application service
|
||||
|
||||
2026-10-04; `INFD-WP-0007-T02/T03`. Implemented in
|
||||
`informed_decision/lifecycle_service.py`, above `LifecycleRepository`.
|
||||
This is a headless Python application interface. No HTTP route or production
|
||||
composition enables it yet.
|
||||
|
||||
## Caller and authority
|
||||
|
||||
The identity adapter supplies an immutable `Caller`: tenant, actor subject/kind,
|
||||
finite expiry and JSON identity attributes. This is trusted in-process context,
|
||||
not a client-controlled payload or credential. `Caller.from_human_session`
|
||||
imports the existing verified KeyCape session, preserving tenant provenance,
|
||||
authentication-derived human identity, assurance, roles and groups. It carries
|
||||
neither the browser token nor CSRF material into policy requests or stored events.
|
||||
|
||||
Agent adapters must authenticate the workload before constructing Caller. This
|
||||
increment does not add an issuer, infer a tenant from a request body, treat a
|
||||
claimed agent name as authentication, or authorize a new workload audience.
|
||||
The existing intake TokenReview audience is not silently reused for this service.
|
||||
Non-cluster/workload composition remains T02/T04 work.
|
||||
|
||||
The service consumes `PolicyClient` observations. It neither evaluates policy
|
||||
nor falls back to local permissions. New proposed action names are
|
||||
`lifecycle.register`, `lifecycle.revise`, `lifecycle.source`,
|
||||
`lifecycle.evaluation`, `lifecycle.comment`, `lifecycle.read`, and
|
||||
`lifecycle.events`. These are not added to the existing review-action allowlist;
|
||||
review and human disposition contracts remain unchanged.
|
||||
|
||||
The request carries the authenticated subject, tenant, resource `memo:<id>`,
|
||||
system `informed-decision`, type `decision-memo`, exact configured policy pin,
|
||||
and context:
|
||||
|
||||
* `contract`: `informed-decision.lifecycle.v1`;
|
||||
* `memo_version`: submitted/requested revision, or null for latest retrieval and
|
||||
record-wide event retrieval;
|
||||
* `command_digest`: SHA-256 of canonical command JSON, including mutation content,
|
||||
expected revision and idempotency key, or the read/page parameters.
|
||||
|
||||
The command digest correlates the requested operation; it is not `view_hash`,
|
||||
an approval binding digest or an authorization verdict. No presentation evidence
|
||||
is supplied as an input to the decision it presents for. Advisory content is not
|
||||
sent to the PDP in clear text. Identity attributes are supplied by trusted
|
||||
adapters, not copied from memo content.
|
||||
|
||||
## Freshness, replay and evidence
|
||||
|
||||
Every operation, including an idempotent retry, obtains a new policy observation
|
||||
and persists it using the existing Store. A deny, expired or mismatched response,
|
||||
unsupported obligation or unavailable policy service prevents the operation.
|
||||
The existing PolicyClient validates resource/subject/context, request digests,
|
||||
policy pins, response lifetime and the supported effect.
|
||||
|
||||
Mutation commands snapshot supplied content before the network call. Under the
|
||||
repository's write transaction, a guard rechecks caller and decision expiry and
|
||||
the persisted observation before even returning an idempotent replay. New events
|
||||
carry `body.policy_observation_id`; receipts replay their original event and
|
||||
observation link while the fresh retry check remains in policy history. The
|
||||
identity/policy guard does not perform network calls while holding the write lock.
|
||||
|
||||
Read authorization precedes repository lookup. Denied requests therefore cannot
|
||||
distinguish missing and existing records through lookup errors. The service
|
||||
rechecks identity and decision expiry after retrieving content and before returning
|
||||
it. Repository tenant partition enforcement also applies. Record-wide event
|
||||
access requires its own action; it does not inherit a memo-read grant.
|
||||
|
||||
The existing `decision_attributable: false` declaration remains in force. A
|
||||
matched, recorded response is not proof of signed PDP attribution. New lifecycle
|
||||
events still reside in the durable pending queue until an Audit Core contract
|
||||
and delivery adapter are admitted.
|
||||
|
||||
## Error and integration contract
|
||||
|
||||
`LifecycleError` supplies bounded status/code pairs: 401 for expired identity,
|
||||
403 for a policy deny, and 503 for unavailable or unusable policy evidence.
|
||||
The repository retains `Conflict`, `EvidenceUnavailable` and validation errors;
|
||||
the future HTTP adapter must map these to bounded envelopes without echoing
|
||||
content, credentials or raw exception diagnostics.
|
||||
|
||||
No accept/publish endpoint is implied by this interface. Human acceptance stays
|
||||
on the existing presentation/disposition path; publication needs an explicit
|
||||
audience and separately authorized transition. The service supplies the shared
|
||||
operations that subsequent HTTP and UI adapters will invoke.
|
||||
|
||||
Before enabling a listener, T02/T04 must provide: reviewed lifecycle PDP package
|
||||
and action/context fixtures; admitted human/workload identity adapters; closed
|
||||
HTTP schemas with canonical URLs and request limits; service admission and
|
||||
publication audience contracts; lifecycle audit receiver admission and readiness
|
||||
accounting. Test fixtures exercise the real PolicyClient against synthetic policy
|
||||
transport. They are not evidence of a deployed lifecycle policy grant.
|
||||
Loading…
Add table
Add a link
Reference in a new issue