Admit named-recipient discovery for lifecycle reviews
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a10780-6d41-7952-9718-dfbc1f41236f
This commit is contained in:
parent
1487bd901f
commit
f234d0175a
3 changed files with 99 additions and 8 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"package": "informed-decision.compact-sitting",
|
"package": "informed-decision.compact-sitting",
|
||||||
"version": "v12",
|
"package_digest": "sha256:31be5985c0c1e6b0c3a039ed7310d1e94daf523a94f6d926e6e8d759d3711873",
|
||||||
"package_digest": "sha256:c5387f7d198dc39ab5967e0c218ce05cc779693565c8875e3035e45ec002a2db"
|
"version": "v13"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
id: informed-decision.compact-sitting
|
id: informed-decision.compact-sitting
|
||||||
name: Compact sitting exact-record human review
|
name: Compact sitting exact-record human review
|
||||||
namespace: informed-decision:decision-memo
|
namespace: informed-decision:decision-memo
|
||||||
version: v12
|
version: v13
|
||||||
status: ready
|
status: ready
|
||||||
package: flexauth.informed_decision.compact_sitting
|
package: flexauth.informed_decision.compact_sitting
|
||||||
allow_ttl: 30s
|
allow_ttl: 30s
|
||||||
|
|
@ -129,6 +129,13 @@ route; it does not assert directory membership in tenant:platform.
|
||||||
Only a real human uses accept. This package neither issues nor consumes
|
Only a real human uses accept. This package neither issues nor consumes
|
||||||
approval. Native pins: `docs/evidence/2026-09-15-sitting-approval-creates.json`.
|
approval. Native pins: `docs/evidence/2026-09-15-sitting-approval-creates.json`.
|
||||||
|
|
||||||
|
v13 adds attribute-based `list` for the retained named recipient. The consumer
|
||||||
|
supplies `review_recipient` from the immutable memo and verifies the live native
|
||||||
|
approval requester, human-control flag and binding digest before asking the PDP.
|
||||||
|
Listing has the existing twelve-hour MFA window; read and all acts retain 900
|
||||||
|
seconds. Existing exact-record branches retain precedence. A list allow exposes
|
||||||
|
only overview metadata and cannot stand in for a read or act decision.
|
||||||
|
|
||||||
```rego
|
```rego
|
||||||
import rego.v1
|
import rego.v1
|
||||||
|
|
||||||
|
|
@ -265,7 +272,7 @@ exact_id if {
|
||||||
|
|
||||||
attribute_attempt if {
|
attribute_attempt if {
|
||||||
not exact_id
|
not exact_id
|
||||||
input.action in {"read", "acknowledge", "accept", "return", "discuss", "decline"}
|
input.action in {"read", "acknowledge", "accept", "return", "discuss", "decline", "list"}
|
||||||
ctx := object.get(input, "context", {})
|
ctx := object.get(input, "context", {})
|
||||||
some key in {"review_group", "memo_class", "intake_submitter", "approval_requester", "approval_human_control"}
|
some key in {"review_group", "memo_class", "intake_submitter", "approval_requester", "approval_human_control"}
|
||||||
object.get(ctx, key, null) != null
|
object.get(ctx, key, null) != null
|
||||||
|
|
@ -288,17 +295,26 @@ identity_core if {
|
||||||
assurance.at > 0
|
assurance.at > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mfa_window := 43200 if input.action == "list" else := 900
|
||||||
|
|
||||||
mfa_fresh if {
|
mfa_fresh if {
|
||||||
identity_core
|
identity_core
|
||||||
age := time.now_ns() / 1000000000 - input.subject.attributes.assurance.at
|
age := time.now_ns() / 1000000000 - input.subject.attributes.assurance.at
|
||||||
age >= -30
|
age >= -30
|
||||||
age <= 900
|
age <= mfa_window
|
||||||
}
|
}
|
||||||
|
|
||||||
stale_mfa if {
|
stale_mfa if {
|
||||||
identity_core
|
identity_core
|
||||||
age := time.now_ns() / 1000000000 - input.subject.attributes.assurance.at
|
age := time.now_ns() / 1000000000 - input.subject.attributes.assurance.at
|
||||||
age > 900
|
age > mfa_window
|
||||||
|
}
|
||||||
|
|
||||||
|
recipient_ok if input.action != "list"
|
||||||
|
|
||||||
|
recipient_ok if {
|
||||||
|
input.action == "list"
|
||||||
|
input.context.review_recipient == input.subject.id
|
||||||
}
|
}
|
||||||
|
|
||||||
resource_ok if {
|
resource_ok if {
|
||||||
|
|
@ -433,6 +449,7 @@ decision := {"effect": "allow", "reason": "operator_admitted_compact_sitting"} i
|
||||||
} else := {"effect": "allow", "reason": "attribute_eligible"} if {
|
} else := {"effect": "allow", "reason": "attribute_eligible"} if {
|
||||||
attribute_attempt
|
attribute_attempt
|
||||||
mfa_fresh
|
mfa_fresh
|
||||||
|
recipient_ok
|
||||||
resource_ok
|
resource_ok
|
||||||
review_group_ok
|
review_group_ok
|
||||||
class_ok
|
class_ok
|
||||||
|
|
@ -451,6 +468,9 @@ decision := {"effect": "allow", "reason": "operator_admitted_compact_sitting"} i
|
||||||
} else := {"effect": "deny", "reason": "identity_refused"} if {
|
} else := {"effect": "deny", "reason": "identity_refused"} if {
|
||||||
attribute_attempt
|
attribute_attempt
|
||||||
not mfa_fresh
|
not mfa_fresh
|
||||||
|
} else := {"effect": "deny", "reason": "recipient_refused"} if {
|
||||||
|
attribute_attempt
|
||||||
|
not recipient_ok
|
||||||
} else := {"effect": "deny", "reason": "resource_refused"} if {
|
} else := {"effect": "deny", "reason": "resource_refused"} if {
|
||||||
attribute_attempt
|
attribute_attempt
|
||||||
not resource_ok
|
not resource_ok
|
||||||
|
|
@ -671,10 +691,34 @@ test_bad_memo_id_refused if {
|
||||||
eligible(object.union(attribute_input, {"resource": object.union(attribute_input.resource, {"id": "memo:NO"})})).reason == "resource_refused"
|
eligible(object.union(attribute_input, {"resource": object.union(attribute_input.resource, {"id": "memo:NO"})})).reason == "resource_refused"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_list_does_not_use_attribute_admission if {
|
test_list_requires_explicit_recipient if {
|
||||||
got := eligible(object.union(attribute_input, {"action": "list"}))
|
got := eligible(object.union(attribute_input, {"action": "list"}))
|
||||||
got.effect == "deny"
|
got.effect == "deny"
|
||||||
got.reason == "compact_sitting_scope_or_identity_refused"
|
got.reason == "recipient_refused"
|
||||||
|
}
|
||||||
|
|
||||||
|
list_input := patched_context(object.union(attribute_input, {"action": "list"}), {"review_recipient": "test-human"})
|
||||||
|
|
||||||
|
test_list_admits_recipient_with_twelve_hour_mfa if {
|
||||||
|
eligible(with_at(list_input, time.now_ns() / 1000000000 - 3600)).effect == "allow"
|
||||||
|
eligible(with_at(list_input, time.now_ns() / 1000000000 - 43201)).reason == "stale_mfa"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_list_refuses_other_recipient if {
|
||||||
|
eligible(patched_context(list_input, {"review_recipient": "other-human"})).reason == "recipient_refused"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_list_does_not_relax_read_or_accept if {
|
||||||
|
every action in {"read", "accept"} {
|
||||||
|
eligible(with_at(object.union(list_input, {"action": action}), time.now_ns() / 1000000000 - 3600)).reason == "stale_mfa"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_list_retains_approval_and_group_guards if {
|
||||||
|
eligible(patched_context(list_input, {"approval_human_control": false})).reason == "human_control_false"
|
||||||
|
eligible(patched_context(list_input, {"approval_requester": "other"})).reason == "requester_mismatch"
|
||||||
|
eligible(patched_context(list_input, {"review_group": "other"})).reason == "review_group_not_verified"
|
||||||
|
eligible(patched_context(list_input, {"memo_class": "other"})).reason == "unknown_class"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_exact_record_ignores_attribute_context if {
|
test_exact_record_ignores_attribute_context if {
|
||||||
|
|
|
||||||
47
workplans/FLEX-WP-0034-informed-decision-review-discovery.md
Normal file
47
workplans/FLEX-WP-0034-informed-decision-review-discovery.md
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
---
|
||||||
|
id: FLEX-WP-0034
|
||||||
|
type: workplan
|
||||||
|
title: "Named-recipient discovery for lifecycle reviews"
|
||||||
|
domain: infotech
|
||||||
|
repo: access-engine
|
||||||
|
status: active
|
||||||
|
owner: codex
|
||||||
|
topic_slug: netkingdom
|
||||||
|
created: "2026-10-05"
|
||||||
|
updated: "2026-10-05"
|
||||||
|
origin: demand
|
||||||
|
origin_ref: INFD-WP-0007-T05
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implement and verify recipient-scoped list admission
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: FLEX-WP-0034-T01
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
Compact-sitting v13 extends the admitted lifecycle attribute branch to list.
|
||||||
|
It requires the carried immutable review recipient to equal the authenticated
|
||||||
|
human subject, existing class/submitter/group admission and live native approval
|
||||||
|
checks in the consumer. Existing exact records retain precedence. Listing uses
|
||||||
|
the existing twelve-hour assurance window; read/acts retain fifteen minutes.
|
||||||
|
32 embedded policy tests and owner InformedDecision regressions pass. Consumer
|
||||||
|
integration covers list/read separation, another recipient and engine outage.
|
||||||
|
Attribution remains unsigned; no new control claim is made.
|
||||||
|
|
||||||
|
## Release through the owning Helm workflow
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: FLEX-WP-0034-T02
|
||||||
|
status: progress
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
Founder requested deploying the authoring increment and implementing the rest of
|
||||||
|
INFD-WP-0007. Authority for its continuation: ADMINISTER @
|
||||||
|
realm:kubernetes/railiance01, activation=APPROVED. Build immutable owner artifact,
|
||||||
|
scan, dry-run/diff, then update the existing review Helm release. The consumer's
|
||||||
|
pinned version/digest must match. Retain the previous artifact/config as rollback.
|
||||||
|
No anonymous publication, additional workload identity or non-approval bind is
|
||||||
|
admitted by this change.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue