access-engine/examples/informed-decision-lifecycle/policy.md
tegwick d2d0b6feeb
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 3s
Build and Publish Container Image / build-and-push (push) Successful in 1m37s
Admit initial decision lifecycle audience and patch evaluator dependencies
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a10780-6d41-7952-9718-dfbc1f41236f
2026-10-04 21:40:39 +02:00

6 KiB

id name namespace version status package allow_ttl actions owner fixtures caring activation
informed-decision.lifecycle Decision lifecycle authoring and collaboration informed-decision:decision-memo v1 ready flexauth.informed_decision.lifecycle 30s
lifecycle.register
lifecycle.read
lifecycle.search
lifecycle.revise
lifecycle.source
lifecycle.evaluation
lifecycle.comment
lifecycle.events
lifecycle.packet
lifecycle.publish
lifecycle.request_review
lifecycle.outcomes
flex-auth
fixtures.json
profile enforce
caring-0.4.0-rc2 false
mode
local

Initial lifecycle audience

Founder direction, 2026-10-04, INFD-WP-0007: net-kingdom-admins humans and system:serviceaccount:state-hub:state-hub. This package is evaluated by Access Engine; the consumer supplies authenticated identity and immutable record facts. The evaluator's workload caller remains informed-decision/review, audience flex-auth. The State Hub subject is authenticated separately for audience informed-decision-lifecycle using CA-verified TokenReview, never a browser token.

Humans require KeyCape AAL2 MFA within 12 hours for authoring. Actual human binding retains the separate review policy's 900-second MFA requirement. This package has no accept action. An initial caller can register and search; record access requires original authorship or an explicit publication audience. Published collaborators may research, discuss and refine. Publication and requesting review require original authorship or an admitted human collaborator. Publication may name only the initial human group; explicit subjects still require admitted identity. Review routing is fixed to State Hub, decision-record class and net-kingdom-admins. Every operation is fresh; neither content nor evaluation assertions confer permission. Command digests correlate commands, not signatures. No presentation/view hash enters this policy.

The sibling registry declares identity contract profiles, following the existing review packages. These are not real human or workload identifiers and mint no identity. Authentication-derived values still come from the enforced consumer boundary (KeyCape sessions or CA-verified TokenReview). This remains a consumer claim trust boundary; a registry declaration is not independent verification.

import rego.v1

common if {
  input.tenant == "tenant:platform"
  input.subject.tenant == input.tenant
  input.resource.tenant == input.tenant
  input.resource.system == "informed-decision"
  input.context.contract == "informed-decision.lifecycle.v1"
  regex.match("^sha256:[0-9a-f]{64}$", input.context.command_digest)
  input.subject.attributes.principal_type_source == "authentication-derived"
  input.subject.attributes.tenant_source in {"registration-supplied", "directory-asserted"}
}

human if {
  input.subject.type == "human"
  "net-kingdom-admins" in input.subject.attributes.groups
  assurance := input.subject.attributes.assurance
  assurance.level == "aal2"
  assurance.mfa == true
  assurance.source == "key-cape"
  assurance.methods == ["pwd", "otp"]
  is_number(assurance.at)
  age := time.now_ns()/1000000000 - assurance.at
  age >= -30
  age <= 43200
}

workload if {
  input.subject.type == "service"
  input.subject.id == "system:serviceaccount:state-hub:state-hub"
  input.subject.attributes.authentication_audience == "informed-decision-lifecycle"
  is_string(input.subject.attributes.workload_uid)
  input.subject.attributes.workload_uid != ""
}

identity if human
identity if workload

memo_resource if {
  input.resource.type == "decision-memo"
  regex.match("^memo:[A-Za-z0-9._:-]{1,256}$", input.resource.id)
}

owner if {
  input.context.record.registered == true
  some author in [input.context.record.registered_by, input.context.record.revision_by]
  author.sub == input.subject.id
  author.kind == "person"
  human
}
owner if {
  input.context.record.registered == true
  some author in [input.context.record.registered_by, input.context.record.revision_by]
  author.sub == input.subject.id
  author.kind == "service"
  workload
}

readable if owner
readable if {
  input.context.record.registered == true
  input.subject.id in input.context.record.published_audience.subjects
}
readable if {
  human
  "net-kingdom-admins" in input.context.record.published_audience.groups
}

may_publish if owner
may_publish if {
  human
  readable
}

audience_ok if {
  a := input.context.publication_audience
  is_array(a.subjects)
  is_array(a.groups)
  count(a.subjects) <= 32
  count(a.groups) <= 32
  count(a.subjects)+count(a.groups) > 0
  every group in a.groups { group == "net-kingdom-admins" }
  every subject in a.subjects { is_string(subject); subject != ""; subject != "*" }
}

operation if {
  input.action == "lifecycle.register"
  memo_resource
  not input.context.record.registered
  input.context.memo_version == 1
}
operation if {
  input.action == "lifecycle.register"
  memo_resource
  owner
  input.context.memo_version == 1
}
operation if {
  input.action == "lifecycle.search"
  input.resource.type == "decision-collection"
  input.resource.id == "decisions"
}
operation if {
  input.action in {"lifecycle.read", "lifecycle.events", "lifecycle.packet", "lifecycle.outcomes",
                   "lifecycle.revise", "lifecycle.source", "lifecycle.evaluation", "lifecycle.comment"}
  memo_resource
  readable
}
operation if {
  input.action == "lifecycle.publish"
  memo_resource
  may_publish
  audience_ok
}
operation if {
  input.action == "lifecycle.request_review"
  memo_resource
  may_publish
  routing := input.context.review_routing
  routing.submitter == "state-hub"
  routing.memo_class == "decision-record"
  routing.review_group == "net-kingdom-admins"
}

decision := {"effect":"allow", "reason":"lifecycle_collaboration"} if {
  common
  identity
  operation
} else := {"effect":"deny", "reason":"lifecycle_scope_or_identity_refused"}
package flexauth.informed_decision.lifecycle_test
import rego.v1
import data.flexauth.informed_decision.lifecycle

test_empty_denied if {
  lifecycle.decision.effect == "deny" with input as {}
}