Documentation
¶
Overview ¶
Declared parameter domains (CLC-1.19 §6.7).
The declaration is applied as a pre-comparison normalization: a value whose domain has a numeric normal form is rewritten into that form, and the ordinary relations then run unchanged. `quantity` is normalized to its number; the three string-shaped domains (`decimal-string`, `subdomain`, `path-prefix`) are decided in the *WithDomains pre-check and then neutralized so the ordinary relation does not re-compare them by shape. A domain name the revision does not define is refused with `unsupported_domain`.
Package semantics implements the CLC-v1 capability language core authorization semantics. It provides deterministic, fail-closed entailment, intersection, and decision functions.
Index ¶
- Constants
- Variables
- func CanonicalJSON(v any) ([]byte, error)
- func CheckConstraint(c string, op Operation) error
- func ConstraintIdentity(c string) (string, error)
- func ConstraintUnion(chain ...Grant) ([]string, error)
- func ConstraintWithin(value, bound any) error
- func CoreEvaluatesEvidenceConstraint(c string) bool
- func Discharge(d Decision, understood []string) error
- func EpochIDOf(v uint64) *uint64
- func MaterialFieldDigest(def ActionTypeDefinition, action map[string]any) ([]string, error)
- func MaterialProjection(def ActionTypeDefinition, action map[string]any) (map[string]any, error)
- func NormalizeForDomains(params map[string]any, dm DomainMap) (map[string]any, string)
- func ObligationIdentities(d Decision) ([]string, error)
- func Obligations(d Decision) []string
- func PAE(payloadType string, payload []byte) []byte
- func RevisionCompatible(inputRevision string) bool
- func ValidateCapabilityID(id string) error
- func ValidateConstraint(c string) error
- func ValidateEvidenceConstraint(c string) error
- func ValidateGrantParams(params map[string]any) error
- func ValidateOperationParams(params map[string]any) error
- func ValidateParamBounds(bounds, params map[string]any) error
- func ValidateRawParams(raw string) error
- type ActionId
- type ActionIdSuite
- type ActionMappingProfile
- type ActionProjector
- type ActionTypeDefinition
- type Challenge
- type ChallengeItem
- type ChallengeParams
- type CombiningAlgorithm
- type ContainmentResult
- type Decision
- func Authorize(effectiveGrant Grant, op Operation) Decision
- func AuthorizeJSONText(grants []Grant, opID, rawParams string) Decision
- func AuthorizeSet(grants []Grant, op Operation) Decision
- func AuthorizeSetWithDomains(grants []Grant, op Operation, dm DomainMap) Decision
- func AuthorizeWithChain(chain []Grant, op Operation) Decision
- func Combine(alg CombiningAlgorithm, decisions ...Decision) (Decision, error)
- func Resolve(d Decision, resolutions []Resolution, now string) Decision
- type DecisionContext
- type DecisionRecord
- type Digest
- type DomainMap
- type DomainSpec
- type Envelope
- func (e Envelope) Check() error
- func (e Envelope) DecisionRecord() (DecisionRecord, error)
- func (e *Envelope) Sign(keyID string, sign func(pae []byte) ([]byte, error)) error
- func (e Envelope) Statement() (Statement, error)
- func (e Envelope) VerifyRecord(verify func(keyID string, pae, sig []byte) error) error
- type EvidenceContext
- type EvidenceEvaluation
- type EvidenceFact
- type EvidenceVerdict
- type Grant
- type MatchResult
- type MatchVerdict
- type ObtainHint
- type Operation
- type RecordConstraints
- type RecordInputs
- type RecordOptions
- type Requirement
- type RequirementConstraint
- type RequirementResult
- type Resolution
- type RetryTiming
- type Satisfaction
- type SatisfactionVerdict
- type Signature
- type SourceChain
- type SourceLink
- type SourceRef
- type Statement
- type Subject
Constants ¶
const ( // PayloadTypeInToto is the DSSE payload type for an in-toto Statement. PayloadTypeInToto = "application/vnd.in-toto+json" // StatementTypeInToto is the in-toto Statement v1 schema type. StatementTypeInToto = "https://in-toto.io/Statement/v1" // PredicateTypeCLCDecision is this profile's predicate type. PredicateTypeCLCDecision = "https://varwof.com/clc/v1/decision-record" )
const ( // SatisfactionReasonExpressionFalse means the requirement's expression was // not filled by eligible facts. SatisfactionReasonExpressionFalse = "requirement:expression_false" // SatisfactionReasonUnsatisfied is the fallback when no more specific // reason is available; it keeps the report deterministic. SatisfactionReasonUnsatisfied = "requirement:unsatisfied" )
Report reason codes that are not already carried by a constraint evaluation.
const ( // ResolutionSatisfied / ResolutionViolated / ResolutionUnknown are the // three §8.5 resolution statuses. ResolutionSatisfied = "satisfied" ResolutionViolated = "violated" ResolutionUnknown = "unknown" )
const ( VerdictAllow = "allow" VerdictDeny = "deny" VerdictAllowUR = "allow_unresolved" )
const CLCRevision = "CLC-1.19"
CLCRevision is the language revision this implementation declares and evaluates. Compatible reading (§12.1): same major, minor ≤ ours. (rev CLC-1.3 · 2026-09-12: CLC-1.3 is additive — `allow_unresolved` verdict + §9.3 identity/aggregation clarifications — so CLC-1.2/1.1 inputs still read fine.) (rev CLC-1.5 · 2026-09-14: the instance identity is the language's own projection form `clc-action:1:…` instead of a `caid:` claim, the v1 suite set is `jcs-sha256` only, §10 states that a top-level `unknown` yields UNSATISFIED, §11 separates `allow_unresolved` from evidence, and §12 keeps delegation containment out of the language. CLC-1.4 inputs still read.) (rev CLC-1.7 · 2026-09-15: the decoded parameter paths return the same stable denial for malformed Unicode as the raw path, and the decoded size check measures the JCS serialization. CLC-1.6 · 2026-09-14: `jcs-sha256` is a real RFC 8785 implementation. Go's json.Marshal HTML-escaped `&`/`<`/`>` and ordered keys by UTF-8 bytes, so identifiers and input digests over such material were not JCS and disagreed across implementations. CanonicalJSON now sorts object members by UTF-16 code units, escapes strings per §3.2.2.2, renders numbers per ECMAScript Number::toString, and refuses invalid UTF-8. The decoded params path refuses malformed Unicode (lone surrogates / invalid UTF-8) with invalid_params_number and counts the §6.2 size in JCS bytes, not a deserializer's re-encoding. The raw params path counts the same JCS-form octets (writeCanonicalString), so the raw and decoded limits agree — json.Marshal would over-count `&`/`<`/`>` and U+2028/U+2029. The bytes change for `&`/`<`/`>` material — a compatibility note for stored digests — while CLC-1.4/1.5 inputs still read.) (rev CLC-1.15 · 2026-09-25: corrective — the §6.6 cross-family numeric × enum meet is refused (invalid_params_binding) in either source order instead of reducing to the filtered enum; Contains returns the §13.3 ContainmentResult{Contains, Reason} shape. Inputs without param_bounds are unaffected; CLC-1.14 and earlier inputs still read.) (rev CLC-1.16 · 2026-09-28: corrective — the I-JSON integer bound (§6.2 step 3), leading-BOM refusal (step 8) and host numeric-carrier normalization (step 9). CLC-1.15 and earlier inputs still read.) (rev CLC-1.17 · 2026-10-13: corrective, no verdict change — §6.2 step 3 is restated so the I-JSON integer bound is judged on the mathematical value of the received token (an exactly-representable value such as 1e100 is refused), number identity is the JSON number not its spelling (-0 ≡ 0, 1e3 ≡ 1000), and a non-integer token is pinned to its IEEE-754 binary64 value. The behavior is what CLC-1.16 already implemented; CLC-1.16 and earlier inputs still read.)
const ChallengeVersion = "CLC-CHALLENGE-v1"
ChallengeVersion is the only @version this revision accepts.
const DefaultCombiningAlgorithm = CombiningDenyOverrides
DefaultCombiningAlgorithm is the algorithm a consumer uses unless it has a reason to pin another one.
const DigestAlgSHA256 = "sha-256"
DigestAlgSHA256 names the only digest algorithm this revision emits.
const RecordLang = "clc-v1"
RecordLang is the language identifier carried by every Decision Record.
const RequirementVersion = "CLC-REQUIREMENT-v1"
RequirementVersion is the only @version this revision accepts.
Variables ¶
var ( // ErrCanonicalInvalidUTF8 means a string (or object key) in the value is // not valid UTF-8. JCS has no representation for it and silently // substituting U+FFFD would change the digest, so the encoding fails. ErrCanonicalInvalidUTF8 = errors.New("canonical_invalid_utf8") // ErrCanonicalSurrogate means a string contains a lone (unpaired) UTF-16 // surrogate. That is not encodable as UTF-8 either, and must not be // guessed at. ErrCanonicalSurrogate = errors.New("canonical_lone_surrogate") // ErrCanonicalNumber means a number is not representable as a finite // IEEE-754 double (the only numeric type JCS admits), e.g. `1e400`. ErrCanonicalNumber = errors.New("canonical_invalid_number") )
var ( // ErrChallengeShape means the challenge is malformed (missing identity, // unusable action digest, bad retry timing, non-UTC instant). ErrChallengeShape = errors.New("challenge_shape") // ErrChallengeExpired means the challenge may no longer drive a retry. ErrChallengeExpired = errors.New("challenge_expired") // ErrChallengeNotOutstanding means the caller asked for a challenge for a // result that needs none — nothing is missing. ErrChallengeNotOutstanding = errors.New("challenge_not_outstanding") // ErrChallengeRetryNotBefore means the challenge's own retry schedule has // not opened yet; retrying now is exactly the amplification it guards // against. ErrChallengeRetryNotBefore = errors.New("challenge_retry_not_before") )
var ( // ErrNoDecisions means there was nothing to combine. A consumer holding no // decision has no authorization, so this is a refusal, not an allow. ErrNoDecisions = errors.New("no_decisions") // ErrUnknownCombiningAlgorithm means the caller named an algorithm this // revision does not define. ErrUnknownCombiningAlgorithm = errors.New("unknown_combining_algorithm") // ErrCombiningWouldDropObligations means the named algorithm would reach // allow while discarding a §8.4 obligation. Obligations are conjunctive, // so the combination fails closed instead. ErrCombiningWouldDropObligations = errors.New("combining_would_drop_obligations") )
var ( // ErrContextMissing means no freshness mechanism was pinned at all (or a // caller required a context that was not supplied). Absence is never an // implicit "fresh". ErrContextMissing = errors.New("context_missing") // ErrContextShape means the context's fields contradict each other (a // bound with nothing to bound, a negative bound, an unusable digest). ErrContextShape = errors.New("context_shape") // ErrContextNotUTC means At carried a non-UTC location; the same instant // would otherwise hash differently in different records. ErrContextNotUTC = errors.New("context_not_utc") // ErrContextFuture means At lies after the checking instant — a // future-dated or skewed appraisal, which is never accepted. ErrContextFuture = errors.New("context_future") // ErrContextStale means At is older than the pinned expiry threshold. ErrContextStale = errors.New("context_stale") // ErrContextNoClock means freshness was asked of a context that pinned only // implicit mechanisms: the nonce/epoch comparison belongs to the native // verifier (RATS §10.2/§10.3), so a clock-based check cannot speak for it. ErrContextNoClock = errors.New("context_no_clock") )
var ( // ErrEnvelopeShape means the envelope or its statement is not the shape this // profile defines (missing members, wrong statement type, unparseable body). ErrEnvelopeShape = errors.New("envelope_shape") // ErrEnvelopePayloadType means the DSSE payload type is not the in-toto one. ErrEnvelopePayloadType = errors.New("envelope_payload_type") // ErrEnvelopeSubjectMismatch means the statement's subject digests do not // match the decision the predicate describes: the envelope has been // re-targeted and must not be consumed. ErrEnvelopeSubjectMismatch = errors.New("envelope_subject_mismatch") // ErrEnvelopeSignature means no signature verified (or verification failed). ErrEnvelopeSignature = errors.New("envelope_signature") )
var ( // ErrEvidenceConstraint means the constraint is not a recognized evidence // type or its value is outside the grammar (fail-closed). ErrEvidenceConstraint = errors.New("invalid_evidence_constraint") // ErrEvidenceNotCoreEvaluated means the type is recognized but its // evaluation belongs to an external authority. ErrEvidenceNotCoreEvaluated = errors.New("evidence_not_core_evaluated") )
var ( // ErrActionShape means the action, its type definition, or an identifier // string is malformed. ErrActionShape = errors.New("action_shape") // ErrActionSuite means an unknown canonicalization+digest suite was named. ErrActionSuite = errors.New("action_suite") // ErrActionNotMatchable means a declared material field is absent: the action // cannot be identified, so it cannot match anything (fail-closed, never // "assume it was the same"). ErrActionNotMatchable = errors.New("action_not_matchable") // ErrActionProjectionLossy means a mapping profile dropped or altered // material content; a lossy projection must never establish equivalence. ErrActionProjectionLossy = errors.New("action_projection_lossy") )
var ( // ErrObligationUnknown means the decision carries an obligation whose // (scheme,type) identity the consumer does not understand, or whose text // cannot be parsed at all. XACML §2.13 requires denying rather than // silently ignoring such an obligation. ErrObligationUnknown = errors.New("obligation_unknown") // ErrObligationShape means the verdict and the obligation list disagree // with §8.4: an allow that carries obligations, an allow_unresolved that // carries none, or an unknown verdict value. ErrObligationShape = errors.New("obligation_shape") )
var ( // ErrRecordOperationCount means the record does not carry exactly one // operation; this revision's verdict is singular, so the record must be. ErrRecordOperationCount = errors.New("record_operation_count") // ErrRecordDigestMismatch means Inputs no longer hash to InputDigest. ErrRecordDigestMismatch = errors.New("record_input_digest_mismatch") // ErrRecordVerdictMismatch means re-running the language over Inputs did // not reproduce the recorded Verdict / Reason / Unresolved. ErrRecordVerdictMismatch = errors.New("record_verdict_mismatch") // ErrRecordRevision means the record claims a language revision this // build cannot re-run, so the record cannot be checked (fail closed). ErrRecordRevision = errors.New("record_unsupported_revision") )
var ( // ErrRequirementShape means the object is malformed, carries unknown // members, or its expression exceeds the bounded parser's limits. ErrRequirementShape = errors.New("requirement_shape") // ErrRequirementVersion means @version is not CLC-REQUIREMENT-v1. ErrRequirementVersion = errors.New("requirement_version") // ErrRequirementExpression means the expression does not parse under the // bounded grammar. ErrRequirementExpression = errors.New("requirement_expression") )
var ( // ErrInvalidResolution / ErrInvalidTimestamp are the §8.5 input-error // reason codes. ErrInvalidResolution = errors.New("invalid_resolution") ErrInvalidTimestamp = errors.New("invalid_timestamp") )
var ( ErrUnsupportedWildcard = errors.New("unsupported_wildcard") ErrInvalidCapabilityID = errors.New("invalid_capability_id") ErrMissingCapabilityID = errors.New("missing_capability_id") ErrInvalidConstraint = errors.New("invalid_constraint") ErrInvalidParamsNull = errors.New("invalid_params_null") ErrParamsMissing = errors.New("params_missing") ErrParamsUndeclared = errors.New("undeclared_param") ErrCapabilityNotAuth = errors.New("capability_not_authorized") ErrUnknownConstraint = errors.New("unknown_constraint") ErrDifferentNamespace = errors.New("different_namespace") ErrLiteralMismatch = errors.New("literal_mismatch") ErrWildcardNoTrailing = errors.New("wildcard_requires_trailing_segment") ErrParamsExceedGrant = errors.New("params_exceed_grant") ErrEmptyBoundDenies = errors.New("empty_bound_denies_class") ErrNotInEnum = errors.New("not_in_enum") ErrNoOverlap = errors.New("no_overlap") ErrAbsentSource = errors.New("absent_source") ErrInvalidParamsDupKey = errors.New("invalid_params_duplicate_key") ErrInvalidParamsNumber = errors.New("invalid_params_number") ErrInvalidParamsSize = errors.New("invalid_params_size") ErrInvalidParamsBinding = errors.New("invalid_params_binding") ErrParamsCardinality = errors.New("params_cardinality") ErrParamsOutOfRange = errors.New("params_out_of_range") ErrParamsNotMultiple = errors.New("params_not_multiple") ErrUnsupportedLangRev = errors.New("unsupported_language_revision") ErrChildExceedsParent = errors.New("child_exceeds_parent") ErrParamsNotNarrower = errors.New("params_not_narrower") )
var ( // ErrSourceChainEmpty means a chain was supplied with no sources. ErrSourceChainEmpty = errors.New("source_chain_empty") // ErrSourceShape means a source is missing its kind/id/digest, or its // digest is not a usable algorithm-tagged value. ErrSourceShape = errors.New("source_shape") // ErrSourceDuplicate means two sources carry the same digest, which makes // the chain ambiguous. ErrSourceDuplicate = errors.New("source_duplicate") // ErrSourceDanglingLink means a link names a digest no source in the chain // carries: the referenced material was not disclosed. ErrSourceDanglingLink = errors.New("source_dangling_link") // ErrSourceEdgeUnbacked means a link claims a relation the source's own // bytes do not contain. Absence is "not enough"; this is deception. ErrSourceEdgeUnbacked = errors.New("source_edge_unbacked") // ErrSourceChainCycle means the links do not form a simple path. ErrSourceChainCycle = errors.New("source_chain_cycle") // ErrSourceChainBranch means a walk was asked for on a source that names // more than one other source, so there is no single path to return. ErrSourceChainBranch = errors.New("source_chain_branch") )
var ValidateIDRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9-]*/[a-zA-Z][a-zA-Z0-9-]*-v[0-9]+:[a-zA-Z][a-zA-Z0-9_:.-]*$`)
ValidateIDRegex is used for syntax validation in vectors.
Functions ¶
func CanonicalJSON ¶
CanonicalJSON returns RFC 8785 (JCS) canonical JSON.
func CheckConstraint ¶
CheckConstraint evaluates a constraint against an operation (§8.1). Rev CLC-1.2: max_rows uses a strict integer and fails closed when the op carries no max_rows value (previously the sloppy %f scan silently skipped both malformed values and missing op params).
func ConstraintIdentity ¶ added in v0.3.0
ConstraintIdentity returns the (scheme,type) identity of a constraint — the granularity §8.1 recognizes constraints at ("the identity of a constraint is two things — the declaring scheme and the type"). A constraint that does not carry a parseable identity cannot be dispatched to any evaluator, so it is reported as an unknown obligation rather than passed along as text.
func ConstraintUnion ¶ added in v0.7.0
ConstraintUnion returns the normalized union of every constraint string carried by the grants in chain — duplicates folded, deterministically ordered (lexically sorted) — the same normalization Intersect applies. An empty chain returns (nil, ErrAbsentSource).
func ConstraintWithin ¶ added in v0.6.0
ConstraintWithin reports whether value is within a declared bound using the same JSON subset semantic as §5.2 valueSubset: numbers compare as upper bounds, arrays are membership sets, objects recurse per key. It is used by publication-boundary checks (a rule's constraint value must not exceed the signer's constraint value). If bound is absent, the constraint is nominal and any value is accepted (matching absent-params = unconstrained). An explicitly empty bound ([]/{}) denies the class, exactly like §5.2.
func CoreEvaluatesEvidenceConstraint ¶ added in v0.3.0
CoreEvaluatesEvidenceConstraint reports whether this revision has an evaluator for the constraint, mirroring coreEvaluatesConstraint on the authorization side. Unrecognized constraints report false.
func Discharge ¶ added in v0.3.0
Discharge reports whether a consumer may act on the decision.
understood lists the obligation identities the consumer understands and can and will discharge, e.g. []string{"varwof/constraint-v1:time"}. A decision with no obligations always discharges. Every obligation on an allow_unresolved decision must be covered by understood; otherwise the consumer must deny (XACML §2.13) — an obligation it does not understand, cannot honour, or has not committed to is never silently dropped, and allow_unresolved is never read as allow.
Discharge answers only "may I act at all"; confirming the obligation's actual value (this time window, this peer address) is the caller's separate, value-level step.
func EpochIDOf ¶ added in v0.3.0
EpochIDOf returns a pointer suitable for DecisionContext.EpochID, so a legitimate epoch id of zero is not read as "absent".
func MaterialFieldDigest ¶ added in v0.3.0
func MaterialFieldDigest(def ActionTypeDefinition, action map[string]any) ([]string, error)
MaterialFieldDigest returns the per-field digests of the material projection in a deterministic order — what an Action-Mapping Profile may project without losing identity.
func MaterialProjection ¶ added in v0.3.0
MaterialProjection returns the map that ComputeActionID hashes, so a caller can disclose exactly what entered the identity (and nothing else did).
func NormalizeForDomains ¶ added in v0.10.0
NormalizeForDomains rewrites every declared quantity value into its normalized number. It returns a canonical reason code when a value cannot be normalized or the declared domain is not defined by the revision.
func ObligationIdentities ¶ added in v0.3.0
ObligationIdentities returns the sorted, deduplicated (scheme,type) identities of the decision's obligations. This is the granularity a consumer declares support at ("I understand and can discharge varwof/constraint-v1:time"), and the granularity §8.4's per-scheme evaluation is scoped to.
func Obligations ¶ added in v0.3.0
Obligations returns an independent copy of the decision's §8.4 obligation list (empty for deny and for a fully evaluated allow).
func PAE ¶ added in v0.3.0
PAE returns the DSSE v1 pre-authentication encoding:
PAE(type, body) = "DSSEv1" SP LEN(type) SP type SP LEN(body) SP body
It is exported because every consumer must compute it identically; a signer and a verifier that disagree here silently fail to interoperate.
func RevisionCompatible ¶
RevisionCompatible reports whether an input declaring the given CLC revision may be evaluated by this implementation (§12.1). A mismatch is resolved before any §9.3 layer and fails closed with unsupported_language_revision — never a silent downgrade.
func ValidateCapabilityID ¶
ValidateCapabilityID checks whether an ID conforms to CLC-v1 §2 grammar. v1 allows: literal segments and trailing * as a complete segment.
func ValidateConstraint ¶
ValidateConstraint checks a constraint against §8.1's identity × value grammar (rev CLC-1.2/1.3): an unrecognized (scheme,type) → unknown_constraint; a recognized type whose value is out of grammar → invalid_constraint.
func ValidateEvidenceConstraint ¶ added in v0.3.0
ValidateEvidenceConstraint checks the closed value grammar of the four evidence-side types. An unrecognized (scheme,type) is an error, never a silent pass — the evidence-side mirror of §8.1's unknown_constraint.
func ValidateGrantParams ¶
ValidateGrantParams checks for null values in params (CLC-v1 §5.2) and applies the §6.2 step 4 caps to the decoded-object path (§6.2 step 6).
func ValidateOperationParams ¶
ValidateOperationParams checks for null values in operation params and applies the §6.2 step 4 caps to the decoded-object path (§6.2 step 6).
func ValidateParamBounds ¶ added in v0.7.0
ValidateParamBounds checks the §6.5 bound grammar and the one-representation binding rule: a key MUST NOT be declared in both params and param_bounds, every Bound is a closed object with at most one value family, and its members are well-typed.
func ValidateRawParams ¶
ValidateRawParams validates the raw JSON text of an operation's params object at the input boundary (§6.2, §9.3 layer 2), before any layer runs. Unlike a decoded map, the raw text preserves duplicate keys, number literals, nesting depth and serialized size — none of which survive a map-based decode (maps drop duplicate keys, and JSON cannot carry non-finite numbers). Rejections follow §6.2 order: size/depth, then duplicate keys, then number shape. Malformed input and a non-object params value fall into invalid_params_number.
Types ¶
type ActionId ¶ added in v0.3.0
type ActionId struct {
Type string
Suite ActionIdSuite
Digest Digest
}
ActionId is a typed, suite-tagged content identity for the material content of one action. It is not an occurrence identifier: an occurrence needs a discriminator supplied by the effect boundary (§6.4).
func ComputeActionID ¶ added in v0.3.0
func ComputeActionID(def ActionTypeDefinition, suite ActionIdSuite, action map[string]any) (ActionId, error)
ComputeActionID projects the action onto the type's material field set and returns its typed identity. A declared field that is absent makes the action non-matchable.
func ParseActionId ¶ added in v0.3.0
ParseActionId parses the string form.
type ActionIdSuite ¶ added in v0.3.0
type ActionIdSuite string
ActionIdSuite names a canonicalization+digest suite. The set is closed.
const ( // SuiteJCSSHA256 is JCS canonicalization over SHA-256; it is the only suite // defined in v1. A name outside the set is a refusal, never a computation. SuiteJCSSHA256 ActionIdSuite = "jcs-sha256" )
type ActionMappingProfile ¶ added in v0.3.0
ActionMappingProfile names a projection by its content hash and is pinned by the relying party (§6.4). The Canonical Action Identifier draft describes the constructs the bridge to and from a CAID, which covers the complete Action Object rather than this projection; the draft is cited here, not reproduced. A profile without a usable digest cannot be pinned, and an unpinned profile establishes nothing.
func (ActionMappingProfile) Pinned ¶ added in v0.3.0
func (p ActionMappingProfile) Pinned() bool
Pinned reports whether the profile is usable as a relying-party pin.
type ActionProjector ¶ added in v0.3.0
type ActionProjector func(profile ActionMappingProfile, in ActionId) (ActionId, error)
ActionProjector projects one natively verified action identity into the expected action type under a profile. It belongs to the native verifier: this package does not parse foreign formats.
type ActionTypeDefinition ¶ added in v0.3.0
type ActionTypeDefinition struct {
Type string `json:"type"`
// MaterialFields are the fields that make up the action's identity.
// Required and optional-but-included fields are both listed here: the
// projection includes exactly this set.
MaterialFields []string `json:"material_fields"`
}
ActionTypeDefinition is the declared material field set of one action type. The relying party pins the definition source (§6.4 step 3).
func (ActionTypeDefinition) Validate ¶ added in v0.3.0
func (d ActionTypeDefinition) Validate() error
Validate checks the definition itself.
type Challenge ¶ added in v0.3.0
type Challenge struct {
Version string `json:"@version"`
ID string `json:"challenge_id"`
Nonce string `json:"nonce"`
Audience string `json:"audience,omitempty"`
ActionDigest Digest `json:"action_digest"`
RequirementID string `json:"requirement_id,omitempty"`
RequirementDigest *Digest `json:"requirement_digest,omitempty"`
Required []ChallengeItem `json:"required"`
ObtainHints []ObtainHint `json:"obtain_hints,omitempty"`
Retry *RetryTiming `json:"retry_timing,omitempty"`
ExpiresAt time.Time `json:"expires_at"`
}
Challenge is a machine-readable statement of what evidence or confirmation is still required. It is not a verdict.
func BuildChallengeFromDecision ¶ added in v0.3.0
func BuildChallengeFromDecision(d Decision, params ChallengeParams) (Challenge, error)
BuildChallengeFromDecision builds the challenge for an authorization decision carrying §8.4 residual obligations: the consumer could not discharge them, so it says which ones it owes an answer for.
func BuildChallengeFromRequirement ¶ added in v0.3.0
func BuildChallengeFromRequirement(req Requirement, result RequirementResult, params ChallengeParams) (Challenge, error)
BuildChallengeFromRequirement builds the challenge for an evidence requirement that was not satisfied. A satisfied result needs no challenge, and a hard denial with nothing missing is reported as such rather than dressed up as a retryable request.
func (Challenge) Authorizes ¶ added in v0.3.0
Authorizes reports whether a challenge authorizes anything. It never does; the method exists so that call sites cannot read one as a verdict by accident.
func (Challenge) Digest ¶ added in v0.3.0
Digest binds the challenge so a caller can identify exactly which challenge it answered.
type ChallengeItem ¶ added in v0.3.0
type ChallengeItem struct {
ID string `json:"id"`
Constraint string `json:"constraint,omitempty"`
Reason string `json:"reason,omitempty"`
}
ChallengeItem names one outstanding thing: an evidence role, or an obligation identity such as "varwof/constraint-v1:time".
type ChallengeParams ¶ added in v0.3.0
type ChallengeParams struct {
ID string
Nonce string
Audience string
ActionDigest Digest
Now time.Time
TTL time.Duration
ObtainHints []ObtainHint
Retry *RetryTiming
}
ChallengeParams are the caller-supplied inputs a challenge needs. ID and Nonce are the caller's: the core does not generate randomness.
type CombiningAlgorithm ¶ added in v0.3.0
type CombiningAlgorithm string
CombiningAlgorithm names how independently decided sources are combined. The set is closed: an unknown name is rejected, never defaulted.
const ( // CombiningDenyOverrides is the default: if any source denies, the // operation is denied; otherwise any source carrying residual obligations // yields allow_unresolved with the union of those obligations; otherwise // allow. It is the fail-closed choice and the one §7 already implements at // the grant-set level. CombiningDenyOverrides CombiningAlgorithm = "deny-overrides" // CombiningPermitOverrides: if any source allows outright, the operation // is allowed; else if any source denies, it is denied; else the residual // obligations union into allow_unresolved. Because "allow" ignores the // sources that did not allow, it is refused whenever any input carries // obligations (that would be a fail-open drop). CombiningPermitOverrides CombiningAlgorithm = "permit-overrides" // CombiningFirstApplicable is ordered: the first decision wins, later ones // are not consulted. The caller's order is the policy order, so this // algorithm is only appropriate where that order is itself a policy. CombiningFirstApplicable CombiningAlgorithm = "first-applicable" )
func (CombiningAlgorithm) Valid ¶ added in v0.3.0
func (a CombiningAlgorithm) Valid() bool
Valid reports whether the algorithm is one of the defined names.
type ContainmentResult ¶ added in v0.8.0
type ContainmentResult struct {
Contains bool `json:"contains"`
Reason string `json:"reason,omitempty"`
}
ContainmentResult is the §13.3 return shape of Contains — the JSON object {contains, reason}. Rev CLC-1.15: the public surface no longer reuses the entailment's MatchResult{Entails}, so callers (and the corpus runners) read the §13.3 field names directly and no translation layer is needed. Reason is empty on success and carries the first failing layer's code on failure.
func Contains ¶ added in v0.7.0
func Contains(parent, child Grant) ContainmentResult
Contains reports whether a child grant stays inside a parent grant's declared authorization boundary, per draft-wei-clc-ext-00 §4 (CLD-D).
The relation is compared on DECLARED sets and DECLARED bounds, not on behavior: an operation that fits the parent proves nothing about a child's runtime behavior (CLC-v1 §12; the extension keeps that scope). If any layer of §4 fails, Contains is false with the first failing layer's reason code (§4 layer order). The result is the §13.3 ContainmentResult shape {contains, reason} (rev CLC-1.15) — the relation signature's own field names, no MatchResult reuse and no runner-side translation.
Layer semantics match the extension draft:
- layer 1: both grants valid (identifier + params grammar);
- layer 2: child id covered by parent id via CLC-v1 path coverage (different_namespace kept; every other coverage failure collapses into child_exceeds_parent, draft §4.2/§5);
- layer 3: child params within parent's declared bounds (number upper bounds, enums as membership) and child key set closed by parent.
Constraints are deliberately NOT part of this relation. Constraints are a separate axis that composes by UNION (conjunction) across a delegation chain (see Intersect, §7), not by subset: a child's constraint set is never compared to its parent's here. Delegation mode is likewise a carrier concept (AIC-JWT DA binds it); the language relation takes no mode.
func ContainsWithDomains ¶ added in v0.10.0
func ContainsWithDomains(parent, child Grant, parentDom, childDom DomainMap) ContainmentResult
ContainsWithDomains applies the parent's and child's declarations and then the ordinary containment relation. A value that cannot be normalized under its declared domain is not comparable, so containment fails closed with `params_not_narrower`.
type Decision ¶
type Decision struct {
Verdict string `json:"verdict"` // "allow", "deny" or "allow_unresolved" (rev CLC-1.3)
Reason string `json:"reason,omitempty"`
// Unresolved lists recognized-but-unevaluated constraints carried on an
// allow_unresolved verdict (§8.4 residual-obligation channel): non-empty
// means the consumer must evaluate/confirm each constraint before acting,
// else deny (AAC §6.6). Empty on deny and on a fully-evaluated allow.
// Ordered deterministically (sorted, deduped).
Unresolved []string `json:"unresolved,omitempty"`
}
Decision is the output of the authorization function.
func Authorize ¶
Authorize evaluates the decision function per CLC-v1 §8 with a single effective grant (§9.3 single-grant path).
func AuthorizeJSONText ¶ added in v0.6.0
AuthorizeJSONText is the normative entry point for a caller that holds the operation's params as JSON text: it runs the §6.2 input-boundary checks on that text and evaluates the operation in one call. rawParams may be empty for an operation that carries no params.
Prefer this over decoding the text and calling AuthorizeSet. A decode is lossy for exactly the inputs §6.2 refuses — a general-purpose JSON decoder replaces a lone surrogate escape and an invalid UTF-8 octet with U+FFFD, and the value that then reaches the decision function is indistinguishable from a legitimate U+FFFD, so the refusal cannot be recovered (§6.2 item 7). A refusal is reported as deny with the §6.2 reason code.
func AuthorizeSet ¶
AuthorizeSet evaluates the §9.3 multi-grant aggregation (rev CLC-1.3).
The caller must have run the §6.2 input-boundary checks on the text it received, if it received text. A params value handed straight to this function cannot carry the information those checks use: a decoder replaces a lone surrogate escape and an invalid UTF-8 octet with U+FFFD, so the value seen here is indistinguishable from a legitimate U+FFFD (§6.2 item 7). A caller holding the raw text should call AuthorizeJSONText instead.
The rest of the contract:
- grants all absent/empty → deny capability_not_authorized (resolved before any layer check, per §9.3 pre-check);
- otherwise operation layer-1 validation runs first, exactly as the single-grant path;
- each grant with a matching operation ID is a covering grant; its params are checked (paramsSubset) then its constraints (ValidateConstraint → CheckConstraint);
- ANY covering grant whose params+constraints fully allow authorizes the operation (union semantics);
- residual obligations (recognized-but-unevaluated constraints) are unioned across the covering-and-allowing grants → allow_unresolved;
- if no covering grant allows: params/constraint-level denials from covering grants surface as the first covering grant's reason in canonical (input) order; grants that did not cover collapse to capability_not_authorized.
func AuthorizeSetWithDomains ¶ added in v0.10.0
AuthorizeSetWithDomains applies one declaration to every grant and to the operation, then runs the ordinary multi-grant aggregation.
func AuthorizeWithChain ¶ added in v0.7.0
AuthorizeWithChain checks each adjacent hop with Contains and, if the whole chain is contained, authorizes op against Intersect(chain...).
An empty chain denies absent_source. The first hop whose containment fails ends the call with that hop's §13.5 reason code — before op validation. An Intersect refusal (no_overlap, empty_bound_denies_class, invalid_params_binding) is returned as deny(reason). Otherwise the operation-layer Decision (allow / allow_unresolved / deny with §9 reasons) passes through unchanged.
func Combine ¶ added in v0.3.0
func Combine(alg CombiningAlgorithm, decisions ...Decision) (Decision, error)
Combine applies a named combining algorithm to independently decided sources.
Every decision must satisfy the §8.4 shape (checkDecisionShape); a malformed one fails closed. deny-overrides reports the first denying source's reason in argument order, so the result is deterministic for a given input order.
func Resolve ¶ added in v0.7.0
func Resolve(d Decision, resolutions []Resolution, now string) Decision
Resolve collapses a decision's §8.4 obligations with a consumer's reports and an optional clock (§8.5). now is an RFC3339 UTC instant, "" for absent.
It is deterministic, fail-closed, idempotent and monotone; it neither invents nor drops obligations. Terminal deny/allow decisions pass through unchanged.
type DecisionContext ¶ added in v0.3.0
type DecisionContext struct {
// At is the explicit-timekeeping instant (RATS §10.1). It MUST be UTC: a
// local-offset instant encodes differently and would change a record's
// input digest for the same moment in time.
At time.Time `json:"at,omitempty"`
// MaxAgeSec bounds how old At may be when the context is checked
// (RFC 9334 §10: the expiry threshold of the appraisal policy). Zero means
// no clock-based bound, which is only sound when another mechanism
// (nonce/epoch) establishes freshness.
MaxAgeSec int64 `json:"max_age_sec,omitempty"`
// Nonce is the appraising entity's unpredictable value that the evidence
// must echo (RATS §10.2).
Nonce string `json:"nonce,omitempty"`
// EpochID is the latest epoch identifier the appraising entity received
// from its distributor (RATS §10.3). A pointer so that a legitimate id of
// zero is distinguishable from "not used".
EpochID *uint64 `json:"epoch_id,omitempty"`
// SnapshotDigest identifies the trust/status snapshot the native verifier
// used (trust anchors, revocation results, epoch distributor state). A
// replay that does not bind this cannot show which inputs it re-appraised.
SnapshotDigest *Digest `json:"snapshot_digest,omitempty"`
}
DecisionContext is the freshness input a relying party pins for one decision. At least one mechanism must be present.
func (DecisionContext) Digest ¶ added in v0.3.0
func (c DecisionContext) Digest() (Digest, error)
Digest binds the context so a record can identify exactly which freshness input it was decided under.
func (DecisionContext) Fresh ¶ added in v0.3.0
func (c DecisionContext) Fresh(now time.Time) error
Fresh reports whether the context's clock mechanism still holds at now.
now is supplied by the caller (the core never reads a clock). A context that pinned only a nonce or an epoch id has no clock claim to check: this returns ErrContextNoClock rather than passing, because a clock-based function cannot speak for an implicit mechanism — the native verifier must have compared the nonce/epoch instead.
func (DecisionContext) IsZero ¶ added in v0.3.0
func (c DecisionContext) IsZero() bool
IsZero reports whether the context pins no freshness mechanism at all.
func (DecisionContext) MaxAge ¶ added in v0.3.0
func (c DecisionContext) MaxAge() time.Duration
MaxAge returns the clock-based expiry threshold as a duration (0 = none).
func (DecisionContext) Validate ¶ added in v0.3.0
func (c DecisionContext) Validate() error
Validate checks that the context is usable: at least one mechanism, no contradiction between fields, a UTC instant when one is given, and a well-formed snapshot digest.
type DecisionRecord ¶ added in v0.3.0
type DecisionRecord struct {
Ver string `json:"ver"` // CLC revision, e.g. CLCRevision
Lang string `json:"lang"` // language identifier, RecordLang
Inputs RecordInputs `json:"inputs"`
InputDigest Digest `json:"input_digest"`
Constraints RecordConstraints `json:"constraints"`
Verdict string `json:"verdict"`
Reason string `json:"reason,omitempty"`
}
DecisionRecord is a self-contained, independently re-computable decision.
Re-computation rules (the properties that make this evidence rather than a log line):
- any holder of Inputs can re-run the same Ver and must obtain the same Verdict / Reason / Constraints.Unresolved;
- equal Inputs produce an equal InputDigest, so two implementations can agree on "same decision input" without exchanging bytes;
- a non-empty Constraints.Unresolved must accompany the allow_unresolved verdict and never allow — Verify enforces this by re-deriving the verdict rather than by trusting the field.
func Record ¶ added in v0.3.0
func Record(grants []Grant, op Operation) (DecisionRecord, error)
Record runs a decision and freezes the inputs and the outcome together.
The semantics are exactly AuthorizeSet's: Verdict, Reason and Unresolved are copied from its result unchanged, never recomputed or reworded here. Inputs are round-tripped through their canonical encoding so the record owns an independent, immutable copy of what was decided.
func RecordWith ¶ added in v0.3.0
func RecordWith(grants []Grant, op Operation, opts RecordOptions) (DecisionRecord, error)
RecordWith records a decision with an optional freshness context and authorization source chain. Both are validated before anything is written: a chain whose claimed relations its own bytes do not back is refused, not recorded.
func RecordWithContext ¶ added in v0.3.0
func RecordWithContext(grants []Grant, op Operation, ctx DecisionContext) (DecisionRecord, error)
RecordWithContext records a decision together with the freshness context it was made under. The context becomes part of the hashed inputs, so re-appraising the record means re-appraising the same instant, nonce or epoch — not "some time later, same verdict".
func (DecisionRecord) CanonicalBytes ¶ added in v0.3.0
func (r DecisionRecord) CanonicalBytes() ([]byte, error)
CanonicalBytes returns the record's own canonical (JCS) encoding — the stable handle a later step signs, or compares between implementations.
func (DecisionRecord) Recompute ¶ added in v0.3.0
func (r DecisionRecord) Recompute() (Decision, error)
Recompute re-runs the language over the record's frozen inputs and returns the decision they produce. It needs nothing outside the record.
func (DecisionRecord) Verify ¶ added in v0.3.0
func (r DecisionRecord) Verify() error
func (DecisionRecord) VerifyAsOf ¶ added in v0.3.0
func (r DecisionRecord) VerifyAsOf(now time.Time) error
Verify checks a record a holder did not produce: the digest must match the frozen inputs, and re-running the language must reproduce the verdict, reason and residual obligations byte for byte. Any failure is a hard error — a record that cannot be reproduced is not evidence. VerifyAsOf verifies the record and additionally requires its decision context to still be fresh at now (RATS §10). A record that verifies but is stale is not evidence of a current decision: a historical re-performance must be labeled historical and must not be reused as a current execution decision (AEB §5.7).
type Digest ¶ added in v0.3.0
Digest is a hash over a canonical byte string. Its JSON form is {"alg": "...", "value": "<base64>"} — a CDDL bstr carried through JSON.
func DigestOf ¶ added in v0.3.0
DigestOf returns the SHA-256 of v's canonical (JCS, RFC 8785) JSON encoding.
The digest is the language's own handle, not a CAID: it is reproducible across any implementation that follows RFC 8785, so two holders can agree on "same decision input" by comparing digests.
func DigestOfCanonical ¶ added in v0.3.0
DigestOfCanonical hashes bytes already known to be canonical, so Record does not encode the inputs twice.
type DomainMap ¶ added in v0.10.0
type DomainMap map[string]DomainSpec
DomainMap maps a parameter name to its declaration.
type DomainSpec ¶ added in v0.10.0
DomainSpec is one parameter's declared domain.
type Envelope ¶ added in v0.3.0
type Envelope struct {
Payload []byte `json:"payload"`
PayloadType string `json:"payloadType"`
Signatures []Signature `json:"signatures"`
}
Envelope is a DSSE v1.0.2 JSON envelope. All three members MUST be present even when empty, so Signatures is never nil once NewEnvelope has run.
func NewEnvelope ¶ added in v0.3.0
func NewEnvelope(rec DecisionRecord) (Envelope, error)
NewEnvelope wraps a decision record in an unsigned DSSE envelope.
func (Envelope) Check ¶ added in v0.3.0
Check verifies the envelope's internal consistency without any signature: payload type, statement shape, subject binding to the decision, and the record's own digest/recomputation.
func (Envelope) DecisionRecord ¶ added in v0.3.0
func (e Envelope) DecisionRecord() (DecisionRecord, error)
DecisionRecord returns the decision record carried as the predicate.
func (*Envelope) Sign ¶ added in v0.3.0
Sign appends a signature over PAE(payloadType, payload). The signing primitive is supplied by the caller: DSSE deliberately leaves the signature format to the out-of-band agreement, so this package does not pick one.
func (Envelope) Statement ¶ added in v0.3.0
Statement decodes the envelope's payload as an in-toto statement.
func (Envelope) VerifyRecord ¶ added in v0.3.0
VerifyRecord checks the envelope and then requires at least one signature to verify. verify is the caller's signature primitive; it receives the KEYID hint (which MUST NOT be trusted on its own) and the PAE bytes.
type EvidenceContext ¶ added in v0.3.0
EvidenceContext carries the inputs the core does not read for itself: the appraisal instant and the boundary-owned identities that exclusion compares against (AEB §5.6 keeps those at the effect boundary).
type EvidenceEvaluation ¶ added in v0.3.0
type EvidenceEvaluation struct {
Constraint string `json:"constraint"`
Verdict EvidenceVerdict `json:"verdict"`
Reason string `json:"reason,omitempty"`
}
EvidenceEvaluation is the result of evaluating one evidence constraint.
func EvaluateEvidenceConstraint ¶ added in v0.3.0
func EvaluateEvidenceConstraint(c string, facts []EvidenceFact, ctx EvidenceContext) (EvidenceEvaluation, error)
EvaluateEvidenceConstraint evaluates one evidence constraint over the eligible facts of a single evidence role. A malformed constraint is an error; a recognized-but-external one returns EvidenceUnknown with ErrEvidenceNotCoreEvaluated as its reason.
type EvidenceFact ¶ added in v0.3.0
type EvidenceFact struct {
// Type is the evidence role this fact fills (e.g. "human-authorization").
Type string
// Subject is the integrity-protected subject identifier. Empty means the
// fact cannot be counted for quorum or exclusion.
Subject string
// IssuedAt is the protected issuance instant (UTC). Zero means absent,
// which fails a freshness bound rather than passing it.
IssuedAt time.Time
// Verified reports whether the native verifier accepted the artifact.
Verified bool
}
EvidenceFact is one natively verified artifact reduced to the fields the core may evaluate over. Everything here must come from the artifact's own protected bytes or from the enforcement point's durable state; presenter labels are not represented at all, so they cannot be counted by accident.
func EvidenceFactsOfRole ¶ added in v0.3.0
func EvidenceFactsOfRole(facts []EvidenceFact, role string) []EvidenceFact
EvidenceFactsOfRole selects the facts filling one evidence role. A constraint is evaluated over exactly one role's facts (the evidence-side analogue of an operation's params being the subject matter of an authorization constraint), so callers scope first and evaluate second.
type EvidenceVerdict ¶ added in v0.3.0
type EvidenceVerdict string
EvidenceVerdict is the three-valued result of evaluating one evidence constraint. Unknown is not satisfied (AEC §9.8: an unknown identifier evaluates to false).
const ( // EvidenceSatisfied means the constraint holds over the eligible facts. EvidenceSatisfied EvidenceVerdict = "satisfied" // EvidenceViolated means it does not hold. EvidenceViolated EvidenceVerdict = "violated" // EvidenceUnknown means the core cannot decide it — an external authority // (the enforcement point, a status source) must, and a consumer that does // not obtain that answer must refuse. EvidenceUnknown EvidenceVerdict = "unknown" )
type Grant ¶
type Grant struct {
ID string `json:"id"`
Params map[string]any `json:"params,omitempty"`
ParamBounds map[string]any `json:"param_bounds,omitempty"`
Constraints []string `json:"constraints,omitempty"`
}
Grant represents a principal's authorization of a capability.
type MatchResult ¶
MatchResult captures the result of an entailment check.
func Entails ¶
func Entails(grant Grant, op Operation) MatchResult
Entails checks if a grant covers an operation per CLC-v1 §5.
func EntailsWithDomains ¶ added in v0.10.0
func EntailsWithDomains(g Grant, op Operation, dm DomainMap) MatchResult
EntailsWithDomains applies the declaration and then the ordinary entailment.
type MatchVerdict ¶ added in v0.3.0
type MatchVerdict string
MatchVerdict is §6.4's decision about two action identities.
const ( // MatchExact means the evidence is about exactly this action. MatchExact MatchVerdict = "MATCH" // MatchNotEquivalent means both identities are comparable and differ. MatchNotEquivalent MatchVerdict = "NOT_EQUIVALENT" // MatchIndeterminate means the comparison cannot be made here (different // suite or action type): it needs a pinned mapping profile, and an // indeterminate comparison is never a match. MatchIndeterminate MatchVerdict = "INDETERMINATE" )
func MapAndMatch ¶ added in v0.3.0
func MapAndMatch(profile ActionMappingProfile, projector ActionProjector, evidence, expected ActionId) MatchVerdict
MapAndMatch applies a pinned profile and compares the projection with the expected identity. Every failure mode — unpinned profile, projector error, or a projection that changes material content — is INDETERMINATE, never MATCH.
func Match ¶ added in v0.3.0
func Match(observed, evidence ActionId) MatchVerdict
Match compares a natively verified evidence identity with the executor's observed identity. It never guesses: anything it cannot decide is INDETERMINATE, which consumers must treat as "not matched" (fail-closed).
type ObtainHint ¶ added in v0.3.0
type ObtainHint struct {
ID string `json:"id"`
Mechanism string `json:"mechanism,omitempty"`
URI string `json:"uri,omitempty"`
}
ObtainHint tells the requester where the outstanding item can be obtained.
type Operation ¶
Operation represents a concrete action request.
func MaterializeDefaults ¶ added in v0.7.0
MaterializeDefaults applies the §6.5 scheme-default rule: for every key the operation omits that the grant declares and the scheme gives a default for, the default is injected before evaluation (precedence explicit > default > absent). A default never adds a key the grant does not declare.
type RecordConstraints ¶ added in v0.3.0
type RecordConstraints struct {
Unresolved []string `json:"unresolved,omitempty"`
}
RecordConstraints carries the §8.4 residual-obligation channel of the decision. This step records only what the core already computes: recognized-but-unevaluated constraints, in the decision's deterministic order. External constraint evaluations are a later step.
type RecordInputs ¶ added in v0.3.0
type RecordInputs struct {
Grants []Grant `json:"grants"`
Operations []Operation `json:"operations"`
// Context is the freshness input the decision was made under (RATS §10).
// Omitted records predate it and keep the same digest: a nil context adds
// no bytes.
Context *DecisionContext `json:"context,omitempty"`
// Sources is the authorization chain the decision rested on (EP-AEG §2).
// Nil on records that predate it, so their digests are unchanged.
Sources *SourceChain `json:"sources,omitempty"`
// RequirementDigest identifies the relying party's evidence requirement
// (CLC-REQUIREMENT-v1) that was applied. The digest, not the object: the
// bar belongs to the relying party's configuration, and a record only has
// to say *which* revision it used (AEC §10).
RequirementDigest *Digest `json:"requirement_digest,omitempty"`
}
RecordInputs is the frozen input of a decision: the grant set and the operation(s) it was evaluated against. The field names are the record's public schema, not an internal detail.
type RecordOptions ¶ added in v0.3.0
type RecordOptions struct {
Context *DecisionContext
Sources *SourceChain
// Requirement is the relying party's evidence requirement. It is
// validated and reduced to its profile digest, which becomes part of the
// hashed inputs. A presenter must not be able to supply or weaken it; this
// API takes it from the caller's configuration, never from the inputs.
Requirement *Requirement
}
RecordOptions are the optional inputs a decision may be recorded with. Both are part of the hashed inputs, so a record identifies exactly what it was decided from rather than "the same grant set, some other time, from some other authority".
type Requirement ¶ added in v0.3.0
type Requirement struct {
Version string `json:"@version"`
ID string `json:"requirement_id"`
Purpose string `json:"purpose,omitempty"`
Expression string `json:"expression"`
Constraints []RequirementConstraint `json:"constraints,omitempty"`
}
Requirement is the relying party's sufficiency bar for one evidence check.
func ParseRequirement ¶ added in v0.3.0
func ParseRequirement(raw []byte) (Requirement, error)
ParseRequirement decodes a requirement as a closed object: a member this revision does not define is an error, never silently dropped. (AEC §4's requirement is likewise a closed object.)
func (Requirement) Digest ¶ added in v0.3.0
func (r Requirement) Digest() (Digest, error)
Digest is the requirement profile digest: the JCS digest of the complete object, so two parties agree on which bar was applied (AEC §4).
func (Requirement) Roles ¶ added in v0.3.0
func (r Requirement) Roles() ([]string, error)
Roles returns the sorted distinct role identifiers the expression names.
func (Requirement) Validate ¶ added in v0.3.0
func (r Requirement) Validate() error
Validate checks the version, the identifiers, every constraint's value grammar, and that the expression parses within the bounded grammar.
type RequirementConstraint ¶ added in v0.3.0
type RequirementConstraint struct {
Role string `json:"role"`
Constraint string `json:"constraint"`
}
RequirementConstraint binds one evidence-side constraint to the evidence role it is evaluated over.
type RequirementResult ¶ added in v0.3.0
type RequirementResult struct {
RequirementID string `json:"requirement_id"`
Digest Digest `json:"digest"`
Verdict EvidenceVerdict `json:"verdict"`
Expression bool `json:"expression"`
MissingRoles []string `json:"missing_roles,omitempty"`
Constraints []EvidenceEvaluation `json:"constraints,omitempty"`
}
RequirementResult is the outcome of evaluating a requirement over facts.
func EvaluateRequirement ¶ added in v0.3.0
func EvaluateRequirement(req Requirement, facts []EvidenceFact, ctx EvidenceContext) (RequirementResult, error)
EvaluateRequirement decides whether the presented facts fill the relying party's bar. The requirement must come from relying-party configuration: this function takes it as an argument and never reads it from the evidence.
Precedence is violated > unknown > satisfied, so no failure degrades toward admission: a violated constraint or a false expression is a definite refusal, and a constraint this revision cannot decide (consumption, an unsupplied boundary identity) leaves the outcome unknown for the consumer to resolve.
func (RequirementResult) Satisfaction ¶ added in v0.4.0
func (r RequirementResult) Satisfaction() Satisfaction
Satisfaction collapses the three-valued evaluation into §10's binary report. A `violated` or an `unknown` both become UNSATISFIED; the reason is the first blocking cause in deterministic order, so `unknown` never reads as SATISFIED.
func (RequirementResult) Satisfied ¶ added in v0.3.0
func (r RequirementResult) Satisfied() bool
Satisfied is the convenience predicate: only an explicit satisfied verdict counts, so "unknown" can never be read as "yes".
type Resolution ¶ added in v0.7.0
Resolution is a consumer's per-obligation report to Resolve (§8.5).
type RetryTiming ¶ added in v0.3.0
type RetryTiming struct {
NotBefore time.Time `json:"not_before"`
JitterSec int `json:"jitter_sec,omitempty"`
}
RetryTiming bounds when a corrected presentation may be attempted.
type Satisfaction ¶ added in v0.4.0
type Satisfaction struct {
Verdict SatisfactionVerdict `json:"verdict"`
Reason string `json:"reason,omitempty"`
}
Satisfaction is §10's exported report form: a binary verdict and a stable reason. It is a projection of RequirementResult and makes no new decision — it exists so a consumer outside the evaluator can act on the normative binary shape instead of parsing the three-valued internals.
type SatisfactionVerdict ¶ added in v0.4.0
type SatisfactionVerdict string
SatisfactionVerdict is the verdict of §10's exported report. The evaluation itself is three-valued; this report is binary.
const ( // SatisfactionSatisfied means every required role was filled and bound and // no constraint failed. SatisfactionSatisfied SatisfactionVerdict = "SATISFIED" // SatisfactionUnsatisfied means the requirement was not met. It covers // both an internal `violated` and an internal `unknown`: §10 requires an // undecidable constraint to collapse to UNSATISFIED with a stable reason, // never to SATISFIED. SatisfactionUnsatisfied SatisfactionVerdict = "UNSATISFIED" )
type Signature ¶ added in v0.3.0
Signature is one DSSE signature entry. KeyID is an unauthenticated hint.
type SourceChain ¶ added in v0.3.0
type SourceChain struct {
Sources []SourceRef `json:"sources"`
Links []SourceLink `json:"links,omitempty"`
}
SourceChain is the set of sources a decision rested on, plus the relations claimed between them.
func (SourceChain) Digest ¶ added in v0.3.0
func (c SourceChain) Digest() (Digest, error)
Digest is the chain's identity: the sorted node identities plus the sorted links, hashed — never the inline material, so two parties holding different disclosures of the same chain agree on it (EP-AEG §2.3).
func (SourceChain) StandingOn ¶ added in v0.3.0
func (c SourceChain) StandingOn(from Digest) ([]SourceRef, error)
StandingOn returns the path an artifact stands on: the artifact first, then each source its own bytes name, ending at a source that names nothing (the authoritative root of this chain).
The walk follows the "artifact names authority" direction (EP-AEG's byte-backed edge), so for `AIC contains the delegation, the delegation stands on the principal certificate` it returns [aic-x509, delegation, principal-cert]. A source that names more than one other source in the chain has no single path: that is ErrSourceChainBranch.
func (SourceChain) Validate ¶ added in v0.3.0
func (c SourceChain) Validate() error
Validate checks the chain's structure and, in particular, that every claimed relation is backed by the source's own references.
type SourceLink ¶ added in v0.3.0
SourceLink is a claimed relation between two sources: From (the authority) names To (the artifact derived from it).
type SourceRef ¶ added in v0.3.0
type SourceRef struct {
// Kind names the artifact class, e.g. "aic-x509", "aic-jwt", "delegation",
// "principal-cert", "policy-permit".
Kind string `json:"kind"`
// ID is a stable identifier within the kind (certificate serial, token
// jti, approval id).
ID string `json:"id"`
// Issuer names who issued it, when that is meaningful.
Issuer string `json:"issuer,omitempty"`
// Digest is the hash of the artifact's natively verified material.
Digest Digest `json:"digest"`
// References are the digests this artifact's own bytes name — the exact
// material backing an edge to another source. A link whose target is not
// listed here is unbacked and refused.
References []Digest `json:"references,omitempty"`
}
SourceRef is one authorization-bearing artifact: a certificate, a token, an approval, a policy decision.
type Statement ¶ added in v0.3.0
type Statement struct {
Type string `json:"_type"`
Subject []Subject `json:"subject"`
PredicateType string `json:"predicateType"`
Predicate DecisionRecord `json:"predicate"`
}
Statement is an in-toto Statement v1 whose predicate is a CLC decision record.
func NewStatement ¶ added in v0.3.0
func NewStatement(rec DecisionRecord) (Statement, error)
NewStatement builds the in-toto statement for a decision record. The subjects are the action (the operation the decision is about) and the decision's own input digest, so both are covered by the statement and by any signature over it.