Documentation
¶
Overview ¶
Package gate defines the durable domain envelope for human and policy gates and the generic three-state access evaluator.
The evaluator routes each normalized requirement kind to exactly one structural AccessSource, applies deny-before-allow precedence across configured access and stored rules, and combines every unmet gated capability into one approval carrying exactly the Approve, Approve always for this workspace, and Deny actions.
Boundaries are deliberate: this package never imports an enforcement package such as sandbox (access, rule, and grant seams are structural, built-in-typed interfaces), never parses raw tool arguments (tools prepare typed requests; see pkg/tool), and never defines a permission-file format (durable rule matching and persistence are consumer-provided).
Example ¶
package main
import (
"context"
"fmt"
"github.com/looprig/harness/pkg/gate"
"github.com/looprig/harness/pkg/tool"
)
// This file backs the README example: pkg/gate/README.md §Example mirrors this
// code verbatim. Keep the two in sync.
// staticAllow is a minimal AccessSource that allows every routed scope.
type staticAllow struct{}
func (staticAllow) AccessVersion() uint16 { return gate.CurrentAccessVersion }
func (staticAllow) AccessFor(kind, scope string) (uint8, error) {
return gate.AccessAllow, nil
}
func main() {
evaluator, err := gate.NewHeadlessEvaluator(
[]gate.AccessBinding{{Kind: "fs.read", Source: staticAllow{}}},
nil, // no stored rules
nil, // no grant issuer: no requirement below requests a grant
)
if err != nil {
fmt.Println(err)
return
}
resolution, err := evaluator.Authorize(context.Background(), tool.Request{
ToolName: "Read",
Requirements: []tool.Requirement{{
Kind: "fs.read",
Match: "Read(/repo/README.md)",
Description: "Read /repo/README.md",
}},
})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(resolution.Approved)
}
Output: true
Index ¶
- Constants
- func DecodeRequest(data []byte) (tool.Request, error)
- func MarshalPayload(payload Payload) ([]byte, error)
- func MarshalResponseAudit(audit ResponseAudit) ([]byte, error)
- func ParseFormAnswers(schema PromptSchema, values map[string]json.RawMessage) (map[string]string, error)
- func SubjectDigest(subject PermissionReviewSubject) ([32]byte, error)
- func ValidateFormAuditBounds(audit FormAudit) error
- func ValidateFormSchema(schema PromptSchema) error
- func ValidateGate(g Gate) error
- func ValidateOpenURLPayload(p OpenURLPayload) error
- func ValidatePermissionClassifierName(name hustle.Name) error
- func ValidateReviewCategories(categories []ReviewRiskCategory) error
- type AccessBinding
- type AccessBindings
- type AccessError
- type AccessErrorKind
- type AccessSource
- type Answer
- type ApprovalAction
- type ApprovalActionDecodeError
- type ApprovalPrompt
- type Approver
- type AskUserAudit
- type AskUserPayload
- type Blocks
- type CloseReason
- type Control
- type Criticality
- type DenialReason
- type DisplayOriginError
- type Effect
- type Evaluation
- type EvaluationError
- type EvaluationErrorKind
- type Evaluator
- func (e *Evaluator) Authorize(ctx context.Context, request tool.Request) (Resolution, error)
- func (e *Evaluator) Evaluate(ctx context.Context, request tool.Request) (Evaluation, error)
- func (e *Evaluator) Interactive() bool
- func (e *Evaluator) Resolve(ctx context.Context, evaluation Evaluation, action ApprovalAction) (Resolution, error)
- type EvidenceAccessEvaluator
- type EvidenceContainmentPolicy
- type EvidenceContainmentVerifier
- type EvidenceObservationVerifier
- type Field
- type FieldKind
- type FormAnswerError
- type FormAnswerErrorKind
- type FormAudit
- type FormAuditError
- type FormAuditErrorKind
- type FormPayload
- type FormSchemaError
- type FormSchemaErrorKind
- type Gate
- type GateResponse
- type GateValidationError
- type GateValidationErrorKind
- type GrantIssuer
- type ID
- type Kind
- type ModelDecisionPolicy
- type NilPayloadError
- type NilResponseAuditError
- type ObservationRequirement
- type OpenPayload
- type OpenURLPayload
- type OpenURLPayloadError
- type OpenURLPayloadErrorKind
- type Option
- type Payload
- type PayloadDecodeError
- type PayloadEncodeError
- type PermissionAssessment
- type PermissionAssessmentOutcome
- type PermissionAudit
- type PermissionClassifier
- type PermissionClassifierNameValidationError
- type PermissionClassifierPanicError
- type PermissionClassifierPanicMethod
- type PermissionClassifierSet
- type PermissionClassifierValidationError
- type PermissionClassifierValidationReason
- type PermissionPayload
- type PermissionReviewPolicy
- type PermissionReviewSubject
- type PolicyAction
- type Prompt
- type PromptSchema
- type RequestDecodeError
- type Resolution
- type ResolverKind
- type ResponseAudit
- type ResponseAuditDecodeError
- type ResponseAuditEncodeError
- type ResponsePolicy
- type ResponseRequest
- type ResponseSource
- type ResponseSourceKind
- type ResponseTemplate
- type ResumeInputPayload
- type ReviewAuthorization
- type ReviewBasis
- type ReviewContext
- type ReviewContextEntry
- type ReviewContextKind
- type ReviewContextOrigin
- type ReviewContextPolicy
- type ReviewDecision
- type ReviewDecisionReason
- type ReviewRecommendation
- type ReviewRisk
- type ReviewRiskCategory
- type ReviewStatus
- type ReviewTruncation
- type ReviewTruncationMask
- type ReviewValidationError
- type ReviewValidationField
- type ReviewValidationReason
- type Route
- type RuleMatcher
- type RuleWriter
- type Subject
- type UnknownPayloadKindError
- type UnknownResponseAuditKindError
Examples ¶
Constants ¶
const ( AccessDeny uint8 = 0 AccessGated uint8 = 1 AccessAllow uint8 = 2 )
Access values are fixed by the structural access ABI. AccessSource implementations deliberately return uint8 so Gate need not import an enforcement package's named access type.
const ( // FormActionAccept submits answers; Values must satisfy ParseFormAnswers. FormActionAccept = "accept" // FormActionDecline records an explicit human refusal to answer. FormActionDecline = "decline" // FormActionCancel records that the request was withdrawn or timed out // rather than refused. FormActionCancel = "cancel" )
Form gate response actions. They are the Control.Action / ResponseRequest.Action values a form gate understands, and are answered through the ordinary ResponsePolicy machinery (a PolicyRespond template naming FormActionDecline is the fail-secure default an integration should configure for an unattended form).
const ( MaxObservationRequirementTargetBytes = 4 << 10 MaxObservationRequirementTokenBytes = 4 << 10 MaxObservationRequirementsPerAssessment = 256 )
Bounds on one ObservationRequirement's fields and on how many may travel with a single classifier's PermissionAssessmentOutcome. These are sized like the other bounded string/collection fields in this package (see review_subject.go's MaxPermissionReview* constants) — generous for any real canonical identity or token, small enough that a misbehaving evidence tool cannot use this new channel to smuggle unbounded data through a review.
const ( MaxReviewContextInputEntries = 4096 MaxReviewContextInputBytes = 4 << 20 MaxReviewContextEntryInputBytes = 2 << 20 MaxReviewContextRootFieldBytes = 64 << 10 )
Hard input bounds constrain work before any context is encoded.
const ( // MaxPermissionReviewRationaleBytes bounds live classifier diagnostics. MaxPermissionReviewRationaleBytes = 2048 // MaxPermissionReviewPolicyRevisionBytes bounds the consumer policy label. MaxPermissionReviewPolicyRevisionBytes = 128 // MaxPermissionClassifierRevisionBytes bounds classifier implementation labels. MaxPermissionClassifierRevisionBytes = 128 )
const ( MaxPermissionReviewRequestRequirements = 4096 MaxPermissionReviewRequestCandidates = 4096 MaxPermissionReviewRequestStringBytes = 1 << 20 MaxPermissionReviewRequestInputBytes = 1 << 20 )
Hard request-input bounds constrain work before any request is cloned, validated, or projected onto the canonical subject wire.
const CurrentAccessVersion uint16 = 1
CurrentAccessVersion is the structural access ABI version understood by Gate.
const CurrentGrantVersion uint16 = 1
CurrentGrantVersion is the structural grant ABI version understood by Gate.
const MaxPermissionClassifierNameBytes = 128
MaxPermissionClassifierNameBytes bounds the stable audit identity stored in registry and event records.
const MaxPermissionReviewSubjectWireBytes = 1 << 20
MaxPermissionReviewSubjectWireBytes bounds strict subject decoding before JSON parsing.
const MaxReviewCategories int = 14
MaxReviewCategories is the maximum number of distinct categories a review may carry. It equals the complete initial closed category domain.
const SupportedReviewTruncationMask = ReviewTruncationUserEntry | ReviewTruncationAssistantEntry | ReviewTruncationToolEntry | ReviewTruncationBlock | ReviewTruncationEntryCount | ReviewTruncationTotalBytes | ReviewTruncationEstimatedTokens | ReviewTruncationActiveAction
SupportedReviewTruncationMask contains every truncation bit understood by this revision. Callers can identify unknown bits with mask&^SupportedReviewTruncationMask.
Variables ¶
This section is empty.
Functions ¶
func DecodeRequest ¶
DecodeRequest strictly decodes and validates an untrusted prepared request. Unknown fields, duplicate object keys, trailing JSON, null, and invariant violations all fail closed with RequestDecodeError.
func MarshalPayload ¶
MarshalPayload encodes a sealed payload as a {kind,data} discriminator wrapper.
func MarshalResponseAudit ¶
func MarshalResponseAudit(audit ResponseAudit) ([]byte, error)
MarshalResponseAudit encodes a sealed response audit as a {kind,data} wrapper.
func ParseFormAnswers ¶
func ParseFormAnswers(schema PromptSchema, values map[string]json.RawMessage) (map[string]string, error)
ParseFormAnswers validates a FormActionAccept response's Values against the form schema and returns the answers keyed by field name.
It is strict in both directions and fails closed: every submitted value must name a schema field, match that field's JSON type (a string for text and select, a bool for confirm), stay within maxFormValueBytes, and — for a select — name a declared option. Every required field must be present. A schema that does not satisfy ValidateFormSchema is rejected before any value is read, so a caller cannot smuggle an answer past an unvalidated field.
A confirm answer is normalized to "true"/"false".
func SubjectDigest ¶
func SubjectDigest(subject PermissionReviewSubject) ([32]byte, error)
SubjectDigest validates the non-digest subject invariants and recomputes its canonical digest while deliberately ignoring the stored digest.
func ValidateFormAuditBounds ¶
ValidateFormAuditBounds reports whether a FormAudit is small enough to journal.
A form audit records user-authored content verbatim (see FormAudit), and the schema that solicited it is authored by a third party. Bounding it is therefore what keeps "the journal records what the human said" from becoming "a hostile integration can append whatever it likes to the journal". The bounds are the same ones ParseFormAnswers applies on the way in — an answer that passed it always passes this — and they are re-checked at both codec boundaries so a record that was never parsed (a forged or corrupted one) can be neither written nor read back.
func ValidateFormSchema ¶
func ValidateFormSchema(schema PromptSchema) error
ValidateFormSchema reports whether a schema is a well-formed, bounded, answerable form request. It fails closed: an unknown field kind is rejected rather than treated as free text.
func ValidateGate ¶
ValidateGate checks the envelope invariants that a gate's Kind implies. It is called on the open path so an invariant cannot be violated by a caller who simply forgot it.
It is deliberately narrow. Every kind that predates it validates as nil — this is an additive hook, not a retroactive schema check, and it must stay that way unless an existing kind's contract is separately tightened.
Today it enforces two rules, both on open-url gates:
- It may not be Restorable. OpenURLPayload's action target is never journaled, so a "restored" open-url gate would present a human with an origin and no URL to open — it would fail open into a broken prompt. Restore must instead close it as unavailable and let a live integration mint a fresh request.
- Prompt.Origin must be a bare origin. The envelope is the only thing a renderer sees, and the origin is what the human's trust decision is made on. Validating it HERE — with the same validateDisplayOrigin the durable payload uses, not a second opinion — is what makes "the envelope's origin is a real origin" a contract a renderer can rely on structurally, rather than a convention like an opener stuffing text into Prompt.Body. An open-url gate with no origin is a prompt that asks a human to authorize an unnamed party, so it is refused rather than rendered.
func ValidateOpenURLPayload ¶
func ValidateOpenURLPayload(p OpenURLPayload) error
ValidateOpenURLPayload reports whether p is a live, well-formed open-url request. It is the open-url counterpart of ValidateFormSchema: the check an opener runs BEFORE a gate exists, so a broken request is refused instead of shown to a human.
Two rules, and both are load-bearing:
- DisplayOrigin must be a bare, journal-safe origin. This is the same check the codec applies, hoisted to the open path because the codec is not always on it — a session with no journal (the nop appender) would otherwise render whatever an integration passed straight to a human, which is precisely the trust decision the origin exists to inform.
- URL must be present. A decoded OpenURLPayload always has an empty URL (the action target is never journaled), so an empty URL here means the request is a restored or half-built one with nothing to open. Refusing it is the same fail-closed rule ValidateGate applies to a Restorable open-url gate: an origin with no target is a broken prompt, not a degraded one.
func ValidatePermissionClassifierName ¶
ValidatePermissionClassifierName applies the stricter canonical name contract shared by permission-classifier registries and durable audit events.
func ValidateReviewCategories ¶
func ValidateReviewCategories(categories []ReviewRiskCategory) error
ValidateReviewCategories validates that categories contains only distinct, known values from the bounded category domain.
Types ¶
type AccessBinding ¶
type AccessBinding struct {
Kind string
Source AccessSource
}
AccessBinding routes one requirement kind to one access source.
type AccessBindings ¶
type AccessBindings struct {
// contains filtered or unexported fields
}
AccessBindings is a validated, exact-kind routing table.
func NewAccessBindings ¶
func NewAccessBindings(bindings []AccessBinding) (AccessBindings, error)
NewAccessBindings validates that each configured kind has exactly one current access source.
func (AccessBindings) AccessFor ¶
func (b AccessBindings) AccessFor(requirement tool.Requirement) (uint8, error)
AccessFor routes requirement to its sole configured source.
type AccessError ¶
type AccessError struct {
Kind AccessErrorKind
Requirement string
Cause error
}
AccessError reports a configuration or source failure while routing access.
func (*AccessError) Error ¶
func (e *AccessError) Error() string
func (*AccessError) Unwrap ¶
func (e *AccessError) Unwrap() error
type AccessErrorKind ¶
type AccessErrorKind string
AccessErrorKind classifies a fail-closed access routing failure.
const ( AccessKindInvalid AccessErrorKind = "kind_invalid" AccessSourceMissing AccessErrorKind = "source_missing" AccessSourceDuplicate AccessErrorKind = "source_duplicate" AccessSourceNil AccessErrorKind = "source_nil" AccessVersionUnsupported AccessErrorKind = "version_unsupported" AccessValueInvalid AccessErrorKind = "value_invalid" AccessSourceFailed AccessErrorKind = "source_failed" )
type AccessSource ¶
AccessSource reports the configured access state for normalized kind/scope pairs. Implementations must fail closed for unknown kinds and malformed scopes.
type Answer ¶
type Answer struct {
GateID ID
Action string
// Values holds form answers keyed by field name. It is nil for any action
// other than an accepted form.
Values map[string]string
Source ResponseSource
}
Answer is the validated result of answering a HOST-OWNED gate, delivered live to the opener that is blocked on it.
It is a LIVE delivery type, not a durable record, and has no JSON codec. A gate answered on behalf of a loop turns into a command the loop consumes in memory; this is the same thing for a gate whose opener is the host itself. What survives the process is the GateResolved event and its FormAudit, which records the same answers durably (see FormAudit) — so the two are separate because they travel differently, not because one hides something from the other.
type ApprovalAction ¶
type ApprovalAction string
ApprovalAction is one of the three exact user-facing permission decisions.
const ( ApprovalApprove ApprovalAction = "Approve" ApprovalApproveAlwaysWorkspace ApprovalAction = "Approve always for this workspace" ApprovalDeny ApprovalAction = "Deny" )
func DecodeApprovalAction ¶
func DecodeApprovalAction(data []byte) (ApprovalAction, error)
DecodeApprovalAction strictly decodes one of the three exact approval actions. Unknown fields, duplicate keys, trailing JSON, and null fail closed.
func ParseApprovalAction ¶
func ParseApprovalAction(s string) (ApprovalAction, bool)
ParseApprovalAction returns the exact approval action named by s. It is the single validation source shared by DecodeApprovalAction and the session gate route; anything but the three exact actions fails closed.
type ApprovalActionDecodeError ¶
type ApprovalActionDecodeError struct{ Cause error }
ApprovalActionDecodeError wraps malformed JSON or a non-exact action.
func (*ApprovalActionDecodeError) Error ¶
func (e *ApprovalActionDecodeError) Error() string
func (*ApprovalActionDecodeError) Unwrap ¶
func (e *ApprovalActionDecodeError) Unwrap() error
type ApprovalPrompt ¶
type ApprovalPrompt struct {
Request tool.Request
Unmet []tool.Requirement
Candidates []tool.RuleCandidate
}
ApprovalPrompt is the single combined user-facing approval for one prepared request: the request being decided, every unmet gated requirement, and every reusable candidate displayed for persistence. It never carries tokens.
type Approver ¶
type Approver interface {
RequestApproval(ctx context.Context, prompt ApprovalPrompt) (ApprovalAction, error)
}
Approver resolves one combined approval prompt to exactly one of the three approval actions. It is consulted at most once per authorized call, only by an interactively constructed evaluator, and any error fails closed.
type AskUserAudit ¶
type AskUserAudit struct {
AnswerPreview string `json:"answer_preview,omitempty"`
}
AskUserAudit stores a redacted preview of a user answer.
type AskUserPayload ¶
type AskUserPayload struct {
Question string `json:"question,omitempty"`
Choices []string `json:"choices,omitempty"`
}
AskUserPayload carries an explicit question and optional fixed choices.
type CloseReason ¶
type CloseReason string
CloseReason records why an open gate was closed.
const ( // CloseAnswered records a direct response. CloseAnswered CloseReason = "answered" // ClosePolicyResponse records an automatic policy response. ClosePolicyResponse CloseReason = "policy_response" // CloseAbandoned records that the gate was left unresolved. CloseAbandoned CloseReason = "abandoned" // CloseOwnerClosed records that the owning resolver closed the gate. CloseOwnerClosed CloseReason = "owner_closed" CloseRestoreUnavailable CloseReason = "restore_unavailable" )
type Control ¶
type Control struct {
Action string `json:"action,omitempty"`
Label string `json:"label,omitempty"`
}
Control describes an action the resolver may choose for a prompt.
func ApprovalControls ¶
func ApprovalControls() []Control
ApprovalControls returns the exact, complete control set of a combined access-approval prompt. An interactive gate offers exactly these three actions; there is no session scope, user-global scope, persistent-deny action, or second capability prompt.
type Criticality ¶
type Criticality string
Criticality classifies whether a gate must survive restore boundaries.
const ( // GateCritical marks a gate that must be restored or resolved explicitly. GateCritical Criticality = "critical" // GateNonCritical marks a gate that may be abandoned across restore. GateNonCritical Criticality = "non_critical" )
type DenialReason ¶
type DenialReason string
DenialReason classifies why an unapproved resolution was not approved. It names gate decision stages only, never who denied or which rule matched.
const ( DenialUnspecified DenialReason = "" DenialStructural DenialReason = "structural" DenialRefused DenialReason = "refused" )
type DisplayOriginError ¶
type DisplayOriginError struct {
// contains filtered or unexported fields
}
DisplayOriginError reports a DisplayOrigin that is not a bare, journal-safe origin.
func (*DisplayOriginError) Error ¶
func (e *DisplayOriginError) Error() string
type Evaluation ¶
type Evaluation struct {
Denied []tool.Requirement
Unmet []tool.Requirement
Candidates []tool.RuleCandidate
// contains filtered or unexported fields
}
Evaluation is the result of evaluating one complete prepared request. Denied and Unmet are combined sets in original request order. Candidates contains every reusable candidate displayed for the unmet set.
type EvaluationError ¶
type EvaluationError struct {
Kind EvaluationErrorKind
Requirement string
Cause error
}
EvaluationError reports a dependency failure during evaluation.
func (*EvaluationError) Error ¶
func (e *EvaluationError) Error() string
func (*EvaluationError) Unwrap ¶
func (e *EvaluationError) Unwrap() error
type EvaluationErrorKind ¶
type EvaluationErrorKind string
EvaluationErrorKind classifies a fail-closed evaluation dependency failure.
const ( EvaluationRuleMatchFailed EvaluationErrorKind = "rule_match_failed" // EvaluationDenied is retained for source compatibility. // Deprecated: configured, stored, and approval denials return an unapproved Resolution. EvaluationDenied EvaluationErrorKind = "denied" EvaluationActionInvalid EvaluationErrorKind = "action_invalid" EvaluationApproverMissing EvaluationErrorKind = "approver_missing" EvaluationApprovalRequired EvaluationErrorKind = "approval_required" EvaluationApprovalFailed EvaluationErrorKind = "approval_failed" EvaluationWriterMissing EvaluationErrorKind = "writer_missing" EvaluationWriteFailed EvaluationErrorKind = "write_failed" EvaluationIssuerMissing EvaluationErrorKind = "issuer_missing" EvaluationGrantVersionUnsupported EvaluationErrorKind = "grant_version_unsupported" EvaluationGrantFailed EvaluationErrorKind = "grant_failed" )
type Evaluator ¶
type Evaluator struct {
// contains filtered or unexported fields
}
Evaluator combines structural access, durable rules, approval persistence, and post-decision grant issuance without importing an enforcement package.
Construction explicitly selects interactive or headless interaction: NewInteractiveEvaluator requires both an approver and a durable rule writer so all three approval actions are honest, while NewHeadlessEvaluator accepts neither, never prompts, and resolves an unmet gated requirement as a typed approval-required denial.
func NewHeadlessEvaluator ¶
func NewHeadlessEvaluator(bindings []AccessBinding, matcher RuleMatcher, issuer GrantIssuer) (*Evaluator, error)
NewHeadlessEvaluator constructs a headless evaluator. Headless construction accepts no approver and no rule writer, never prompts, and exposes no interactive actions: a gated requirement with no compatible saved rule resolves to a typed approval-required denial.
func NewInteractiveEvaluator ¶
func NewInteractiveEvaluator(bindings []AccessBinding, matcher RuleMatcher, approver Approver, writer RuleWriter, issuer GrantIssuer) (*Evaluator, error)
NewInteractiveEvaluator constructs an interactive evaluator. Interactive construction requires both an approver and a durable rule writer so all three approval actions (Approve, Approve always for this workspace, Deny) are honest; a missing approver or writer fails construction.
func (*Evaluator) Authorize ¶
Authorize runs one complete prepared request through the combined gate: it evaluates once, opens at most one approval (interactive construction only, and only when gated requirements remain unmet), resolves the chosen action, and mints fresh execution-bound grants for the approved call.
A configured or stored deny, and an interactive Deny action, return an unapproved Resolution with no error and mint nothing. A headless evaluator with unmet requirements returns a typed approval-required denial. Any dependency or approver failure is a fail-closed error.
func (*Evaluator) Evaluate ¶
Evaluate resolves every access state, then every stored deny, then every stored allow. It never serializes approval gates: all unmatched gated requirements are returned together as one combined unmet set.
func (*Evaluator) Interactive ¶
Interactive reports whether this evaluator was interactively constructed.
func (*Evaluator) Resolve ¶
func (e *Evaluator) Resolve(ctx context.Context, evaluation Evaluation, action ApprovalAction) (Resolution, error)
Resolve applies one exact approval action. Workspace approval persists the entire displayed candidate set in one RuleWriter call before any grant is minted; a persistence failure blocks execution. Approve writes nothing. Deny and evaluated denials mint no grants.
type EvidenceAccessEvaluator ¶
type EvidenceAccessEvaluator interface {
AccessFor(tool.Requirement) (uint8, error)
}
EvidenceAccessEvaluator is the deliberately non-interactive, read-only access seam Harness's evidence-tool runtime (internal/hustleruntime, design §13.1) uses to evaluate one prepared evidence Requirement's configured access state. AccessBindings satisfies it structurally without exposing approval, stored-rule, persistence, or grant capabilities; a consumer's own access source (for example a sandbox access-profile adapter) may implement it directly. It lives in this public package — rather than internal/hustleruntime, which an out-of-module consumer like a rig implementation cannot import — so a consumer can name and implement the interface and its collaborator types (see EvidenceContainmentPolicy) and install them via rig.WithPermissionReviewEvidence.
type EvidenceContainmentPolicy ¶
EvidenceContainmentPolicy is the complete security context exposed to an EvidenceContainmentVerifier. ReadRoot must be the canonical workspace root; SecurityCeiling is the effective, non-widenable policy for the ONE review this evidence call belongs to. It is always sourced from that review's own frozen basis (hustle.Request.SecurityCeiling), never a session-wide constant — a long-running session's later review must be bound against ITS OWN current ceiling, not one frozen at session or controller construction.
type EvidenceContainmentVerifier ¶
type EvidenceContainmentVerifier interface {
VerifyEvidenceContainment(ctx context.Context, policy EvidenceContainmentPolicy, request tool.Request) error
}
EvidenceContainmentVerifier independently resolves every prepared evidence target, including symlinks and ambiguous scopes, against the canonical read root and enforces the configured security ceiling (design §13.1). It receives no session, gate, mutation, grant, rule, or loop-control capability — only the two policy values above and a defensive clone of the normalized prepared request. Implementations must fail closed when a tool-owned Requirement cannot be mapped unambiguously. This is the narrow, trusted-caller seam a consumer implements and installs via rig.WithPermissionReviewEvidence.
type EvidenceObservationVerifier ¶
type EvidenceObservationVerifier interface {
VerifyEvidenceObservations(ctx context.Context, policy EvidenceContainmentPolicy, requirements []ObservationRequirement) error
}
EvidenceObservationVerifier is the consumer-supplied, read-only seam that rechecks every previously recorded ObservationRequirement immediately before a classifier-originated auto-approval claims the gate (design §13.4, TOCTOU). It mirrors EvidenceContainmentVerifier's shape exactly — same trusted-caller narrowness, same reused EvidenceContainmentPolicy (the canonical read root plus the review's own non-widenable security ceiling are the identical security context both checks need: containment independently re-resolves ONE prepared request at evidence-gathering time, this independently re-resolves EVERY previously recorded target at pre-approval time — the same "resolve fresh, trust nothing captured earlier" contract, just at a different point in the review's lifecycle). A new EvidenceObservationPolicy type was deliberately NOT introduced: the concerns are identical, and duplicating the type would only invite the two to drift.
It receives no session, gate, mutation, grant, rule, or loop-control capability — only the policy and a defensive copy of the requirements to recheck. Implementations must independently re-derive each requirement's current token from its Target and fail closed (return a non-nil error) on any mismatch OR on any target that can no longer be unambiguously resolved/verified — exactly EvidenceContainmentVerifier's own "fail closed when a ... Requirement cannot be mapped unambiguously" requirement, applied to a recheck instead of a first check. This is the narrow, trusted-caller seam a consumer implements and installs via rig.WithPermissionReviewObservations.
type Field ¶
type Field struct {
Name string `json:"name,omitempty"`
Label string `json:"label,omitempty"`
Kind FieldKind `json:"kind,omitempty"`
Required bool `json:"required,omitzero"`
Options []Option `json:"options,omitempty"`
Default json.RawMessage `json:"default,omitempty"`
}
Field describes one structured input in a prompt schema.
type FieldKind ¶
type FieldKind string
FieldKind identifies the expected shape of a prompt field value.
const ( // FieldText accepts free-form text input. FieldText FieldKind = "text" // FieldSelect accepts one value from a fixed option list. FieldSelect FieldKind = "select" // FieldMultiSelect accepts multiple values from a fixed option list. FieldMultiSelect FieldKind = "multi_select" // FieldConfirm accepts a boolean confirmation. A schema whose only field is // a FieldConfirm is the confirmation-only request. FieldConfirm FieldKind = "confirm" )
type FormAnswerError ¶
type FormAnswerError struct {
Kind FormAnswerErrorKind
Field string
}
FormAnswerError reports a form response that does not satisfy its schema.
func (*FormAnswerError) Error ¶
func (e *FormAnswerError) Error() string
type FormAnswerErrorKind ¶
type FormAnswerErrorKind string
FormAnswerErrorKind classifies a rejected form answer set.
const ( // FormAnswerUnknownField reports a submitted value naming no schema field. FormAnswerUnknownField FormAnswerErrorKind = "unknown_field" // FormAnswerMissingRequired reports a required field with no value. FormAnswerMissingRequired FormAnswerErrorKind = "missing_required" // FormAnswerTypeInvalid reports a value whose JSON type does not match the // field kind (a string for text/select, a bool for confirm). FormAnswerTypeInvalid FormAnswerErrorKind = "type_invalid" // FormAnswerTooLong reports a value exceeding maxFormValueBytes. FormAnswerTooLong FormAnswerErrorKind = "too_long" // FormAnswerOptionNotAllowed reports a select value outside the field options. FormAnswerOptionNotAllowed FormAnswerErrorKind = "option_not_allowed" )
type FormAudit ¶
type FormAudit struct {
// Values holds the submitted answers keyed by field name, exactly as
// ParseFormAnswers produced them (a confirm answer is "true"/"false"). Only
// names declared by the form's schema appear. JSON object keys marshal in
// sorted order, so the durable record is stable.
Values map[string]string `json:"values,omitempty"`
}
FormAudit is the durable record of how a form gate was answered: the answers themselves, keyed by schema field name.
It records USER-AUTHORED CONTENT verbatim, including free text, and that is deliberate. A journal is meant to be the durable truth of a session, and a human's form answer shaped that session as surely as anything they typed into chat — which command.UserInput already records verbatim, block for block. A form answer that reached a durable record only as "a field was answered" would make the journal an incomplete account of its own session. Recording the answer is the norm here; withholding it would be the outlier.
This is NOT a licence to journal secrets, and two controls keep them out:
- Credential-soliciting fields are refused before a form is ever opened, so the answer to one never exists. That rejection lives with the integration that translates a third-party request into a schema (MCP design §Elicitation); it is the load-bearing control, not this type.
- An authorization target is not user content and never becomes one. See OpenURLPayload: a URL carrying a PKCE verifier or `state` is structurally excluded from every durable type, and sensitive authorization goes through an open-url gate rather than a form field.
Unredacted is not unbounded. A form schema is authored by a third party, so a hostile or buggy integration must not be able to append an unbounded record to the journal. Values is bounded on BOTH codec boundaries by ValidateFormAuditBounds: at most maxFormFields entries, each name at most maxFormFieldNameBytes and each value at most maxFormValueBytes. Those are the same bounds ParseFormAnswers enforces on the way in, re-checked here so a record that was never parsed cannot be journaled or restored either.
func NewFormAudit ¶
func NewFormAudit(schema PromptSchema, answers map[string]string) FormAudit
NewFormAudit builds the durable audit for answers that already satisfied ParseFormAnswers against schema.
The schema drives the walk (not the answers map), so a field name the schema never declared cannot reach a durable record even if a caller puts one in answers.
type FormAuditError ¶
type FormAuditError struct {
Kind FormAuditErrorKind
Field string
}
FormAuditError reports a form audit whose contents exceed the durable bounds.
func (*FormAuditError) Error ¶
func (e *FormAuditError) Error() string
type FormAuditErrorKind ¶
type FormAuditErrorKind string
FormAuditErrorKind classifies a rejected form audit record.
const ( // FormAuditTooManyValues reports more than maxFormFields answers. FormAuditTooManyValues FormAuditErrorKind = "too_many_values" // FormAuditFieldNameTooLong reports an over-long answer field name. FormAuditFieldNameTooLong FormAuditErrorKind = "field_name_too_long" // FormAuditValueTooLong reports an answer exceeding maxFormValueBytes. FormAuditValueTooLong FormAuditErrorKind = "value_too_long" )
type FormPayload ¶
type FormPayload struct {
Title string `json:"title,omitempty"`
Body string `json:"body,omitempty"`
Schema PromptSchema `json:"schema,omitzero"`
}
FormPayload carries a bounded, structured human-input request.
It is the AUTHORITATIVE record of what was asked, mirroring how AskUserPayload carries the question that Gate.Prompt merely renders: the Prompt is a presentation projection an opener derives from this payload, while the payload is what a response is validated against (ParseFormAnswers) and what the journal durably records. The two are deliberately not the same field — Prompt is public envelope, the payload is private.
Schema is bounded and restricted to the answerable field kinds; see ValidateFormSchema. It is validated at BOTH codec boundaries, so a malformed schema can neither be journaled nor restored.
type FormSchemaError ¶
type FormSchemaError struct {
Kind FormSchemaErrorKind
Field string
}
FormSchemaError reports a form schema that violates the form contract.
func (*FormSchemaError) Error ¶
func (e *FormSchemaError) Error() string
type FormSchemaErrorKind ¶
type FormSchemaErrorKind string
FormSchemaErrorKind classifies a rejected form schema.
const ( // FormSchemaEmpty reports a schema with no fields. FormSchemaEmpty FormSchemaErrorKind = "schema_empty" // FormSchemaTooManyFields reports more than maxFormFields fields. FormSchemaTooManyFields FormSchemaErrorKind = "schema_too_many_fields" // FormSchemaFieldNameEmpty reports a field with no name. FormSchemaFieldNameEmpty FormSchemaErrorKind = "field_name_empty" // FormSchemaFieldNameTooLong reports an over-long field name. FormSchemaFieldNameTooLong FormSchemaErrorKind = "field_name_too_long" // FormSchemaFieldNameDuplicate reports two fields sharing a name. FormSchemaFieldNameDuplicate FormSchemaErrorKind = "field_name_duplicate" // FormSchemaFieldKindUnsupported reports a field kind a form cannot answer. FormSchemaFieldKindUnsupported FormSchemaErrorKind = "field_kind_unsupported" // FormSchemaFieldOptionsInvalid reports a select field with no options, too // many options, or an empty option value. FormSchemaFieldOptionsInvalid FormSchemaErrorKind = "field_options_invalid" )
type Gate ¶
type Gate struct {
ID ID `json:"id,omitzero"`
Kind Kind `json:"kind,omitempty"`
Resolver ResolverKind `json:"resolver,omitempty"`
Blocks Blocks `json:"blocks,omitempty"`
Effect Effect `json:"effect,omitempty"`
Criticality Criticality `json:"criticality,omitempty"`
Subject Subject `json:"subject,omitzero"`
Prompt Prompt `json:"prompt,omitzero"`
ResponsePolicy ResponsePolicy `json:"response_policy,omitzero"`
Restorable bool `json:"restorable,omitzero"`
}
Gate is the durable envelope for an open human or policy-resolved gate.
type GateResponse ¶
type GateResponse struct {
GateID ID `json:"gate_id,omitzero"`
Action string `json:"action,omitempty"`
Values map[string]json.RawMessage `json:"values,omitempty"`
Source ResponseSource `json:"source,omitzero"`
}
GateResponse is the resolved response envelope for a gate.
type GateValidationError ¶
type GateValidationError struct {
Kind GateValidationErrorKind
GateKind Kind
Cause error
}
GateValidationError reports a gate envelope that violates a kind's invariants.
func (*GateValidationError) Error ¶
func (e *GateValidationError) Error() string
func (*GateValidationError) Unwrap ¶
func (e *GateValidationError) Unwrap() error
type GateValidationErrorKind ¶
type GateValidationErrorKind string
GateValidationErrorKind classifies a rejected gate envelope.
const ( // GateRestorableNotAllowed reports a gate marked Restorable whose kind can // never be restored. GateRestorableNotAllowed GateValidationErrorKind = "restorable_not_allowed" // GateOriginInvalid reports a gate whose Prompt.Origin is missing or is not // a bare origin, on a kind that requires one. GateOriginInvalid GateValidationErrorKind = "origin_invalid" )
type GrantIssuer ¶
type GrantIssuer interface {
GrantVersion() uint16
IssueGrant(ctx context.Context, executionID, command, cwd, kind, scope, class, target string, expiryUnixMilli int64) (string, error)
}
GrantIssuer mints one structural, execution-bound grant token. The signature is intentionally dependency-free and is satisfied structurally by an enforcing executor without importing harness.
type ID ¶
ID is the shared gate identifier type. It aliases uuid.UUID so existing text and JSON codecs are preserved exactly.
type Kind ¶
type Kind string
Kind identifies the user-facing gate scenario.
const ( // KindPermission is a tool permission approval gate. KindPermission Kind = "harness.permission" // KindAskUser is an explicit user-question gate. KindAskUser Kind = "harness.ask_user" // KindForm is a structured human-input gate: a bounded set of typed fields // (including a confirmation-only field) answered by a human or by policy. It // is protocol-neutral — any integration that needs structured human input // opens one. KindForm Kind = "harness.form" // KindOpenURL is a gate asking a human to open an action URL out-of-band // (typically a browser) and, when RequiresCompletion is set, to report back. // // An open-url gate is inherently EPHEMERAL: its action target is bound to a // live out-of-band exchange and is deliberately never journaled (see // OpenURLPayload), so it can never be restored. ValidateGate rejects an // open-url gate marked Restorable. KindOpenURL Kind = "harness.open_url" )
type ModelDecisionPolicy ¶
type ModelDecisionPolicy struct {
Prompt string `json:"prompt,omitempty"`
AllowedActions []string `json:"allowed_actions,omitempty"`
Default ResponseTemplate `json:"default,omitzero"`
Metadata json.RawMessage `json:"metadata,omitempty"`
}
ModelDecisionPolicy configures a model-assisted gate decision.
type NilPayloadError ¶
type NilPayloadError struct{}
NilPayloadError is returned when MarshalPayload receives a nil payload.
func (*NilPayloadError) Error ¶
func (e *NilPayloadError) Error() string
type NilResponseAuditError ¶
type NilResponseAuditError struct{}
NilResponseAuditError is returned when MarshalResponseAudit receives nil audit data.
func (*NilResponseAuditError) Error ¶
func (e *NilResponseAuditError) Error() string
type ObservationRequirement ¶
ObservationRequirement is one canonical-identity/token pair recorded when a target-sensitive evidence tool observed one target during evidence gathering (design §13.4, TOCTOU). Target is the verifier-defined canonical identity of the observed target (for example a canonicalized absolute path or a resolved git ref) — Harness never computes, interprets, or canonicalizes it, for the same reason EvidenceContainmentVerifier's own doc comment gives for why Harness does not own path canonicalization. Token is an opaque, verifier-defined proof of that target's observed state at capture time (for example a hash of stable metadata) — again never computed or interpreted by Harness.
A zero ObservationRequirement is never valid on its own (see Valid); the zero value exists only so the type is usable as an ordinary Go value (comparable, safe to append to a nil slice).
func (ObservationRequirement) Valid ¶
func (o ObservationRequirement) Valid() bool
Valid reports whether both fields are non-empty, valid UTF-8, free of NUL bytes, and within MaxObservationRequirementTargetBytes/TokenBytes. It is the one shape check every producer and consumer of this type shares — evidence_runner.go applies it before ever recording a requirement (a malformed report from a target-sensitive tool is dropped, not retained), and CombinePermissionAssessments applies it again at the outcome boundary (defense in depth: never trust a single call site to have validated untrusted-shaped data).
type OpenPayload ¶
type OpenPayload struct {
GateID ID `json:"gate_id,omitzero"`
Payload Payload `json:"payload,omitempty"`
}
OpenPayload records a gate opening and its nested scenario payload.
type OpenURLPayload ¶
type OpenURLPayload struct {
DisplayOrigin string `json:"display_origin,omitempty"`
// URL is never serialized. See the type doc.
URL string `json:"-"`
RequiresCompletion bool `json:"requires_completion,omitzero"`
}
OpenURLPayload asks a human to open an action URL out-of-band.
URL is the EPHEMERAL action target. An authorization URL carries secrets — OAuth `state`, a PKCE challenge, one-time codes — so it MUST NOT reach a journal, an event, or an audit record. That exclusion is STRUCTURAL, not remembered: the field is `json:"-"`, and the codec marshals openURLPayloadData, a type that HAS NO URL FIELD at all. Both boundaries have to be deleted for a URL to leak. A decoded OpenURLPayload therefore ALWAYS has an empty URL, which is exactly why an open-url gate may not be Restorable (enforced by ValidateGate) — the action target cannot survive a restore, and a reconnecting integration must mint a fresh one.
DisplayOrigin is the durable, journal-safe origin shown to the human, e.g. "https://github.com". It is validated as a BARE origin (scheme + host only) at both codec boundaries: without that check a caller could defeat the whole design by passing the full action URL as the "origin".
type OpenURLPayloadError ¶
type OpenURLPayloadError struct {
Kind OpenURLPayloadErrorKind
}
OpenURLPayloadError reports an open-url payload that cannot be acted on.
func (*OpenURLPayloadError) Error ¶
func (e *OpenURLPayloadError) Error() string
type OpenURLPayloadErrorKind ¶
type OpenURLPayloadErrorKind string
OpenURLPayloadErrorKind classifies a rejected open-url payload.
const ( // OpenURLTargetMissing reports an open-url payload with no action URL. OpenURLTargetMissing OpenURLPayloadErrorKind = "target_missing" )
type Payload ¶
type Payload interface {
// contains filtered or unexported methods
}
Payload is the sealed union of durable gate payload records.
func UnmarshalPayload ¶
UnmarshalPayload decodes a {kind,data} payload wrapper and fails closed on unknown kinds.
type PayloadDecodeError ¶
PayloadDecodeError wraps malformed payload JSON or malformed payload data.
func (*PayloadDecodeError) Error ¶
func (e *PayloadDecodeError) Error() string
func (*PayloadDecodeError) Unwrap ¶
func (e *PayloadDecodeError) Unwrap() error
type PayloadEncodeError ¶
PayloadEncodeError wraps failures while encoding a payload wrapper or data.
func (*PayloadEncodeError) Error ¶
func (e *PayloadEncodeError) Error() string
func (*PayloadEncodeError) Unwrap ¶
func (e *PayloadEncodeError) Unwrap() error
type PermissionAssessment ¶
type PermissionAssessment struct {
Basis ReviewBasis
Risk ReviewRisk
Authorization ReviewAuthorization
Categories []ReviewRiskCategory
Recommendation ReviewRecommendation
Rationale string
}
PermissionAssessment is a classifier's authority-free recommendation for one exact permission review subject.
type PermissionAssessmentOutcome ¶
type PermissionAssessmentOutcome struct {
Subject PermissionReviewSubject
Applicable bool
Status ReviewStatus
Assessment PermissionAssessment
Observations []ObservationRequirement
}
PermissionAssessmentOutcome is one classifier's applicability and terminal review state. Only applicable, allowed outcomes carry an assessment that can contribute to eligibility.
Observations is the set of ObservationRequirement tokens (design §13.4, TOCTOU) this classifier's OWN evidence gathering recorded — one review may gather evidence about several distinct targets, so this is a slice, not a single value. It is meaningful only when Status is ReviewStatusAllowed (validPermissionAssessmentOutcome requires it empty otherwise, mirroring Assessment's own zero-value-unless-allowed rule): a classifier that never reached an allowed terminal never contributes evidence a response could be approved on, so its observations (if any were gathered before it failed) are simply discarded rather than carried forward.
type PermissionAudit ¶
type PermissionAudit struct {
RequirementDescriptions []string `json:"requirement_descriptions,omitempty"`
CandidateDescriptions []string `json:"candidate_descriptions,omitempty"`
}
PermissionAudit is the durable, redacted record of a permission approval: the bounded display DESCRIPTIONS of the requirements the user approved and — for a workspace approval — of the reusable rule candidates that were displayed for persistence. It never carries grant tokens, token material, or raw tool arguments; descriptions are the only permitted content.
type PermissionClassifier ¶
type PermissionClassifier interface {
Name() hustle.Name
Revision() string
Definition() hustle.Definition
Applies(PermissionReviewSubject) bool
MarshalInput(PermissionReviewSubject) (json.RawMessage, error)
ValidateResult(PermissionReviewSubject, hustle.Result) (PermissionAssessment, error)
}
PermissionClassifier is the deliberately narrow contract implemented by trusted classifier packages. It conveys data and immutable Hustle policy, never gate response or durable-grant authority.
type PermissionClassifierNameValidationError ¶
type PermissionClassifierNameValidationError struct{}
PermissionClassifierNameValidationError reports a rejected classifier audit name without echoing the untrusted value.
func (*PermissionClassifierNameValidationError) Error ¶
func (*PermissionClassifierNameValidationError) Error() string
type PermissionClassifierPanicError ¶
type PermissionClassifierPanicError struct {
Method PermissionClassifierPanicMethod
}
PermissionClassifierPanicError is the redacted recovery product for a panic raised by a registered PermissionClassifier implementation. Classifiers are trusted but not infallible: a buggy implementation can still panic, and a panic on the review goroutine would otherwise crash the whole process rather than just fail the one review. This is the bounded internal failure design's error taxonomy calls "callback panic at a trusted boundary" — it deliberately retains no panic value, since the panic could be an error, a string, or anything else, and could itself carry raw classifier- controlled subject content.
func (*PermissionClassifierPanicError) Error ¶
func (e *PermissionClassifierPanicError) Error() string
type PermissionClassifierPanicMethod ¶
type PermissionClassifierPanicMethod string
PermissionClassifierPanicMethod identifies which trust-boundary method of a registered PermissionClassifier panicked. It exists only to route a recovered panic into a bounded, content-free error — never to convey the panic value itself.
const ( PermissionClassifierPanicMarshalInput PermissionClassifierPanicMethod = "marshal_input" PermissionClassifierPanicValidateResult PermissionClassifierPanicMethod = "validate_result" )
type PermissionClassifierSet ¶
type PermissionClassifierSet struct {
// contains filtered or unexported fields
}
PermissionClassifierSet is an immutable, ordered classifier registry.
func NewPermissionClassifierSet ¶
func NewPermissionClassifierSet( classifiers ...PermissionClassifier, ) (PermissionClassifierSet, error)
NewPermissionClassifierSet validates metadata without executing classifier applicability, serialization, or result parsing behavior.
func (PermissionClassifierSet) Classifiers ¶
func (s PermissionClassifierSet) Classifiers() []PermissionClassifier
Classifiers returns an independent ordered registry view.
type PermissionClassifierValidationError ¶
type PermissionClassifierValidationError struct {
Index int
Reason PermissionClassifierValidationReason
}
PermissionClassifierValidationError reports only a bounded reason and registration position.
func (*PermissionClassifierValidationError) Error ¶
func (*PermissionClassifierValidationError) Error() string
type PermissionClassifierValidationReason ¶
type PermissionClassifierValidationReason string
PermissionClassifierValidationReason is the bounded registry rejection domain. Rejected classifier metadata is never included in the error.
const ( PermissionClassifierInvalid PermissionClassifierValidationReason = "invalid" PermissionClassifierDuplicate PermissionClassifierValidationReason = "duplicate" )
type PermissionPayload ¶
PermissionPayload carries the typed prepared access request a permission gate decides — the request narrowed to what the approval prompt displayed. It is validated at BOTH codec boundaries (tool.ValidateRequest on marshal, the strict DecodeRequest on unmarshal), so a malformed or token-bearing record can neither be journaled nor restored. It never carries grant tokens: tool.Request has no token field, and unknown wire keys are rejected.
type PermissionReviewPolicy ¶
type PermissionReviewPolicy struct {
Revision string
MaximumAutoRisk ReviewRisk
MinimumAuthorization map[ReviewRisk]ReviewAuthorization
AbsoluteHuman []ReviewRiskCategory
MaterialTruncation ReviewTruncationMask
// contains filtered or unexported fields
}
PermissionReviewPolicy is the local, consumer-owned ceiling applied after a classifier result has been validated.
func DefaultPermissionReviewPolicy ¶
func DefaultPermissionReviewPolicy(revision string) (PermissionReviewPolicy, error)
DefaultPermissionReviewPolicy constructs the Codex-compatible default: low and medium need no authorization evidence, while high requires medium.
func NewPermissionReviewPolicy ¶
func NewPermissionReviewPolicy( revision string, maximum ReviewRisk, minimum map[ReviewRisk]ReviewAuthorization, absoluteHuman []ReviewRiskCategory, material ReviewTruncationMask, ) (PermissionReviewPolicy, error)
NewPermissionReviewPolicy validates and owns a policy that is at least as restrictive as Harness's hard review ceiling.
func (PermissionReviewPolicy) Sealed ¶
func (p PermissionReviewPolicy) Sealed() bool
Sealed reports whether policy was constructed through NewPermissionReviewPolicy or DefaultPermissionReviewPolicy, as opposed to a hand-built literal PermissionReviewPolicy{} that never went through either constructor. It is a convenience for a caller that wants to fail fast at its own configuration boundary rather than discovering the same zero seal later: EvaluatePermissionAssessment already fails closed on an unsealed policy regardless of whether a caller checks Sealed() first, so this method adds no new security enforcement — it only reports the existing invariant earlier and with a clearer symptom.
type PermissionReviewSubject ¶
type PermissionReviewSubject struct {
Basis ReviewBasis `json:"basis"`
Request tool.Request `json:"request"`
Context ReviewContext `json:"context"`
}
PermissionReviewSubject is the immutable, authority-labeled input to a permission classifier.
func NewPermissionReviewSubject ¶
func NewPermissionReviewSubject( basis ReviewBasis, request tool.Request, context ReviewContext, ) (PermissionReviewSubject, error)
NewPermissionReviewSubject validates, owns, and digest-stamps one subject.
func (PermissionReviewSubject) Clone ¶
func (s PermissionReviewSubject) Clone() PermissionReviewSubject
Clone returns an owned copy of the subject and all nested slices.
type PolicyAction ¶
type PolicyAction string
PolicyAction names the automatic behavior to apply when policy resolves a gate.
const ( // PolicyWait leaves the gate open for an explicit response. PolicyWait PolicyAction = "wait" // PolicyRespond resolves the gate with a configured response template. PolicyRespond PolicyAction = "respond" // PolicySuspendSession suspends session progress while the gate remains open. PolicySuspendSession PolicyAction = "suspend_session" // PolicyModelDecide asks a model decision policy to choose a response. PolicyModelDecide PolicyAction = "model_decide" )
type Prompt ¶
type Prompt struct {
Title string `json:"title,omitempty"`
Body string `json:"body,omitempty"`
// Origin is the validated bare origin an open-url gate asks a human to
// authorize, e.g. "https://github.com". It is the security-load-bearing part
// of such a prompt: it is the thing a human makes the trust decision on, so
// a renderer must be able to display it AS a validated origin.
//
// For a KindOpenURL gate it is REQUIRED and ValidateGate enforces that it is
// a bare origin — scheme and host, no path, query, fragment, or userinfo —
// with the same check that guards the durable OpenURLPayload.DisplayOrigin.
// That is what lets a renderer trust it structurally instead of by
// convention: an opener cannot smuggle a full action URL (with its `state`
// and PKCE parameters) into the place a human reads as "who am I trusting".
//
// It is NOT the action target. The ephemeral URL lives only on the private
// OpenURLPayload, reaches no durable record and no renderer, and opening it
// is the host's job.
//
// Other kinds leave it empty.
Origin string `json:"origin,omitempty"`
Schema PromptSchema `json:"schema,omitzero"`
Controls []Control `json:"controls,omitempty"`
}
Prompt is the user-facing content and schema for resolving a gate.
It is the PUBLIC presentation projection: it travels on the gate envelope (and therefore on event.GateOpened) while the payload stays private to the opener and the session. A renderer sees only this.
Every field a renderer must be able to TRUST — rather than treat as arbitrary text an integration supplied — is derived from the private payload by the session at open time (see the GateHost open path), never taken on the caller's word. Origin and Schema are those fields; Title and Body are prose.
type PromptSchema ¶
type PromptSchema struct {
Fields []Field `json:"fields,omitempty"`
}
PromptSchema groups the structured fields requested by a prompt.
type RequestDecodeError ¶
type RequestDecodeError struct{ Cause error }
RequestDecodeError wraps malformed request JSON or a decoded request that violates the prepared request invariants.
func (*RequestDecodeError) Error ¶
func (e *RequestDecodeError) Error() string
func (*RequestDecodeError) Unwrap ¶
func (e *RequestDecodeError) Unwrap() error
type Resolution ¶
type Resolution struct {
Approved bool `json:"approved"`
Grants []string `json:"-"`
Denial DenialReason `json:"-"`
DenialDescription string `json:"-"`
}
Resolution is the live result of applying an approval action. Grants and denial routing metadata are deliberately excluded from JSON: they may only travel through the live authorization path, never a prompt, display, journal, or audit payload.
type ResolverKind ¶
type ResolverKind string
ResolverKind identifies the owner responsible for resolving a gate response.
const ( // ResolverLoop routes responses to a loop-local gate resolver. ResolverLoop ResolverKind = "loop" // ResolverSession routes responses through the session gate resolver. ResolverSession ResolverKind = "session" )
type ResponseAudit ¶
type ResponseAudit interface {
// contains filtered or unexported methods
}
ResponseAudit is the sealed union of durable, redacted gate response audit records.
func UnmarshalResponseAudit ¶
func UnmarshalResponseAudit(data []byte) (ResponseAudit, error)
UnmarshalResponseAudit decodes a {kind,data} wrapper and fails closed on unknown kinds.
type ResponseAuditDecodeError ¶
ResponseAuditDecodeError wraps malformed audit JSON or malformed audit data.
func (*ResponseAuditDecodeError) Error ¶
func (e *ResponseAuditDecodeError) Error() string
func (*ResponseAuditDecodeError) Unwrap ¶
func (e *ResponseAuditDecodeError) Unwrap() error
type ResponseAuditEncodeError ¶
ResponseAuditEncodeError wraps failures while encoding response audit data.
func (*ResponseAuditEncodeError) Error ¶
func (e *ResponseAuditEncodeError) Error() string
func (*ResponseAuditEncodeError) Unwrap ¶
func (e *ResponseAuditEncodeError) Unwrap() error
type ResponsePolicy ¶
type ResponsePolicy struct {
// Timeout marshals as a time.Duration integer in nanoseconds.
Timeout time.Duration `json:"timeout,omitzero"`
// OnTimeout is the action taken when Timeout elapses.
OnTimeout PolicyAction `json:"on_timeout,omitempty"`
// Response is the template submitted through RespondGate for PolicyRespond.
Response ResponseTemplate `json:"response,omitzero"`
// ModelDecision configures PolicyModelDecide when a responder exists.
ModelDecision ModelDecisionPolicy `json:"model_decision,omitzero"`
}
ResponsePolicy describes automatic handling for an unresolved gate.
func (ResponsePolicy) EffectiveAction ¶
func (p ResponsePolicy) EffectiveAction() PolicyAction
EffectiveAction returns the configured timeout action, defaulting to PolicyWait.
type ResponseRequest ¶
type ResponseRequest struct {
Action string `json:"action,omitempty"`
Values map[string]json.RawMessage `json:"values,omitempty"`
}
ResponseRequest is the generic action and value payload used to answer a gate.
type ResponseSource ¶
type ResponseSource struct {
Kind ResponseSourceKind `json:"kind,omitempty"`
Reason string `json:"reason,omitempty"`
}
ResponseSource describes the origin and reason for a response.
type ResponseSourceKind ¶
type ResponseSourceKind string
ResponseSourceKind identifies who produced a gate response.
const ( // ResponseFromUser records a response supplied by a human user. ResponseFromUser ResponseSourceKind = "user" // ResponseFromPolicy records a response supplied by gate policy. ResponseFromPolicy ResponseSourceKind = "policy" // ResponseFromModel records a response supplied by model decision policy. ResponseFromModel ResponseSourceKind = "model" // ResponseFromClassifier records a response the session itself // constructed from an eligible automated permission-classifier // assessment (design §16.3). Only a private session-runtime method may // stamp this source; a public caller's attempt to select it directly is // rejected rather than honored (see internal/sessionruntime.RespondGate). ResponseFromClassifier ResponseSourceKind = "classifier" )
type ResponseTemplate ¶
type ResponseTemplate struct {
Action string `json:"action,omitempty"`
Values map[string]json.RawMessage `json:"values,omitempty"`
}
ResponseTemplate is a reusable response payload for policy-driven resolution.
type ResumeInputPayload ¶
type ResumeInputPayload struct {
InputID uuid.UUID `json:"input_id,omitzero"`
Preview string `json:"preview,omitempty"`
}
ResumeInputPayload records input made available when a gate resumes work.
type ReviewAuthorization ¶
type ReviewAuthorization string
ReviewAuthorization is the closed strength of authorization evidenced by the live review context.
const ( ReviewAuthorizationUnknown ReviewAuthorization = "unknown" ReviewAuthorizationLow ReviewAuthorization = "low" ReviewAuthorizationMedium ReviewAuthorization = "medium" ReviewAuthorizationHigh ReviewAuthorization = "high" )
func ParseReviewAuthorization ¶
func ParseReviewAuthorization(value string) (ReviewAuthorization, bool)
ParseReviewAuthorization parses a closed review authorization value.
type ReviewBasis ¶
type ReviewBasis struct {
GateID ID `json:"gate_id"`
ToolExecutionID ID `json:"tool_execution_id"`
SubjectDigest [32]byte `json:"subject_digest"`
ContextRevision string `json:"context_revision"`
GatePolicyRevision string `json:"gate_policy_revision"`
ClassifierRevision string `json:"classifier_revision"`
SecurityCeiling string `json:"security_ceiling"`
}
ReviewBasis binds a classifier decision to one exact live permission request and the policy revisions under which it was reviewed.
type ReviewContext ¶
type ReviewContext struct {
Coordinates identity.Coordinates
ContextRevision string
WorkspaceRoot string
WorkingDirectory string
RetryReason string
SecurityCeiling string
GatePolicyRevision string
Entries []ReviewContextEntry
Truncation ReviewTruncation
}
ReviewContext is a bounded live-only snapshot used by permission review.
func BuildReviewContext ¶
func BuildReviewContext(input ReviewContext, policy ReviewContextPolicy) (ReviewContext, error)
BuildReviewContext builds an owned, validated review context snapshot.
func (ReviewContext) Clone ¶
func (c ReviewContext) Clone() ReviewContext
Clone returns a context that does not alias the receiver's entry slice.
type ReviewContextEntry ¶
type ReviewContextEntry struct {
Origin ReviewContextOrigin
Kind ReviewContextKind
Content string
Truncated bool
}
ReviewContextEntry is an authority-labeled block in a review snapshot.
type ReviewContextKind ¶
type ReviewContextKind string
ReviewContextKind is the semantic kind of one permission-review entry.
const ( ReviewContextKindUserMessage ReviewContextKind = "user_message" ReviewContextKindAssistantMessage ReviewContextKind = "assistant_message" ReviewContextKindAssistantToolRequest ReviewContextKind = "assistant_tool_request" ReviewContextKindToolResult ReviewContextKind = "tool_result" ReviewContextKindToolPreview ReviewContextKind = "tool_preview" ReviewContextKindRuntimeContext ReviewContextKind = "runtime_context" ReviewContextKindExternalContent ReviewContextKind = "external_content" ReviewContextKindOmission ReviewContextKind = "omission" )
func ParseReviewContextKind ¶
func ParseReviewContextKind(value string) (ReviewContextKind, bool)
ParseReviewContextKind parses one exact closed entry kind.
type ReviewContextOrigin ¶
type ReviewContextOrigin string
ReviewContextOrigin is the authority origin of one permission-review entry.
const ( ReviewContextOriginUser ReviewContextOrigin = "user" ReviewContextOriginAssistant ReviewContextOrigin = "assistant" ReviewContextOriginTool ReviewContextOrigin = "tool" ReviewContextOriginRuntime ReviewContextOrigin = "runtime" ReviewContextOriginExternal ReviewContextOrigin = "external" ReviewContextOriginOmission ReviewContextOrigin = "omission" )
func ParseReviewContextOrigin ¶
func ParseReviewContextOrigin(value string) (ReviewContextOrigin, bool)
ParseReviewContextOrigin parses one exact closed authority origin.
type ReviewContextPolicy ¶
type ReviewContextPolicy struct {
Revision string
MaxBytes int
MaxEstimatedTokens int
MaxEntries int
MaxUserEntryBytes int
MaxAgentEntryBytes int
MaxToolEntryBytes int
MaxBlockBytes int
MaxActiveActionBytes int
}
ReviewContextPolicy specifies deterministic bounds for a review snapshot.
type ReviewDecision ¶
type ReviewDecision struct {
Eligible bool
Reason ReviewDecisionReason
}
ReviewDecision reports only local one-shot eligibility. It deliberately carries neither a gate action nor classifier-provided text.
func CombinePermissionAssessments ¶
func CombinePermissionAssessments( policy PermissionReviewPolicy, classifiers PermissionClassifierSet, outcomes []PermissionAssessmentOutcome, ) ReviewDecision
CombinePermissionAssessments applies ordered conjunctive review semantics against one immutable registered classifier set. Every registered classifier must contribute exactly one outcome in registration order. The first applicable failure wins; non-applicable outcomes are neutral only when their status is exactly not_applicable.
func EvaluatePermissionAssessment ¶
func EvaluatePermissionAssessment( policy PermissionReviewPolicy, subject PermissionReviewSubject, assessment PermissionAssessment, ) ReviewDecision
EvaluatePermissionAssessment validates all public inputs again and applies the local hard ceiling without mutating them.
type ReviewDecisionReason ¶
type ReviewDecisionReason string
ReviewDecisionReason is the closed, non-sensitive explanation for a local review eligibility decision.
const ( ReviewDecisionEligible ReviewDecisionReason = "eligible" ReviewDecisionInvalidPolicy ReviewDecisionReason = "invalid_policy" ReviewDecisionInvalidAssessment ReviewDecisionReason = "invalid_assessment" ReviewDecisionBasisMismatch ReviewDecisionReason = "basis_mismatch" ReviewDecisionRecommendation ReviewDecisionReason = "recommendation" ReviewDecisionRiskCeiling ReviewDecisionReason = "risk_ceiling" ReviewDecisionAuthorization ReviewDecisionReason = "authorization" ReviewDecisionAbsoluteHuman ReviewDecisionReason = "absolute_human" ReviewDecisionMaterialTruncation ReviewDecisionReason = "material_truncation" ReviewDecisionNoApplicableClassifier ReviewDecisionReason = "no_applicable_classifier" ReviewDecisionClassifierStatus ReviewDecisionReason = "classifier_status" )
func ParseReviewDecisionReason ¶
func ParseReviewDecisionReason(value string) (ReviewDecisionReason, bool)
ParseReviewDecisionReason parses an exact closed decision reason.
func (ReviewDecisionReason) Valid ¶
func (r ReviewDecisionReason) Valid() bool
Valid reports whether the reason belongs to the closed decision domain.
type ReviewRecommendation ¶
type ReviewRecommendation string
ReviewRecommendation is the classifier's closed recommendation. A classifier can recommend a one-shot allow or defer to the already-open human gate; it cannot deny or create a durable approval.
const ( ReviewAllow ReviewRecommendation = "allow" ReviewNeedsHuman ReviewRecommendation = "needs_human" )
func ParseReviewRecommendation ¶
func ParseReviewRecommendation(value string) (ReviewRecommendation, bool)
ParseReviewRecommendation parses a closed review recommendation value.
type ReviewRisk ¶
type ReviewRisk string
ReviewRisk is the classifier's closed risk level for a permission request.
const ( ReviewRiskLow ReviewRisk = "low" ReviewRiskMedium ReviewRisk = "medium" ReviewRiskHigh ReviewRisk = "high" ReviewRiskCritical ReviewRisk = "critical" )
func ParseReviewRisk ¶
func ParseReviewRisk(value string) (ReviewRisk, bool)
ParseReviewRisk parses a closed review risk value.
type ReviewRiskCategory ¶
type ReviewRiskCategory string
ReviewRiskCategory is a closed, policy-relevant reason for a review risk.
const ( ReviewCategoryDataExfiltration ReviewRiskCategory = "data_exfiltration" ReviewCategoryCredentialAccess ReviewRiskCategory = "credential_access" // #nosec G101 -- Closed taxonomy label, not a credential. ReviewCategoryCredentialProbing ReviewRiskCategory = "credential_probing" // #nosec G101 -- Closed taxonomy label, not a credential. ReviewCategoryDestructiveLocal ReviewRiskCategory = "destructive_local" ReviewCategoryPersistentSecurityWeakening ReviewRiskCategory = "persistent_security_weakening" ReviewCategoryProductionMutation ReviewRiskCategory = "production_mutation" ReviewCategoryProtectedSourceControl ReviewRiskCategory = "protected_source_control" ReviewCategoryUntrustedCodeExecution ReviewRiskCategory = "untrusted_code_execution" ReviewCategoryMutableNetwork ReviewRiskCategory = "mutable_network" ReviewCategoryPromptInjection ReviewRiskCategory = "prompt_injection" ReviewCategoryAuthorizationConflict ReviewRiskCategory = "authorization_conflict" ReviewCategoryTargetAmbiguity ReviewRiskCategory = "target_ambiguity" ReviewCategoryInsufficientEvidence ReviewRiskCategory = "insufficient_evidence" )
func ParseReviewRiskCategory ¶
func ParseReviewRiskCategory(value string) (ReviewRiskCategory, bool)
ParseReviewRiskCategory parses a closed review risk category.
type ReviewStatus ¶
type ReviewStatus string
ReviewStatus is the closed terminal status recorded for a permission review.
const ( ReviewStatusAllowed ReviewStatus = "allowed" ReviewStatusNeedsHuman ReviewStatus = "needs_human" ReviewStatusNotApplicable ReviewStatus = "not_applicable" ReviewStatusTimedOut ReviewStatus = "timed_out" ReviewStatusFailed ReviewStatus = "failed" ReviewStatusCancelled ReviewStatus = "cancelled" ReviewStatusStale ReviewStatus = "stale" )
func ParseReviewStatus ¶
func ParseReviewStatus(value string) (ReviewStatus, bool)
ParseReviewStatus parses a closed review status value.
type ReviewTruncation ¶
type ReviewTruncation struct {
Applied ReviewTruncationMask
Material ReviewTruncationMask
OmittedEntries int
OmittedBytes int
}
ReviewTruncation summarizes bounded context loss.
type ReviewTruncationMask ¶
type ReviewTruncationMask uint16
ReviewTruncationMask identifies which context limits were exercised.
const ( ReviewTruncationUserEntry ReviewTruncationMask = 1 << iota ReviewTruncationAssistantEntry ReviewTruncationToolEntry ReviewTruncationBlock ReviewTruncationEntryCount ReviewTruncationTotalBytes ReviewTruncationEstimatedTokens ReviewTruncationActiveAction )
type ReviewValidationError ¶
type ReviewValidationError struct {
Field ReviewValidationField
Reason ReviewValidationReason
}
ReviewValidationError reports a bounded review validation failure. It deliberately carries no rejected value so untrusted classifier output cannot leak through logs or audit messages.
func (*ReviewValidationError) Error ¶
func (e *ReviewValidationError) Error() string
type ReviewValidationField ¶
type ReviewValidationField string
ReviewValidationField identifies the bounded part of a review value that failed validation.
const ( ReviewValidationFieldContext ReviewValidationField = "context" ReviewValidationFieldContextEntry ReviewValidationField = "context_entry" ReviewValidationFieldContextPolicy ReviewValidationField = "context_policy" )
const ( ReviewValidationFieldBasis ReviewValidationField = "basis" ReviewValidationFieldDigest ReviewValidationField = "digest" ReviewValidationFieldRequest ReviewValidationField = "request" ReviewValidationFieldWire ReviewValidationField = "wire" )
const (
ReviewValidationFieldCategories ReviewValidationField = "categories"
)
type ReviewValidationReason ¶
type ReviewValidationReason string
ReviewValidationReason classifies a review validation failure.
const ( ReviewValidationUnsupported ReviewValidationReason = "unsupported" ReviewValidationDuplicate ReviewValidationReason = "duplicate" ReviewValidationTooMany ReviewValidationReason = "too_many" )
const ( ReviewValidationRequired ReviewValidationReason = "required" ReviewValidationInvalid ReviewValidationReason = "invalid" ReviewValidationOutOfBounds ReviewValidationReason = "out_of_bounds" ReviewValidationReserved ReviewValidationReason = "reserved" )
const ReviewValidationMismatch ReviewValidationReason = "mismatch"
type Route ¶
type Route struct {
GateID ID `json:"gate_id,omitzero"`
LoopID ID `json:"loop_id,omitzero"`
ToolExecutionID ID `json:"tool_execution_id,omitzero"`
}
Route carries the identifiers needed to deliver a response to a gate.
type RuleMatcher ¶
type RuleMatcher interface {
MatchesDeny(context.Context, tool.Requirement) (bool, error)
MatchesAllow(context.Context, tool.Requirement) (bool, error)
}
RuleMatcher checks independently stored deny and allow rules for one normalized requirement. Evaluator always checks every deny before consulting any allow.
type RuleWriter ¶
type RuleWriter interface {
WriteRules(context.Context, []tool.RuleCandidate) error
}
RuleWriter atomically persists a complete batch of displayed reusable allow candidates. Returning an error means none of the candidates were persisted.
type Subject ¶
type Subject struct {
ToolExecutionID ID `json:"tool_execution_id,omitzero"`
ToolUseID string `json:"tool_use_id,omitempty"`
TurnID ID `json:"turn_id,omitzero"`
StepID ID `json:"step_id,omitzero"`
InputID ID `json:"input_id,omitzero"`
}
Subject identifies the work item a gate is about.
type UnknownPayloadKindError ¶
type UnknownPayloadKindError struct {
Kind string
}
UnknownPayloadKindError is returned when a payload wrapper names no known kind.
func (*UnknownPayloadKindError) Error ¶
func (e *UnknownPayloadKindError) Error() string
type UnknownResponseAuditKindError ¶
type UnknownResponseAuditKindError struct {
Kind string
}
UnknownResponseAuditKindError is returned when an audit wrapper names no known kind.
func (*UnknownResponseAuditKindError) Error ¶
func (e *UnknownResponseAuditKindError) Error() string