semantics

package
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2026 License: Apache-2.0 Imports: 21 Imported by: 5

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

View Source
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"
)
View Source
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.

View Source
const (
	// ResolutionSatisfied / ResolutionViolated / ResolutionUnknown are the
	// three §8.5 resolution statuses.
	ResolutionSatisfied = "satisfied"
	ResolutionViolated  = "violated"
	ResolutionUnknown   = "unknown"
)
View Source
const (
	VerdictAllow   = "allow"
	VerdictDeny    = "deny"
	VerdictAllowUR = "allow_unresolved"
)
View Source
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.)

View Source
const ChallengeVersion = "CLC-CHALLENGE-v1"

ChallengeVersion is the only @version this revision accepts.

View Source
const DefaultCombiningAlgorithm = CombiningDenyOverrides

DefaultCombiningAlgorithm is the algorithm a consumer uses unless it has a reason to pin another one.

View Source
const DigestAlgSHA256 = "sha-256"

DigestAlgSHA256 names the only digest algorithm this revision emits.

View Source
const RecordLang = "clc-v1"

RecordLang is the language identifier carried by every Decision Record.

View Source
const RequirementVersion = "CLC-REQUIREMENT-v1"

RequirementVersion is the only @version this revision accepts.

Variables

View Source
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")
)
View Source
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")
)
View Source
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")
)
View Source
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")
)
View Source
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")
)
View Source
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")
)
View Source
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")
)
View Source
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")
)
View Source
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")
)
View Source
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")
)
View Source
var (
	// ErrInvalidResolution / ErrInvalidTimestamp are the §8.5 input-error
	// reason codes.
	ErrInvalidResolution = errors.New("invalid_resolution")
	ErrInvalidTimestamp  = errors.New("invalid_timestamp")
)
View Source
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")
)
View Source
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")
)
View Source
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

func CanonicalJSON(v any) ([]byte, error)

CanonicalJSON returns RFC 8785 (JCS) canonical JSON.

func CheckConstraint

func CheckConstraint(c string, op Operation) error

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

func ConstraintIdentity(c string) (string, error)

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

func ConstraintUnion(chain ...Grant) ([]string, error)

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

func ConstraintWithin(value, bound any) error

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

func CoreEvaluatesEvidenceConstraint(c string) bool

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

func Discharge(d Decision, understood []string) error

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

func EpochIDOf(v uint64) *uint64

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

func MaterialProjection(def ActionTypeDefinition, action map[string]any) (map[string]any, error)

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

func NormalizeForDomains(params map[string]any, dm DomainMap) (map[string]any, string)

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

func ObligationIdentities(d Decision) ([]string, error)

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

func Obligations(d Decision) []string

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

func PAE(payloadType string, payload []byte) []byte

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

func RevisionCompatible(inputRevision string) bool

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

func ValidateCapabilityID(id string) error

ValidateCapabilityID checks whether an ID conforms to CLC-v1 §2 grammar. v1 allows: literal segments and trailing * as a complete segment.

func ValidateConstraint

func ValidateConstraint(c string) error

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

func ValidateEvidenceConstraint(c string) error

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

func ValidateGrantParams(params map[string]any) error

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

func ValidateOperationParams(params map[string]any) error

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

func ValidateParamBounds(bounds, params map[string]any) error

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

func ValidateRawParams(raw string) error

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

func ParseActionId(s string) (ActionId, error)

ParseActionId parses the string form.

func (ActionId) String added in v0.3.0

func (a ActionId) String() string

String renders the identifier form: clc-action:1:<type>:<suite>:<digest-b64url>.

func (ActionId) Validate added in v0.3.0

func (a ActionId) Validate() error

Validate checks the identifier's own shape.

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

type ActionMappingProfile struct {
	ID     string `json:"id"`
	Digest Digest `json:"digest"`
}

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

func (c Challenge) Authorizes() bool

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

func (c Challenge) Digest() (Digest, error)

Digest binds the challenge so a caller can identify exactly which challenge it answered.

func (Challenge) Usable added in v0.3.0

func (c Challenge) Usable(now time.Time) error

Usable reports whether the challenge may still drive a retry at now. An expired challenge is dead: retrying against it is the load amplification the challenge specification warns about.

func (Challenge) Validate added in v0.3.0

func (c Challenge) Validate() error

Validate checks the challenge's shape and bindings.

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

func Authorize(effectiveGrant Grant, op Operation) Decision

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

func AuthorizeJSONText(grants []Grant, opID, rawParams string) Decision

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

func AuthorizeSet(grants []Grant, op Operation) Decision

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

func AuthorizeSetWithDomains(grants []Grant, op Operation, dm DomainMap) Decision

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

func AuthorizeWithChain(chain []Grant, op Operation) Decision

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):

  1. any holder of Inputs can re-run the same Ver and must obtain the same Verdict / Reason / Constraints.Unresolved;
  2. equal Inputs produce an equal InputDigest, so two implementations can agree on "same decision input" without exchanging bytes;
  3. 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

type Digest struct {
	Alg   string `json:"alg"`
	Value []byte `json:"value"`
}

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

func DigestOf(v any) (Digest, error)

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

func DigestOfCanonical(canonical []byte) Digest

DigestOfCanonical hashes bytes already known to be canonical, so Record does not encode the inputs twice.

func (Digest) Equal added in v0.3.0

func (d Digest) Equal(o Digest) bool

Equal reports whether two digests name the same algorithm and bytes.

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

type DomainSpec struct {
	Name string
	Unit string // fixed for quantity; empty otherwise
}

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

func (e Envelope) Check() error

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

func (e *Envelope) Sign(keyID string, sign func(pae []byte) ([]byte, error)) error

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

func (e Envelope) Statement() (Statement, error)

Statement decodes the envelope's payload as an in-toto statement.

func (Envelope) VerifyRecord added in v0.3.0

func (e Envelope) VerifyRecord(verify func(keyID string, pae, sig []byte) error) error

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

type EvidenceContext struct {
	Now       time.Time
	Initiator string
	Executor  string
}

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.

func Intersect

func Intersect(grants ...Grant) (Grant, error)

Intersect combines multiple grants per CLC-v1 §6. Returns the effective grant or an error.

type MatchResult

type MatchResult struct {
	Entails bool
	Reason  string
}

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

type Operation struct {
	ID     string         `json:"id"`
	Params map[string]any `json:"params,omitempty"`
}

Operation represents a concrete action request.

func MaterializeDefaults added in v0.7.0

func MaterializeDefaults(grant Grant, op Operation, defaults map[string]any) Operation

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

type Resolution struct {
	Constraint string `json:"constraint"`
	Status     string `json:"status"`
}

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

type Signature struct {
	KeyID string `json:"keyid,omitempty"`
	Sig   []byte `json:"sig"`
}

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 struct {
	From Digest `json:"from"`
	To   Digest `json:"to"`
}

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.

type Subject added in v0.3.0

type Subject struct {
	Name   string            `json:"name,omitempty"`
	Digest map[string]string `json:"digest"`
}

Subject is one in-toto subject entry: a name and a digest keyed by algorithm in lowercase without separators (in-toto convention: "sha256").

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL