Add producer-owned Go approval fact validation and canonical vectors
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
This commit is contained in:
parent
eece796b8b
commit
3485415b82
14 changed files with 5655 additions and 0 deletions
137
docs/access-claim-fact-reader.md
Normal file
137
docs/access-claim-fact-reader.md
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
# Thin Access Approval fact reader — owner source contract proposal
|
||||
|
||||
Owner scope: Approval Engine / PIP. Work: APPROVAL-WP-0005-T01, selected source
|
||||
subset of APPROVAL-WP-0002-T01/T05 under CUST-WP-0080. This interprets the existing
|
||||
claim schema 0.1, not a new API/schema/signature or policy grant. Access's B2
|
||||
implementation stays DENY until its separately reviewed complete successor.
|
||||
|
||||
## Authoritative source and guarantee
|
||||
|
||||
Only server-installed, independently admitted provider origin and transport may
|
||||
GET `/v1/approvals/{id}/claim`, with least-scope `approval:read` credential and
|
||||
exact admitted store tenant. No caller URL/opener/key/verified flag, redirects,
|
||||
environment proxy, caller JSON or public same-channel key bootstrap can supply
|
||||
this provenance. Owner transport must enforce total deadline/size, exact 200
|
||||
application/json UTF-8, no encoding, duplicate-key/nonfinite/malformed JSON
|
||||
refusal, no retry and bounded errors without bearer/body leakage. Provider
|
||||
unavailability refuses this source join. Never invoke a credential-carrying
|
||||
Python subprocess as the Go adapter; reuse Go's existing transport boundary.
|
||||
|
||||
At the provider observation, `valid_now: true` AND `binding.human_control: true`
|
||||
means the configured required count of distinct entries is met and every entry
|
||||
has verified `principal_type=human`. `/entries` obtains identities/types only from
|
||||
its authenticator, rejects non-human binds for this declaration, and persists
|
||||
UNIQUE(approval_id, subject_id); `_evaluate` repeats the threshold/human check.
|
||||
It does NOT mean required_count is two, requester/actor/principal is human,
|
||||
directory-derived tenant membership, a particular MFA/assurance level, policy
|
||||
eligibility, uncompromised source, native issuer admission, or continuing
|
||||
validity after the observation. Consumer trusts this owner evaluation instead
|
||||
of fetching identities/counting approvers or revalidating human JWTs. Native
|
||||
issuer/profile acceptance is still T01, not established by synthetic vectors.
|
||||
|
||||
## Minimal typed fact validation
|
||||
|
||||
The submitted claim and fetched observation must each satisfy existing schema
|
||||
0.1 with exact JSON types; `human_control` must be present and exactly true on
|
||||
this lane despite being historically optional in the generic schema. Require
|
||||
schema_version=0.1, kind=approval-claim, issuer=approval-engine, exact expected
|
||||
approval UUID, state=valid, valid_now=true, consumed=false, reason_code=ok,
|
||||
pdp_path=true and non-null lowercase sha256 pdp_digest. Reject decision-shaped
|
||||
keys and unknown schema members (except optional informational yields_to).
|
||||
The signed/requesting service must never become a human approver by inference.
|
||||
|
||||
At one captured UTC validation instant now, require zoned RFC3339 timestamps,
|
||||
positive integral ttl_seconds, observed_at<=now<not_after and
|
||||
not_after=observed_at+ttl_seconds. Require not_before<=now<expires_at. Do not
|
||||
invent skew or extend TTL; a separately admitted clock policy is needed to
|
||||
relax the zero-skew default. The provider default TTL is 30 seconds but source
|
||||
configuration may differ; an Access-owned maximum may only shorten it.
|
||||
|
||||
Compare exact five-field act against the owner-derived normalized proposed act,
|
||||
including all nested target values; no action vocabulary translation is owned
|
||||
here. Recompute native binding.digest from those five fields and require exact
|
||||
served and expected digest correspondence. Match pdp_digest to Access's actual
|
||||
ApprovalBindingDigest over the exact normalized original request (excludes only
|
||||
top-level context.approval). Preserve execution_contract and all other context.
|
||||
Never substitute full request_digest, native act digest or a guessed Go digest.
|
||||
The PDP digest proves correspondence, not decision authenticity or approval
|
||||
source authenticity. Registry/normalization changes must fail or reissue.
|
||||
|
||||
## Original request is immutable; fresh observation stays out-of-band
|
||||
|
||||
The original submitted context.approval must remain byte/value-equivalent under
|
||||
Access's existing canonical request rules. Do not replace it, update freshness,
|
||||
change binding/declarations, or manufacture an evaluated refreshed request.
|
||||
Validate its own original freshness/validity too; a newly fetched observation
|
||||
cannot rescue a stale original claim. Require all non-freshness authority facts
|
||||
to match between the submitted and fetched claims: schema/kind/issuer/id,
|
||||
state/valid_now/consumed/reason_code, binding and validity. Optional yields_to
|
||||
is informational only. Each native binding is independently recomputed.
|
||||
Freshness is validated separately on both observations; a later observation
|
||||
may have a later freshness window without changing the signed request.
|
||||
|
||||
Return a package-private/server-created typed observation handle only after
|
||||
these checks. Access may bind its source identity/revision, observed_at,
|
||||
not_after, fetched-claim content digest and validation time into owner-produced
|
||||
signed provenance separately from unchanged request_digest and
|
||||
approval_binding_digest. This does not require changing the Approval schema;
|
||||
Access owns the minimal existing provenance representation and signer contract.
|
||||
An HTTP caller cannot instantiate this handle or set its verified state.
|
||||
|
||||
Suggested Go boundary, for Access to implement in its existing process:
|
||||
`ApprovalFactSource.Observe(ctx, approvalID) (Observation, error)` reads only
|
||||
provider bytes plus source provenance; a separate package-private validator
|
||||
joins Observation with the immutable normalized request, submitted claim and
|
||||
captured validation time. The handle retains detached raw provider bytes and
|
||||
strict parsed numbers (json.Number), not caller-installable validation flags.
|
||||
Neither this reader nor its validator evaluates policy. Access's existing
|
||||
single policy evaluator consumes the verified factual handle without rewriting
|
||||
the request. Callers may not supply a ready-made Observation on the HTTP body.
|
||||
|
||||
Effective input deadline is the minimum of ORIGINAL submitted freshness,
|
||||
fetched freshness, approval expiry, authenticated caller expiry and operation/
|
||||
decision deadlines. No equality at an expiry boundary is accepted. Refetching
|
||||
does not renew the original full request. Revocation/use may race a read; the
|
||||
PEP still performs one original exact consume CAS before effect and retains
|
||||
the same live Platform journal admission, deadlines and unknown-outcome rules.
|
||||
Access never consumes or executes. Historical confirm_consumption cannot
|
||||
substitute for this fresh predecision source validation.
|
||||
|
||||
## Go canonicalization and golden vectors
|
||||
|
||||
`examples/access-claim-reader-vectors.json` contains producer-generated native
|
||||
canonical UTF-8 bytes/digests and source lifecycle observations, plus explicitly
|
||||
mutated refusal fixtures. Python dict equality and Go decoding numbers into
|
||||
float64 lose authority distinctions: True/1, 1/1.0 and integers above 2^53 differ.
|
||||
Go default json.Marshal additionally escapes HTML/U+2028 differently. Retain
|
||||
number tokens with UseNumber and implement/test compatibility with Approval's
|
||||
existing Python json.dumps(sort_keys=True,separators=(',', ':'),ensure_ascii=False)
|
||||
format; do not silently normalize types or hash whole binding/freshness.
|
||||
UseNumber preserves number tokens but is NOT a canonicalizer: producer parsing
|
||||
of `1e-7` emits `1e-07`, `1e20` emits `1e+20`, `1.0` remains distinct from `1`,
|
||||
and negative floating zero emits `-0.0`. A signed typed Go envelope alone does
|
||||
not preserve or authenticate Python's original numeric representation.
|
||||
|
||||
These finite vectors are a minimum, not proof of a complete Python float/string
|
||||
port. A thin pilot may fail closed on unsupported numeric/string domains only
|
||||
if its actual owner-compiled operation contract excludes those values and Access
|
||||
declares/tests that boundary. It may not silently change the global Approval
|
||||
algorithm or accept a digest without checking the proposed act. Required vectors
|
||||
cover bool/int/float distinction, large integer, Unicode/HTML/U+2028, reordered
|
||||
keys, nested containers, exponent formatting and negative floating zero. These
|
||||
are authoritative producer vectors for the domains shown, not arbitrary new
|
||||
restrictions on current pilot metadata. No currently authoritative narrow numeric
|
||||
restriction was established by this review, so it cannot be presumed. If a
|
||||
small shared producer-owned Go canonicalization helper becomes necessary, scope
|
||||
it explicitly to the actual consumer need and test against these producer bytes;
|
||||
do not invent a second unverified serialization algorithm in the adapter. This
|
||||
initial review provided vectors only. Subsequent scoped WP0005-T03 supplies
|
||||
`adapters/go/approvalfact`, a producer-owned pure data implementation agreed with
|
||||
Access; it adds no credential transport/runtime. The Go consumer owner must
|
||||
return exact accepted/refused vector results and transport negatives
|
||||
(redirect/proxy/duplicate/size/deadline).
|
||||
|
||||
The superseded service-http-join-proposal-r1 B3 refreshed-evaluation wording is
|
||||
changes-requested for this immutable-context path. B2's source reader can proceed
|
||||
against this proposal after Access's own design/conformance review. No foreign
|
||||
owner assent, policy activation, live credential or native acceptance is implied.
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"reviewer": "approval-engine-owner (agent approval-engine, codex)",
|
||||
"decision": "changes_requested",
|
||||
"comment": "INTENT Purpose and exact binding/claim boundary: B2 can reuse existing GET claim schema0.1 and verified human-control predicate without republishing approver identities. Superseded B3 refreshed evaluated-request/context design cannot satisfy the exact original strict handoff; retain original context/full and approval-free digests and verify fresh provider observation out-of-band. Work APPROVAL-WP-0005-T01; source base eece796b8bbee19551f6aa07ad389cbf83cabcdb. No policy activation, issuer admission, foreign owner assent or native acceptance.",
|
||||
"reviewed_artifact": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-16/access-native-source/service-http-join-proposal-r1.json",
|
||||
"reviewed_sha256": "e0f8f12c48faf2331cf366591028577beffe617250d7005dcaf2a8b7396535fc",
|
||||
"owner_contract": "docs/access-claim-fact-reader.md",
|
||||
"owner_contract_sha256": "3dfb7987a4d9fe461b917cfa5bae7beef61bc4d9a4561fdded3e90128f5966de",
|
||||
"required_returns": [
|
||||
"Access own B2 contract/conformance review and exact native canonicalization parity; original submitted context/digests retained",
|
||||
"Owner-admitted read identity/store tenant and trusted provider transport separate from source fixture tests",
|
||||
"Actual runtime source integration and original consumer/journal/cas path before any policy eligibility successor"
|
||||
],
|
||||
"native_accepted": false
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue