2026-10-04 18:25:21 +02:00
|
|
|
# Protected lifecycle application service
|
|
|
|
|
|
|
|
|
|
2026-10-04; `INFD-WP-0007-T02/T03`. Implemented in
|
|
|
|
|
`informed_decision/lifecycle_service.py`, above `LifecycleRepository`.
|
2026-10-04 20:17:24 +02:00
|
|
|
This is a headless Python application interface. An optional runtime composition
|
|
|
|
|
enables the browser workspace; production admission is still outstanding.
|
2026-10-04 18:25:21 +02:00
|
|
|
|
|
|
|
|
## 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`,
|
2026-10-04 20:17:24 +02:00
|
|
|
`lifecycle.evaluation`, `lifecycle.comment`, `lifecycle.read`,
|
|
|
|
|
`lifecycle.events`, `lifecycle.publish`, `lifecycle.search` and `lifecycle.packet`. These are not added to the existing review-action allowlist;
|
2026-10-04 18:25:21 +02:00
|
|
|
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,
|
2026-10-04 20:17:24 +02:00
|
|
|
expected revision and idempotency key, or the read/page parameters;
|
|
|
|
|
* `record`: tenant-partitioned registration facts, original registering actor,
|
|
|
|
|
current revision and latest publication audience for the requested revision;
|
|
|
|
|
* `publication_audience`: explicit subjects/groups for a publication command.
|
|
|
|
|
|
|
|
|
|
Search uses resource `decisions`, type `decision-collection`, then independently
|
|
|
|
|
authorizes each candidate with `lifecycle.read`. A page examines at most 50
|
|
|
|
|
candidates; denied records are omitted, unavailable policy fails the request.
|
|
|
|
|
Opaque cursors are scoped to caller/tenant/query and expire after five minutes.
|
|
|
|
|
Packet access checks its own action and a revision-specific packet index; a
|
|
|
|
|
known content digest grants no retrieval capability.
|
2026-10-04 18:25:21 +02:00
|
|
|
|
|
|
|
|
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
|
2026-10-04 20:17:24 +02:00
|
|
|
the persisted observation and unchanged record/audience facts before even
|
|
|
|
|
returning an idempotent replay. New events
|
2026-10-04 18:25:21 +02:00
|
|
|
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
|
2026-10-04 20:17:24 +02:00
|
|
|
it, including unchanged record/audience facts. Repository tenant partition enforcement also applies. Record-wide event
|
2026-10-04 18:25:21 +02:00
|
|
|
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
|
2026-10-04 20:17:24 +02:00
|
|
|
events remain pending until the opt-in audit adapter obtains a receiver receipt.
|
|
|
|
|
See LifecycleWorkspaceDelivery.md for custody, reconciliation and admission.
|
2026-10-04 18:25:21 +02:00
|
|
|
|
|
|
|
|
## 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;
|
2026-10-04 20:17:24 +02:00
|
|
|
the HTTP adapter maps these to bounded envelopes without echoing
|
2026-10-04 18:25:21 +02:00
|
|
|
content, credentials or raw exception diagnostics.
|
|
|
|
|
|
2026-10-04 20:17:24 +02:00
|
|
|
Publication appends an immutable event for the current revision with an explicit
|
|
|
|
|
nonempty audience (at most 32 subjects and 32 groups, no wildcard). A successor
|
|
|
|
|
revision does not inherit publication. Audience facts are inputs to Access Engine,
|
|
|
|
|
never local permissions. Human acceptance requires the separate existing
|
|
|
|
|
presentation/disposition path; the lifecycle-to-review handoff is still open.
|
2026-10-04 18:25:21 +02:00
|
|
|
|
|
|
|
|
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.
|