Contract accepted by user-engine; FLEX-DEC-2026-019 accepted. - serve-readback: dedicated pin, no enforcement routes, own caller binding - CheckRequest.purpose hashed into every digest when set; /v1/check refuses it - POST /v1/readback/application_access and GET /v1/readback/systems - optional client_binding on protected systems; readback schemas - schemaguard implements maxItems - T05 added: informed-decision needs an application.access rule over realm-level claims (its lifecycle rules read authentication_audience) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 609675@bnt-lap001 Assistant-Session: 4987a36e-eb38-4b10-803c-15a29cd78212
347 lines
9.9 KiB
JSON
347 lines
9.9 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "https://flex-auth.netkingdom/schemas/decision_envelope.schema.json",
|
|
"title": "DecisionEnvelope",
|
|
"description": "Published flex-auth decision-record contract (flex-auth.decision-record.v1). This is the PDP's output artifact under security-layer-model_v0.7 \u00a717.",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"id",
|
|
"effect",
|
|
"resource",
|
|
"subject",
|
|
"provenance"
|
|
],
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"contract_version": {
|
|
"const": "flex-auth.decision-record.v1"
|
|
},
|
|
"request_id": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"effect": {
|
|
"enum": [
|
|
"allow",
|
|
"deny",
|
|
"redact",
|
|
"audit_only",
|
|
"not_applicable"
|
|
]
|
|
},
|
|
"reason": {
|
|
"type": "string"
|
|
},
|
|
"matched_policy_version": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"matched_rule": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"resource": {
|
|
"$ref": "https://flex-auth.netkingdom/schemas/check_request.schema.json#/$defs/resource_ref"
|
|
},
|
|
"subject": {
|
|
"$ref": "https://flex-auth.netkingdom/schemas/check_request.schema.json#/$defs/subject_ref"
|
|
},
|
|
"binding": {
|
|
"$ref": "#/$defs/decision_binding"
|
|
},
|
|
"lifetime": {
|
|
"$ref": "#/$defs/lifetime"
|
|
},
|
|
"obligations": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/$defs/obligation"
|
|
}
|
|
},
|
|
"diagnostics": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
},
|
|
"provenance": {
|
|
"$ref": "#/$defs/provenance"
|
|
},
|
|
"signature": {
|
|
"$ref": "#/$defs/envelope_signature",
|
|
"description": "Detached Ed25519 signature over the canonical envelope with this field omitted. Additive: request_digest is unaffected (FLEX-WP-0024). mode unsigned states absence rather than omitting the field."
|
|
},
|
|
"caring": {
|
|
"$ref": "#/$defs/caring_decision_metadata"
|
|
}
|
|
},
|
|
"allOf": [
|
|
{
|
|
"if": {
|
|
"properties": {
|
|
"effect": {
|
|
"const": "allow"
|
|
}
|
|
},
|
|
"required": [
|
|
"effect"
|
|
]
|
|
},
|
|
"then": {
|
|
"required": [
|
|
"lifetime"
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"$defs": {
|
|
"decision_binding": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"subject",
|
|
"action",
|
|
"resource",
|
|
"request_digest"
|
|
],
|
|
"properties": {
|
|
"tenant": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"subject": {
|
|
"$ref": "https://flex-auth.netkingdom/schemas/check_request.schema.json#/$defs/subject_ref"
|
|
},
|
|
"action": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"resource": {
|
|
"$ref": "https://flex-auth.netkingdom/schemas/check_request.schema.json#/$defs/resource_ref"
|
|
},
|
|
"context": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
},
|
|
"purpose": {
|
|
"const": "readback",
|
|
"description": "Present only on an advisory readback decision (FLEX-DEC-2026-019); hashed into every digest."
|
|
},
|
|
"request_digest": {
|
|
"type": "string",
|
|
"pattern": "^sha256:[0-9a-f]{64}$",
|
|
"description": "Digest over the EVALUATED request, after the evaluator overlays registry facts onto subject and resource. It pins what the decision was a function of and makes the decision replayable by flex-auth for audit. It is NOT consumer-computable and must not be used as the consumer's replay test - use submitted_request_digest. With provenance.registry_snapshot_digest it identifies the evaluated request completely."
|
|
},
|
|
"approval_binding_digest": {
|
|
"type": "string",
|
|
"pattern": "^sha256:[0-9a-f]{64}$",
|
|
"description": "Present only when the request carried an approval claim on context.approval. The request digest computed with that claim removed from context, so it is stable across attaching the claim and can therefore be named by an approval's pdp_digest recorded at issue time (GH-DEC-2026-008, FLEX-DEC-2026-007). Compare claim.binding.pdp_digest to THIS value, never to request_digest: a claim can never equal the digest of the request that carries it. Not a replay identity -- two requests differing only in which approval was presented share this value and their decisions do not. request_digest remains the replay identity."
|
|
},
|
|
"submitted_request_digest": {
|
|
"type": "string",
|
|
"pattern": "^sha256:[0-9a-f]{64}$",
|
|
"description": "RequestDigest over the request exactly as the consumer sent it, before the evaluator overlays registry facts. This is the digest a PEP compares for security-layer-model 6.4 obligation 2; request_digest is over the enriched request and is not consumer-computable. See docs/request-enrichment.md (FLEX-DEC-2026-012)."
|
|
}
|
|
}
|
|
},
|
|
"obligation": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"type"
|
|
],
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"parameters": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
},
|
|
"lifetime": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"kind",
|
|
"expires_at"
|
|
],
|
|
"properties": {
|
|
"kind": {
|
|
"enum": [
|
|
"ttl"
|
|
]
|
|
},
|
|
"ttl": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"not_before": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"expires_at": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
}
|
|
}
|
|
},
|
|
"provenance": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"evaluator",
|
|
"mode"
|
|
],
|
|
"properties": {
|
|
"evaluator": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"mode": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"policy_package": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"policy_version": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"policy_package_digest": {
|
|
"type": "string",
|
|
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
},
|
|
"registry_snapshot_digest": {
|
|
"type": "string",
|
|
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
},
|
|
"directory_etag": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"input_claim_digests": {
|
|
"type": "object",
|
|
"additionalProperties": {
|
|
"type": "string",
|
|
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
}
|
|
},
|
|
"decision_time": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"caller": {
|
|
"$ref": "#/$defs/caller_provenance",
|
|
"description": "Authenticated caller of the PDP, not decision material. Additive: request_digest is unaffected because the caller is not in binding (FLEX-DEC-2026-009)."
|
|
}
|
|
}
|
|
},
|
|
"envelope_signature": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"mode"
|
|
],
|
|
"properties": {
|
|
"mode": {
|
|
"enum": [
|
|
"signed",
|
|
"unsigned"
|
|
]
|
|
},
|
|
"alg": {
|
|
"const": "ed25519"
|
|
},
|
|
"kid": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"value": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"description": "base64url Ed25519 signature over json.Marshal of the envelope with signature omitted"
|
|
}
|
|
}
|
|
},
|
|
"caller_provenance": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"mode"
|
|
],
|
|
"properties": {
|
|
"mode": {
|
|
"enum": [
|
|
"disabled",
|
|
"warn",
|
|
"enforce"
|
|
]
|
|
},
|
|
"principal": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"audience": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"not_after": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"description": "Reviewed token exp, RFC3339 UTC. A principal recorded under warn was observed, not enforced."
|
|
}
|
|
}
|
|
},
|
|
"caring_decision_metadata": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"profile"
|
|
],
|
|
"properties": {
|
|
"profile": {
|
|
"const": "caring-0.4.0-rc2"
|
|
},
|
|
"descriptor": {
|
|
"$ref": "https://flex-auth.netkingdom/schemas/caring_access_descriptor.schema.json"
|
|
},
|
|
"restrictions_evaluated": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "https://flex-auth.netkingdom/schemas/caring_access_descriptor.schema.json#/$defs/restriction"
|
|
},
|
|
"uniqueItems": true
|
|
},
|
|
"exposure_modes": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "https://flex-auth.netkingdom/schemas/caring_access_descriptor.schema.json#/$defs/exposure_mode"
|
|
},
|
|
"uniqueItems": true
|
|
},
|
|
"derived_capabilities": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "https://flex-auth.netkingdom/schemas/caring_access_descriptor.schema.json#/$defs/derived_capability"
|
|
}
|
|
},
|
|
"conformance_findings": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "https://flex-auth.netkingdom/schemas/caring_access_descriptor.schema.json#/$defs/conformance_finding"
|
|
}
|
|
},
|
|
"exposure_event": {
|
|
"$ref": "https://flex-auth.netkingdom/schemas/caring_access_descriptor.schema.json#/$defs/exposure_event"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|