Documentation
¶
Overview ¶
Package aic-verifier provides a drop-in Go SDK for HTTP services that need to enforce AIC (Authorization Identity Certificate) authorization, derived from varwof/gateway-core.
It exposes two integration styles with a shared admission pipeline:
- Middleware: wrap any http.Handler with aicverifier.AuthMiddleware, and the SDK authenticates every request (mTLS client certificate or a Bearer AIC-JWT) and runs the full AIC decision chain before your handler runs.
- Reverse proxy: aicverifier.Server listens on one address and forwards verified requests to a real backend API, injecting the verified client identity to the backend via X-AIC-* headers.
Both styles run the identical pipeline (CRL -> OCSP -> RBAC -> AIC -> constraints -> plugins) so a request admitted by one is admitted by the other.
Package gw provides the shared security engine for varwof gateways.
Capability plugin types (CapabilityPlugin, PluginContext, PluginResult, PluginDecision, HTTPFacts, PluginRegistry) are defined in the types module and re-exported here for backward compatibility.
Package gw provides the shared gateway core library for the varwof project.
Index ¶
- Constants
- Variables
- func AICFingerprint(cert *x509.Certificate) string
- func ArchiveAuditFile(path string) error
- func AuditDuration(start time.Time, entry *AuditEntry)
- func AuthorizeCapabilities(caps []Capability, opID string, params map[string]any) (semantics.Decision, error)
- func AuthorizeCapabilitiesWithConstraints(caps, constraints []Capability, opID string, params map[string]any) (semantics.Decision, error)
- func AuthorizeGrants(grants []semantics.Grant, opID string, params map[string]any) (semantics.Decision, error)
- func AuthorizeOperation(aic *AIC, pa *PrincipalAuthorization, opID string, params map[string]any) (semantics.Decision, error)
- func BaseTLSConfig(cipherSuites []string, minTLSVersion string) *tls.Config
- func BuildCipherSuites(names []string) []uint16
- func BuildSourceChain(clientCert *x509.Certificate, aic *AIC, userCert *x509.Certificate) (*semantics.SourceChain, error)
- func CanonicalJSON(v any) ([]byte, error)
- func CheckAuthorizationConstraints(constraints []Capability, clientIP string) error
- func CheckAuthorizationConstraintsAt(constraints []Capability, clientIP, timeHHMM string) error
- func CheckDAFreshness(ts time.Time, now time.Time, maxAge time.Duration) error
- func CheckDelegatedAgentCert(cert *x509.Certificate) string
- func CheckDelegatedAgentHeaders(cert *x509.Certificate, r *http.Request) stringdeprecated
- func CheckRole(roles []string, allowed []string) bool
- func ClientTLSConfig(caCertFile, certFile, keyFile string, cipherSuites []string, ...) (*tls.Config, error)
- func ConnectionConstraintEvaluator(clientIP string) func(op Operation, unresolved []string) bool
- func ConstraintRecheckLoop(aicConstraints, paConstraints []Capability, clientIP string, ...)
- func ConstraintStrings(constraints []Capability) []string
- func DAHash(cert *x509.Certificate) string
- func DAHashFromAIC(aic *AIC) string
- func DecodeBase64(s string) ([]byte, error)
- func DelegatedAgentServerIdentity(cert *x509.Certificate, principal string) (user string, expiry time.Time, reason string)
- func EffectiveDelegationCapabilities(chain []*x509.Certificate, principalCaps []pki.Capability, maxChainLen int) ([]pki.Capability, error)
- func EffectiveDelegationCapabilitiesFromAIC(chain []*x509.Certificate, topPrincipal *x509.Certificate, maxChainLen int) ([]pki.Capability, error)
- func EncodeBase64(data []byte) string
- func ExtractOCSPURL(cert *x509.Certificate) string
- func ExtractPolicyRoles(cert *x509.Certificate) []string
- func ExtractRoles(cert *x509.Certificate) []string
- func ExtractSPIFFEIDFromCert(cert *x509.Certificate) string
- func FetchOCSPResponseRaw(cert, issuer *x509.Certificate, ocspURL string) ([]byte, error)
- func FilterAuditFile(file string, since time.Time, action string, cn, serial, mapping string) error
- func FindStartOffsetByTime(file string, target time.Time) (int64, error)
- func HasAIC(cert *x509.Certificate) bool
- func HasDelegatedAgentOU(cert *x509.Certificate) bool
- func HashLeaf(data []byte) []byte
- func HashNode(left, right []byte) []byte
- func IsAdminOU(ou string) bool
- func LoadCA(caCertFile string) (*x509.CertPool, error)
- func LoadCACert(caCertFile string) (*x509.Certificate, error)
- func LoadCAFromFile(path string) (*x509.CertPool, error)
- func LoadCert(certFile, keyFile string) (*tls.Certificate, error)
- func LoadEvidenceRecord(path string) (*semantics.DecisionRecord, error)
- func LoadGatewayPolicy(policyPath, sigSuffix string, opts *PolicyVerifyOptions, require bool) error
- func LogAdmission(result AdmissionResult, clientIP string, logger *slog.Logger)
- func LogPluginDecision(logger *AuditLogger, entry PluginAuditEntry)
- func MTLSServerConfig(caCertFile string, cert *tls.Certificate, cipherSuites []string, ...) (*tls.Config, error)
- func MarshalTSARequest(req TimeStampReq) ([]byte, error)
- func MaskCertSerial(serial string) string
- func MaskEmail(email string) string
- func MaskFilePath(path string) string
- func MaskString(s string, visible int) string
- func MaskToken(token string) string
- func MatchCapability(id, pattern string) bool
- func MatchCapabilityPriority(id, pattern string) int
- func MatchCapabilityRules(id string, rules []pki.CapabilityRule) pki.CapabilityRuleMatch
- func NeedRevoke(cert *x509.Certificate) bool
- func NewAdmissionEnvelope(rec AdmissionRecord) (semantics.Envelope, error)
- func NewOutcomeEnvelope(rec OutcomeRecord) (semantics.Envelope, error)
- func NewReplayNonceStore(ttl time.Duration, max int) *memReplayStore
- func NormalizeSerial(serial *big.Int) string
- func OfflineLifetimeFor(ocspFallback string) time.Duration
- func ParseCertPEM(data []byte) (*x509.Certificate, error)
- func ParseCertPEMFile(path string) (*x509.Certificate, error)
- func ParsePrivateKeyPEM(data []byte) (crypto.Signer, error)
- func ParsePrivateKeyPEMFile(path string) (crypto.Signer, error)
- func PeerCertRoles(r *http.Request) []string
- func ProfileSubjectDigest(env semantics.Envelope) (semantics.Digest, bool, error)
- func RecorderSubjectDigest(env semantics.Envelope) (semantics.Digest, bool, error)
- func RegisterConstraint(ev ConstraintEvaluator) error
- func RegisterGeoResolver(name string, fn GeoResolver)
- func RegisterParameterValidator(v ParameterValidator) error
- func RegisterPlugin(p CapabilityPlugin) error
- func ReplaceConstraint(ev ConstraintEvaluator) error
- func RequireRoles(r *http.Request, allowedRoles []string) bool
- func ResetConstraints()
- func ResetParameterValidators()
- func ResetPlugins()
- func SanitizeString(s string) string
- func ServerTLSConfig(cert *tls.Certificate, cipherSuites []string, minTLSVersion string) *tls.Config
- func SetAuthorizationPolicy(p *AuthorizationPolicy)
- func SetGlobalCapabilityRegistry(cr CapabilityRegistry)
- func SetIdentityHeaderMode(r *http.Request, mode IdentityHeaderMode)
- func SignPolicy(policyData []byte, cert *x509.Certificate, signer crypto.Signer) ([]byte, error)
- func SignerHasAdminOU(cert *x509.Certificate) bool
- func StartOCSPStapling(tlsCert *tls.Certificate, cfg *tls.Config, caCertFile string, ...)
- func SubjectDigestByName(env semantics.Envelope, name string) (semantics.Digest, bool, error)
- func SynthesizeCertFromJWT(outer *aicjwt.OuterClaims) (*x509.Certificate, error)
- func TLSVersionFromString(s string) uint16
- func ToGrant(c Capability) (semantics.Grant, error)
- func ToGrantSet(caps []Capability) ([]semantics.Grant, error)
- func ValidateAIC(aic *AIC) error
- func VerifyAuditEntry(data []byte, tsaClient *TSAClient) error
- func VerifyBundle(bundle *CredentialBundle, roots *x509.CertPool) error
- func VerifyDelegationAuth(aic *AIC, userCert *x509.Certificate) error
- func VerifyDelegationChain(chain []*x509.Certificate, topPrincipal *x509.Certificate, maxDepth int) error
- func VerifyDelegationChainWithCaps(chain []*x509.Certificate, topPrincipal *x509.Certificate, ...) ([]pki.Capability, error)
- func VerifyFnFromPublicKey(pub *ecdsa.PublicKey) func(keyID string, pae, sig []byte) error
- func VerifyLayer2(chain []*x509.Certificate, cfg *PipelineConfig, roles []string) (AdmissionResult, *Layer2Result)
- func VerifyPrincipalKeyHash(agent, principal *x509.Certificate) error
- func VerifyProof(leaf []byte, proof []ProofStep, root []byte) bool
- func VerifyProofBounded(leaf []byte, proof []ProofStep, root []byte, maxProofLen int) bool
- func VerifySPIFFESAN(cert *x509.Certificate, expectedID string) bool
- func VerifySignedPolicy(sigDER, policyData []byte, roots *x509.CertPool, requireAdminOU bool) (*x509.Certificate, error)
- type AIC
- type AdmissionConfig
- type AdmissionFact
- type AdmissionIdentity
- type AdmissionRecord
- type AdmissionResult
- type AlgorithmIdentifier
- type ApprovalRequester
- type AuditAction
- type AuditChain
- func (c *AuditChain) BatchCount() int
- func (c *AuditChain) Dump() string
- func (c *AuditChain) GetTree(batchNumber int) *SealedTree
- func (c *AuditChain) LatestRoot() string
- func (c *AuditChain) LatestRootBytes() []byte
- func (c *AuditChain) Seal(entries [][]byte, previousRoot string) *SealedTree
- func (c *AuditChain) SealChecked(entries [][]byte, previousRoot string) (*SealedTree, error)
- func (c *AuditChain) Verify(batchNumber int, leaf []byte, proof []ProofStep) (bool, error)
- func (c *AuditChain) VerifyContinuity() error
- func (c *AuditChain) VerifyJSON(req *VerifyRequest) *VerifyResponse
- type AuditEntry
- type AuditFilter
- type AuditLogger
- type AuditVerifier
- type AuthContext
- type AuthError
- type AuthMode
- type AuthorizationPolicy
- func (p *AuthorizationPolicy) HasGrant(role, capability string) bool
- func (p *AuthorizationPolicy) HasParamDefault(scheme, capID, param string) (any, bool)
- func (p *AuthorizationPolicy) IntersectGrants(roles []string, aicCapIds []string) []string
- func (p *AuthorizationPolicy) ParamDefaults(scheme, capID string) map[string]any
- func (p *AuthorizationPolicy) RoleByOU(ou string) string
- func (p *AuthorizationPolicy) RoleGrants(role string) []string
- type CRLCache
- func (c *CRLCache) ForceRefresh() error
- func (c *CRLCache) IsRevoked(caDN string, serial *big.Int) (bool, error)
- func (c *CRLCache) IsRevokedCert(cert *x509.Certificate) (bool, error)
- func (c *CRLCache) LastRefresh() time.Time
- func (c *CRLCache) Start(stop <-chan struct{})
- func (c *CRLCache) Stats() (int, time.Time, time.Time)
- type CRLRevokedFunc
- type Capability
- type CapabilityPlugin
- type CapabilityRegistry
- type ChallengeCarrier
- type ChallengeConfig
- type Config
- type ConstraintContext
- type ConstraintEvaluator
- type ConstraintRegistry
- func (r *ConstraintRegistry) Find(capabilityId string) (ConstraintEvaluator, error)
- func (r *ConstraintRegistry) Keys() []string
- func (r *ConstraintRegistry) Len() int
- func (r *ConstraintRegistry) Register(ev ConstraintEvaluator) error
- func (r *ConstraintRegistry) Remove(capabilityId string)
- func (r *ConstraintRegistry) Replace(ev ConstraintEvaluator) error
- func (r *ConstraintRegistry) Reset()
- type ContentInfo
- type CredentialBundle
- type DecisionResult
- type DelegationAuthTBS
- type DelegationAuthorization
- type DelegationChainVerifier
- type DelegationMode
- type DelegationPolicy
- type ErrorCode
- type EvidenceAuditChain
- type EvidenceAuditEntry
- type EvidenceAuthorization
- type EvidenceBundle
- type EvidenceCapability
- type EvidenceConfig
- type EvidenceContainer
- type EvidenceContext
- type EvidenceDecision
- type EvidenceExporter
- type EvidenceFailure
- type EvidenceGrantRef
- type EvidenceKind
- type EvidenceManifest
- type EvidenceOperation
- type EvidenceOutcome
- type EvidenceProfile
- type EvidenceQuery
- type EvidenceSink
- type EvidenceSubject
- type EvidenceSupervisionEvent
- type EvidenceVerifyReport
- type ExtField
- type ExternalPolicyRef
- type FileEvidenceExporter
- type FileSink
- func (s *FileSink) Emit(ctx EvidenceContext, rec semantics.DecisionRecord, env semantics.Envelope) (RecordRef, error)
- func (s *FileSink) EmitAdmission(ctx EvidenceContext, rec AdmissionRecord, env semantics.Envelope) (RecordRef, error)
- func (s *FileSink) EmitOutcome(ctx EvidenceContext, rec OutcomeRecord, env semantics.Envelope) (RecordRef, error)
- type GapCounter
- type GeoResolver
- type HTTPFacts
- type Hooks
- type IdentityHeaderMode
- type JWTVerifier
- type JWTVerifyOptions
- type Layer1Result
- type Layer2Result
- type Layer3Result
- type MerkleTree
- type MessageImprint
- type NonceCache
- type OCSPCache
- type OfflineRBAC
- type Operation
- type OperationDecision
- type OutcomeRecord
- type OutcomeSink
- type OverrideRecorder
- type PKIStatusInfo
- type ParameterValidator
- type ParameterValidatorRegistry
- func (r *ParameterValidatorRegistry) Find(schemeID string) (ParameterValidator, error)
- func (r *ParameterValidatorRegistry) Keys() []string
- func (r *ParameterValidatorRegistry) Len() int
- func (r *ParameterValidatorRegistry) Register(v ParameterValidator) error
- func (r *ParameterValidatorRegistry) Reset()
- func (r *ParameterValidatorRegistry) ValidateCapability(granted, declared Capability) error
- type PermissionDef
- type PermissionLevel
- type PipelineCheck
- type PipelineConfig
- type PipelineResult
- type PluginAuditEntry
- type PluginContext
- type PluginDecision
- type PluginRegistry
- type PluginResult
- type PolicyNamespace
- type PolicyRole
- type PolicyServer
- type PolicySigningConfig
- type PolicySigningIdentity
- type PolicyVerifyOptions
- type PrincipalAuthorization
- type PrincipalUid
- type ProblemDetails
- type ProofStep
- type ProofStepJSON
- type Reason
- type RecordRef
- type RecorderDescriptor
- type ResourceScope
- type RiskAssessment
- type RiskMonitor
- type RiskMonitorConfig
- type RiskRule
- type RiskViolation
- type RoleDef
- type RotatingFile
- type Route
- type SPIFFEID
- type SealedTree
- type SemanticVerdict
- type Server
- type ServerOptions
- type SignedAuditEntry
- type SignedSupervisionEvent
- type SlogSink
- func (s SlogSink) Emit(ctx EvidenceContext, rec semantics.DecisionRecord, env semantics.Envelope) (RecordRef, error)
- func (s SlogSink) EmitAdmission(ctx EvidenceContext, rec AdmissionRecord, env semantics.Envelope) (RecordRef, error)
- func (s SlogSink) EmitOutcome(ctx EvidenceContext, rec OutcomeRecord, env semantics.Envelope) (RecordRef, error)
- type Summary
- type SupervisionPolicy
- type SupervisionQuery
- type SupervisionResult
- type SupervisionStore
- type TSAClient
- type TSTInfo
- type TimeRange
- type TimeStampReq
- type TimeStampResp
- type Translator
- type TrustLayer
- type UserPermission
- type VerifyRequest
- type VerifyResponse
Constants ¶
const ( OCSPFallbackAllow = "allow" OCSPFallbackDeny = "deny" OCSPFallbackCRL = "crl" )
OCSPFallbackAllow/Deny/CRL are OCSP fallback policy constants.
const ( // OutcomeExecuted means the executor established that the effect occurred. OutcomeExecuted = "executed" // OutcomeFailed means it established the effect did not occur. OutcomeFailed = "failed" // OutcomeIndeterminate means it could not establish either. OutcomeIndeterminate = "indeterminate" // OutcomeObserved means a response was observed but the deployment has not // classified the effect (the honest default for a proxy that only saw HTTP). OutcomeObserved = "observed" )
Recommended outcome tokens. The field is a plain string: the vocabulary belongs to the execution boundary (AEB), not to this SDK.
const ( RoleAdmin = "gateway:admin" RoleOps = "gateway:ops" RoleAudit = "gateway:audit" RoleDeploy = "gateway:deploy" RoleRead = "gateway:read" RoleWild = "gateway:*" )
RoleAdmin/Ops/Audit/Deploy/Read/Wild are role constants.
const ( SupervisionDecisionApproved = pki.SupervisionDecisionApproved SupervisionDecisionDenied = pki.SupervisionDecisionDenied SupervisionDecisionPending = pki.SupervisionDecisionPending )
Supervision decisions referenced by SupervisionResult.Decision (aliases of the shared pki constants).
const AdmissionRecordPredicateType = "https://varwof.com/aic/v1/admission-record"
AdmissionRecordPredicateType identifies pipeline-level (pre-language) records.
const AdmissionRecordVersion = "AIC-ADMISSION-RECORD-v1"
AdmissionRecordVersion is the record revision.
const CLCRevision = semantics.CLCRevision
CLCRevision is the CLC language revision this SDK decides with.
const ConstraintAuditRequiredKey = "op:audit:required"
ConstraintAuditRequiredKey is the capabilityId for audit-required operation constraint.
const ConstraintCIDRKey = "network:cidr"
ConstraintCIDRKey is the capabilityId for IP network range constraint.
const ConstraintConcurrentKey = "session:max-concurrent"
ConstraintConcurrentKey is the capabilityId for max concurrent connections constraint.
const ConstraintGeoFenceKey = "geo-fence"
ConstraintGeoFenceKey is the capabilityId for geo fence constraint.
const ConstraintHardTimeoutKey = "session:hard-timeout"
ConstraintHardTimeoutKey is the capabilityId for session hard timeout constraint.
const ConstraintIdleTimeoutKey = "session:idle-timeout"
ConstraintIdleTimeoutKey is the capabilityId for session idle timeout constraint.
const ConstraintReadOnlyKey = "op:readonly"
ConstraintReadOnlyKey is the capabilityId for read-only operation constraint.
const ConstraintTimeWindowKey = "time:window"
ConstraintTimeWindowKey is the capabilityId for time window constraint.
const DefaultDAAgeMax = 30 * time.Second
DefaultDAAgeMax is the default value for the DelegationAuthorization.timestamp freshness window (the delegation-authorization validation flow: |now - timestamp| ≤ 30s).
const DefaultMaskRune = '*'
DefaultMaskRune is the character used to replace sensitive content.
const DefaultMaxBodyBytes = 1 << 20 // 1 MiB
DefaultMaxBodyBytes is the maximum request body the pipeline reads for capability plugin evaluation (bounded copy; the full body is restored before forwarding or calling the next handler).
const DefaultMaxChainLength = 8
DefaultMaxChainLength is the default upper bound for maximum delegation chain length (anti-certificate-bomb). Chains exceeding this are rejected (specification maxDepth is set by the top Principal; this serves as the gateway-side hard limit). Real-world Agent delegation depths are typically 2–3 levels; default 8 provides ample margin.
const HardTimeoutMax = 86400
HardTimeoutMax is the maximum value for session:hard-timeout (seconds).
const HardTimeoutMin = 60
HardTimeoutMin is the minimum value for session:hard-timeout (seconds).
const IdleTimeoutMax = 3600
IdleTimeoutMax is the maximum value for session:idle-timeout (seconds).
const IdleTimeoutMin = 30
IdleTimeoutMin is the minimum value for session:idle-timeout (seconds).
const MaxConcurrentMax = 1024
MaxConcurrentMax is the maximum value for the max parameter of the max-concurrent constraint.
const MaxConcurrentMin = 1
MaxConcurrentMin is the minimum value for the max parameter of the max-concurrent constraint.
const OfflineLifetimeLimit = time.Hour
OfflineLifetimeLimit is the maximum remaining certificate validity enforced in offline mode (G2(b): ≤1h).
const OutcomeRecordPredicateType = "https://varwof.com/aic/v1/outcome-record"
OutcomeRecordPredicateType identifies execution-boundary records.
const OutcomeRecordVersion = "AIC-OUTCOME-RECORD-v1"
OutcomeRecordVersion is the record revision.
const ProblemContentType = "application/problem+json"
ProblemContentType is the RFC 9457 media type.
const ProblemTypeEvidenceRequired = "https://varwof.com/clc/v1/problems/evidence-required"
ProblemTypeEvidenceRequired identifies this profile's problem type.
const RolePrefix = "gateway:"
RolePrefix is the gateway role OU prefix.
Variables ¶
var ( OIDSigECDSAWithSHA256 = pki.OIDSigECDSAWithSHA256 OIDSigECDSAWithSHA384 = pki.OIDSigECDSAWithSHA384 OIDSigECDSAWithSHA512 = pki.OIDSigECDSAWithSHA512 OIDSigRSAWithSHA256 = pki.OIDSigRSAWithSHA256 OIDSigRSAWithSHA384 = pki.OIDSigRSAWithSHA384 OIDSigRSAWithSHA512 = pki.OIDSigRSAWithSHA512 OIDSigRSAPSSWithSHA256 = pki.OIDSigRSAPSSWithSHA256 OIDSigEd25519 = pki.OIDSigEd25519 OIDSHA256 = pki.OIDSHA256 OIDSHA384 = pki.OIDSHA384 OIDSHA512 = pki.OIDSHA512 )
OID re-exports — signature algorithm OIDs.
var ( AIAOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 1} OCSPOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1} )
AIAOID is the Authority Information Access method OID. OCSPOID is the OCSP responder OID.
var ErrRecordSignerMismatch = errors.New("aic-verifier: record signature does not verify against the pinned key")
ErrRecordSignerMismatch is returned when a record's signature does not verify against the key a deployment pinned via VerifyFnFromPublicKey.
var SecureCipherSuites = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384, tls.TLS_CHACHA20_POLY1305_SHA256, }
SecureCipherSuites is the list of secure cipher suites (GCM/CHACHA).
var Version = "0.1.0"
Version is the package version, set via -ldflags -X github.com/varwof/gateway-core.Version=x.y.z.
Functions ¶
func AICFingerprint ¶
func AICFingerprint(cert *x509.Certificate) string
AICFingerprint computes the SHA-256 hex fingerprint of the AIC extension DER encoding in the certificate. Returns empty string if the certificate has no AIC extension (audit field uses omitempty, keeping old entries readable).
func ArchiveAuditFile ¶
ArchiveAuditFile archives (compress-rotates) the audit log file.
func AuditDuration ¶
func AuditDuration(start time.Time, entry *AuditEntry)
AuditDuration computes and sets the duration of an audit entry.
func AuthorizeCapabilities ¶
func AuthorizeCapabilities(caps []Capability, opID string, params map[string]any) (semantics.Decision, error)
AuthorizeCapabilities decides a concrete operation against an explicit capability list.
func AuthorizeCapabilitiesWithConstraints ¶
func AuthorizeCapabilitiesWithConstraints(caps, constraints []Capability, opID string, params map[string]any) (semantics.Decision, error)
AuthorizeCapabilitiesWithConstraints decides an operation against a capability list whose grants also carry the connection's authorization constraints.
Constraints are what make the three-valued verdict interesting: the CLC core evaluates a few of them itself (max_rows), recognises the rest (time:window, network:cidr) and reports them as residual obligations rather than silently dropping them — so a caller that reads only "allow" fails closed.
func AuthorizeGrants ¶
func AuthorizeGrants(grants []semantics.Grant, opID string, params map[string]any) (semantics.Decision, error)
AuthorizeGrants decides opID/params against an explicit CLC-v1 grant set.
func AuthorizeOperation ¶
func AuthorizeOperation(aic *AIC, pa *PrincipalAuthorization, opID string, params map[string]any) (semantics.Decision, error)
AuthorizeOperation decides a concrete operation for an admitted connection.
The effective authority is the AIC capability set intersected with the PrincipalAuthorization grants when a PA is present (the P∩C model): the operation has to be authorized by both, so each set is decided and the results are combined — deny wins, and residual obligations union.
func BaseTLSConfig ¶
BaseTLSConfig creates a base TLS configuration.
func BuildCipherSuites ¶
BuildCipherSuites builds a cipher suite list from name strings.
func BuildSourceChain ¶ added in v0.2.0
func BuildSourceChain(clientCert *x509.Certificate, aic *AIC, userCert *x509.Certificate) (*semantics.SourceChain, error)
BuildSourceChain returns the authorization sources this admission can prove it rested on: the AIC (or plain leaf) certificate it verified, plus the principal certificate when the deployment configured one.
func CanonicalJSON ¶
CanonicalJSON serializes v with keys sorted lexicographically at every level (map-key order guaranteed by encoding/json) and no extraneous whitespace.
func CheckAuthorizationConstraints ¶
func CheckAuthorizationConstraints(constraints []Capability, clientIP string) error
CheckAuthorizationConstraints performs offline validation of authorizationConstraints. Supports: network:cidr (requires ClientIP), time:window, geo-fence (requires ClientIP), session:max-concurrent, session:hard-timeout, session:idle-timeout, op:readonly, op:audit:required. Unknown constraint types are ignored by default (forward compatible); the caller logs audit warnings; after registering a custom constraint executor (RegisterConstraint), it will be recognized and executed.
func CheckAuthorizationConstraintsAt ¶
func CheckAuthorizationConstraintsAt(constraints []Capability, clientIP, timeHHMM string) error
CheckAuthorizationConstraintsAt is the same as CheckAuthorizationConstraints, but evaluates time-window constraints at a specified UTC time (HH:MM), useful for testing and offline decision demonstrations. When timeHHMM is empty, uses the current time. The tz field in time-window is converted to the corresponding timezone during evaluation.
func CheckDAFreshness ¶
CheckDAFreshness validates that DelegationAuthorization.timestamp is within the freshness window. When now is nil, uses time.Now(); when maxAge <= 0, uses DefaultDAAgeMax. Zero-value timestamp (never set) is treated as expired.
func CheckDelegatedAgentCert ¶
func CheckDelegatedAgentCert(cert *x509.Certificate) string
CheckDelegatedAgentCert validates the legitimacy of a Delegated-Agent certificate (for non-HTTP protocols like TCP). Returns empty string on success, non-empty rejection reason on failure.
A certificate that merely carries the "Delegated-Agent" OU is not legitimate on its own (finding 17): the OU is a plaintext subject attribute anyone can mint, so a Delegated-Agent cert must additionally be core-signed (carry a valid AIC extension) and be within its validity window. Certificates without the OU are not delegated-agent certs and pass through.
func CheckDelegatedAgentHeaders
deprecated
func CheckDelegatedAgentHeaders(cert *x509.Certificate, r *http.Request) string
Deprecated: CheckDelegatedAgentHeaders validates the X-Agent-User/X-Agent-TTL headers of a Delegated-Agent certificate (B1 username delegation path). The username cannot be cryptographically bound to the certificate; identity propagation has been changed to B2 (X-Client-Cert-DER certificate passthrough). This function is kept for legacy client compatibility only. Certificates without Delegated-Agent OU pass through directly. Returns empty string on success, non-empty rejection reason on failure.
Security note (G4): The client's X-Agent-User / X-Agent-TTL headers are entirely controlled by the requestor and must never be trusted as the true identity of the proxied user. This function only performs "declarative" validation (prompting ops to configure the delegation channel). The real delegation identity is derived by DelegatedAgentServerIdentity from the core-signed AIC/GatewaySession, and the gateway overwrites X-Agent-User / X-Agent-TTL headers before forwarding.
func ClientTLSConfig ¶
func ClientTLSConfig(caCertFile, certFile, keyFile string, cipherSuites []string, minTLSVersion string) (*tls.Config, error)
ClientTLSConfig creates an mTLS client-side configuration.
func ConnectionConstraintEvaluator ¶ added in v0.2.0
ConnectionConstraintEvaluator returns an UnresolvedEvaluator that discharges the obligations the connection-level constraint registry can evaluate for the given client IP (source CIDRs, time windows, and whatever else is registered).
It exists because the connection-level check and the language-level obligation are two halves of one rule: the language *declares* the constraint, and this evaluator *discharges* it. Passing it is an explicit act — a deployment that does not pass it keeps the §8.4 default, which is fail-closed — so the release is a declared policy rather than an implicit bypass. Anything the registry cannot evaluate (for example `max_rows`) is not discharged here: return false and let the caller decide.
func ConstraintRecheckLoop ¶
func ConstraintRecheckLoop(aicConstraints, paConstraints []Capability, clientIP string, interval time.Duration, done <-chan struct{}, onViolation func(reason string))
ConstraintRecheckLoop periodically re-evaluates authorizationConstraints (G3: constraint timing consistency).
Constraints on long-lived data plane connections like TCP are only checked once at handshake; time-window / revocation constraints that expire over time cease to be effective after crossing the window — for example, a "weekdays 9-18 only" connection established during the night window remains active during the day. This function re-evaluates the authorizationConstraints of both AIC and PrincipalAuthorization at the given interval (using the current time), calling onViolation when a constraint is no longer met (gateway disconnects and audits accordingly). Stops when done is closed (idempotent).
Any single constraint evaluation failure is treated as a violation; a and pa may both be nil/empty (skipping the corresponding set). Callers should ensure onViolation is non-nil (nil internally only logs, no actual action).
func ConstraintStrings ¶
func ConstraintStrings(constraints []Capability) []string
ConstraintStrings renders authorization constraints in CLC form: <scheme>:<type>[:<params-json>], e.g. varwof/constraint-v1:network:cidr:["192.0.2.0/24"].
func DAHash ¶
func DAHash(cert *x509.Certificate) string
DAHash computes the SHA-256 hex hash of the DelegationAuthorization signatureValue in the AIC (authorization evidence fingerprint). Returns empty string if AIC is missing or has no DA signature.
func DAHashFromAIC ¶
DAHashFromAIC computes the SHA-256 hash of the DelegationAuthorization signatureValue for a parsed AIC. Returns empty string if no DA signature is present.
func DecodeBase64 ¶
DecodeBase64 performs Base64 decoding.
func DelegatedAgentServerIdentity ¶
func DelegatedAgentServerIdentity(cert *x509.Certificate, principal string) (user string, expiry time.Time, reason string)
DelegatedAgentServerIdentity derives the server-asserted delegation identity from the core-signed certificate extension (AIC), preventing G4 identity spoofing: never trust client-supplied plaintext headers. Returns:
- user: the server-asserted proxied subject (from AIC.PrincipalUid or cert CN/OU fallback)
- expiry: delegation validity deadline (zero time.Time{} when no hard-timeout constraint)
- reason: non-empty rejection reason (illegal conditions other than missing Delegated-Agent OU)
func EffectiveDelegationCapabilities ¶
func EffectiveDelegationCapabilities(chain []*x509.Certificate, principalCaps []pki.Capability, maxChainLen int) ([]pki.Capability, error)
EffectiveDelegationCapabilities validates per-level capability subsets and computes the intersection along the delegation chain.
chain goes top-down: chain[0]=topmost delegated Agent, chain[len-1]=bottom Agent. principalCaps is the effective capabilities P of the original principal (top Principal). Returns C_eff = P ∩ C_1 ∩ … ∩ C_n.
Process: eff = P; for each level C_i, first verify C_i ⊆ eff (permissions only decrease, escalation is rejected), then eff = filterCovered(C_i, eff) (retain this level's capabilities that are authorized by eff).
Note: This function only performs capability semantic validation; signature verification is handled separately by Verify.
func EffectiveDelegationCapabilitiesFromAIC ¶
func EffectiveDelegationCapabilitiesFromAIC(chain []*x509.Certificate, topPrincipal *x509.Certificate, maxChainLen int) ([]pki.Capability, error)
EffectiveDelegationCapabilitiesFromAIC is a convenience entry point: extracts AIC.capabilities from the top Principal certificate as P, then recursively computes the intersection. Chain signature verification is performed separately by the caller.
func ExtractOCSPURL ¶
func ExtractOCSPURL(cert *x509.Certificate) string
ExtractOCSPURL extracts the OCSP URL from the certificate's AIA extension.
func ExtractPolicyRoles ¶
func ExtractPolicyRoles(cert *x509.Certificate) []string
ExtractPolicyRoles extracts policy roles from the certificate OU. Uses the global authorization policy's (if set) OU→role mapping to resolve role names; when no policy is set, falls back to the hardcoded ExtractRoles (only identifies gateway: prefix OUs). Returned role names include both policy role names and original gateway:* OUs (if present), for compatibility with both configuration styles.
func ExtractRoles ¶
func ExtractRoles(cert *x509.Certificate) []string
ExtractRoles extracts the gateway role list from the certificate OU.
func ExtractSPIFFEIDFromCert ¶
func ExtractSPIFFEIDFromCert(cert *x509.Certificate) string
ExtractSPIFFEIDFromCert extracts a SPIFFE ID from a certificate's SAN URIs. Returns "" if no SPIFFE URI is found.
func FetchOCSPResponseRaw ¶
func FetchOCSPResponseRaw(cert, issuer *x509.Certificate, ocspURL string) ([]byte, error)
FetchOCSPResponseRaw fetches the raw OCSP response bytes.
func FilterAuditFile ¶
FilterAuditFile filters the audit file by conditions and prints matching entries.
func FindStartOffsetByTime ¶
FindStartOffsetByTime binary-searches for the file offset corresponding to a given time.
func HasAIC ¶
func HasAIC(cert *x509.Certificate) bool
HasAIC reports whether the certificate carries a valid AIC extension (G2: short-lived certificate identification). Malformed AIC returns false (such certificates are denied in the admission pipeline and will not enter the data plane).
func HasDelegatedAgentOU ¶
func HasDelegatedAgentOU(cert *x509.Certificate) bool
HasDelegatedAgentOU is the exported wrapper of hasDelegatedAgentOU, for gateways to check delegation identity before forwarding.
func HashLeaf ¶
HashLeaf computes the SHA256 hash of a Merkle tree leaf node. Finding 22: the leaf is domain-separated (prefix 0x00) from internal node hashes so a leaf hash can never be presented as an internal node hash or vice versa.
func HashNode ¶
HashNode computes the SHA256 hash of a Merkle tree internal node. Finding 22: internal nodes use a distinct domain prefix (0x01) so lone-leaf roots (HashLeaf, 0x00) cannot collide with node hashes.
func IsAdminOU ¶
IsAdminOU checks whether the OU is an admin role (compatible with gateway:admin and bare admin).
func LoadCA ¶
LoadCA loads a CA certificate pool and validates that every certificate in the bundle is a CA.
func LoadCACert ¶
func LoadCACert(caCertFile string) (*x509.Certificate, error)
LoadCACert loads and parses a single CA certificate (first PEM block).
func LoadCAFromFile ¶
LoadCAFromFile loads a PEM CA chain into a CertPool.
func LoadCert ¶
func LoadCert(certFile, keyFile string) (*tls.Certificate, error)
LoadCert loads a TLS certificate key pair.
func LoadEvidenceRecord ¶ added in v0.2.0
func LoadEvidenceRecord(path string) (*semantics.DecisionRecord, error)
LoadEvidenceRecord reads a DSSE envelope written by FileSink and returns the decision record it carries, checking both the envelope (structure, subject binding) and the record (re-computation) on the way. It is the bridge from the emission side to the reporting side: an EvidenceBundle gets its authority from this record.
func LoadGatewayPolicy ¶
func LoadGatewayPolicy(policyPath, sigSuffix string, opts *PolicyVerifyOptions, require bool) error
LoadGatewayPolicy loads and sets the gateway authorization policy. If authorization_file is configured, loads it (with signature verification); opts=nil skips signature verification. On successful load, sets the global policy via SetAuthorizationPolicy. On failure, if require=true returns an error; otherwise degrades by keeping the existing policy.
func LogAdmission ¶
func LogAdmission(result AdmissionResult, clientIP string, logger *slog.Logger)
LogAdmission records the admission decision log.
func LogPluginDecision ¶
func LogPluginDecision(logger *AuditLogger, entry PluginAuditEntry)
LogPluginDecision writes a plugin decision event to the audit log. Deny and execution errors are logged as WARN, allow as INFO. When entry.Level is empty, it is inferred from Decision ("allow"→INFO, others→WARN).
func MTLSServerConfig ¶
func MTLSServerConfig(caCertFile string, cert *tls.Certificate, cipherSuites []string, minTLSVersion string) (*tls.Config, error)
MTLSServerConfig creates an mTLS server-side configuration.
func MarshalTSARequest ¶
func MarshalTSARequest(req TimeStampReq) ([]byte, error)
MarshalTSARequest serializes a TSA request to DER.
func MaskCertSerial ¶
MaskCertSerial masks a certificate serial number, keeping only the last 4 hex chars. Example: "A1:B2:C3:D4:E5:F6" → "**********E5:F6"
func MaskEmail ¶
MaskEmail masks an email address, keeping domain visible. Example: "alice@example.com" → "a***e@example.com"
func MaskFilePath ¶
MaskFilePath masks the filename portion of a path, keeping the directory. Example: "/etc/pki/certs/secret.pem" → "/etc/pki/certs/********.pem"
func MaskString ¶
MaskString replaces all but the last `visible` characters with the mask rune. If the input is shorter than visible, it returns the input unchanged. If visible is 0, the entire string is masked. Returns empty string unchanged.
func MaskToken ¶
MaskToken masks an API token or key, keeping only first and last 4 chars for long tokens, and proportionally fewer for short tokens so that at least half of the token is always masked (finding 21: a 9-char token must not reveal 8 chars). Example: "sk-abc123def456ghi789" → "sk-a*******i789"
func MatchCapability ¶
MatchCapability checks whether a capability matches a pattern (supports * and a:b:* prefix).
func MatchCapabilityPriority ¶
MatchCapabilityPriority checks whether id matches pattern and returns a five-level priority (pki.MatchPriorityExact .. MatchPriorityGlobal, 0 means no match). Semantics: capabilityId is segmented by ':', '*' matches a single segment, '**' crosses segments, priority: exact(5) > single-segment(4) > multi-segment(3) > scheme(2) > global(1).
func MatchCapabilityRules ¶
func MatchCapabilityRules(id string, rules []pki.CapabilityRule) pki.CapabilityRuleMatch
MatchCapabilityRules makes a decision within a rule set (allow + deny) by priority: takes the highest-priority matching rule; at equal priority, deny takes precedence over allow. Returns Matched=false when no rule matches.
func NeedRevoke ¶
func NeedRevoke(cert *x509.Certificate) bool
NeedRevoke checks whether a certificate needs proactive revocation.
func NewAdmissionEnvelope ¶ added in v0.2.0
func NewAdmissionEnvelope(rec AdmissionRecord) (semantics.Envelope, error)
NewAdmissionEnvelope wraps the record in the same DSSE/in-toto envelope the CLC records use, with a pipeline-level predicate type. A consumer that already speaks the envelope needs no new transport, only this predicate.
The statement's subjects are the facts the refusal rested on (client certificate DER, AIC extension, ...), so the envelope is bound to the same material an in-toto consumer would expect — matched purely by digest.
func NewOutcomeEnvelope ¶ added in v0.2.0
func NewOutcomeEnvelope(rec OutcomeRecord) (semantics.Envelope, error)
NewOutcomeEnvelope wraps the record in the same DSSE/in-toto envelope used by the decision and admission records; only the predicate type differs.
func NewReplayNonceStore ¶
NewReplayNonceStore returns a process-local replay-protection store. Nonces are retained for ttl (default 24h) and at most max entries (default 4096); beyond that the oldest are evicted. The store is intended for single-node gateways; multi-node deployments should share a distributed nonce store instead.
func NormalizeSerial ¶
NormalizeSerial converts a certificate serial number to standard hex format (uppercase, no 0x prefix, zero-padded to 40 characters).
func OfflineLifetimeFor ¶
OfflineLifetimeFor returns the enforced offline limit based on the OCSP fallback policy. OCSPFallbackAllow (fail-open) and OCSPFallbackCRL (crl data can lag a recent revocation) → 1h cap; deny/disabled → 0 (not enforced; deny is fail-closed). Called by the gateway when constructing PipelineConfig.OfflineMaxCertLifetime.
func ParseCertPEM ¶
func ParseCertPEM(data []byte) (*x509.Certificate, error)
ParseCertPEM parses the first certificate from PEM bytes.
func ParseCertPEMFile ¶
func ParseCertPEMFile(path string) (*x509.Certificate, error)
ParseCertPEMFile reads and parses a PEM certificate file.
func ParsePrivateKeyPEM ¶
ParsePrivateKeyPEM parses a PEM private key (PKCS#1/PKCS#8/EC/RSA).
func ParsePrivateKeyPEMFile ¶
ParsePrivateKeyPEMFile reads and parses a PEM private key file.
func PeerCertRoles ¶
PeerCertRoles extracts roles from the first peer certificate in the request's TLS connection. Returns nil if the request has no TLS or no peer certs.
func ProfileSubjectDigest ¶ added in v0.2.0
ProfileSubjectDigest returns the digest a consumer should look for when it wants to know which evidence shape a statement carries.
func RecorderSubjectDigest ¶ added in v0.2.0
RecorderSubjectDigest returns the digest of the recorder descriptor that emitted this envelope, when it carries one.
func RegisterConstraint ¶
func RegisterConstraint(ev ConstraintEvaluator) error
RegisterConstraint registers a constraint evaluator in the global registry (extension point).
func RegisterGeoResolver ¶
func RegisterGeoResolver(name string, fn GeoResolver)
RegisterGeoResolver registers a custom geographic resolver (extension point) for use in the geo-fence resolver mode (e.g. third-party geographic databases like ip2region).
func RegisterParameterValidator ¶
func RegisterParameterValidator(v ParameterValidator) error
RegisterParameterValidator registers a validator in the built-in parameter boundary validator registry.
func RegisterPlugin ¶
func RegisterPlugin(p CapabilityPlugin) error
RegisterPlugin registers a plugin in the global registry.
func ReplaceConstraint ¶
func ReplaceConstraint(ev ConstraintEvaluator) error
ReplaceConstraint replaces an evaluator in the global registry (hot update extension point).
func RequireRoles ¶
RequireRoles checks whether the mTLS peer certificate in the request carries at least one of the given allowedRoles. It is a convenience wrapper around PeerCertRoles + CheckRole for HTTP handler middleware.
func ResetConstraints ¶
func ResetConstraints()
ResetConstraints clears the global registry and re-registers built-in types (for testing only).
func ResetParameterValidators ¶
func ResetParameterValidators()
ResetParameterValidators clears the built-in parameter boundary validator registry (testing only).
func SanitizeString ¶
SanitizeString removes non-printable characters and trims whitespace. Useful for sanitizing user input before logging.
func ServerTLSConfig ¶
func ServerTLSConfig(cert *tls.Certificate, cipherSuites []string, minTLSVersion string) *tls.Config
ServerTLSConfig creates a server-side TLS configuration.
func SetAuthorizationPolicy ¶
func SetAuthorizationPolicy(p *AuthorizationPolicy)
SetAuthorizationPolicy sets the global authorization policy.
func SetGlobalCapabilityRegistry ¶
func SetGlobalCapabilityRegistry(cr CapabilityRegistry)
SetGlobalCapabilityRegistry sets the package-level default capability registry. Passing nil clears it (disables capability registration validation).
func SetIdentityHeaderMode ¶
func SetIdentityHeaderMode(r *http.Request, mode IdentityHeaderMode)
SetIdentityHeaderMode is kept for the per-request (middleware) integration style. Security note: callers must only pass an identity mode derived from server configuration, never from a client-supplied header value; the reverse proxy reads its mode from Config.IdentityMode so clients cannot influence how much identity is disclosed to backends.
func SignPolicy ¶
SignPolicy uses an admin identity to create a PKCS#7 detached signature (SHA-256) for policy data, returning .sig DER. signer must be a crypto.Signer (supports RSA/ECDSA/Ed25519).
func SignerHasAdminOU ¶
func SignerHasAdminOU(cert *x509.Certificate) bool
SignerHasAdminOU checks whether the signer certificate carries the admin OU.
func StartOCSPStapling ¶
func StartOCSPStapling(tlsCert *tls.Certificate, cfg *tls.Config, caCertFile string, stopCh <-chan struct{}, translator Translator, lang string)
StartOCSPStapling starts the OCSP stapling background refresh.
func SubjectDigestByName ¶ added in v0.2.0
SubjectDigestByName reads a named subject digest back out of an envelope, for the CLC statement and for the pipeline/outcome statements alike.
func SynthesizeCertFromJWT ¶
func SynthesizeCertFromJWT(outer *aicjwt.OuterClaims) (*x509.Certificate, error)
SynthesizeCertFromJWT builds an X.509 certificate carrying the AIC claims of an AIC-JWT, so downstream certificate-based pipeline stages (CheckAdmission, capability matching, audit) work unchanged.
func TLSVersionFromString ¶
TLSVersionFromString converts a version string to a TLS version number.
func ToGrant ¶
func ToGrant(c Capability) (semantics.Grant, error)
ToGrant converts a declared capability (AIC or PrincipalAuthorization) into a CLC-v1 grant. The identifier grammar and params shape are validated, so a declaration that cannot be decided is reported instead of silently ignored.
func ToGrantSet ¶
func ToGrantSet(caps []Capability) ([]semantics.Grant, error)
ToGrantSet converts a capability list into a CLC-v1 grant set.
func VerifyAuditEntry ¶
VerifyAuditEntry verifies the TSA timestamp signature of an audit entry.
func VerifyBundle ¶
func VerifyBundle(bundle *CredentialBundle, roots *x509.CertPool) error
VerifyBundle verifies the credential bundle dual chain:
- Agent chain → trust root (default client authentication EKU);
- Principal chain → same trust root;
- keyHash match: AIC.PrincipalUid.KeyHash == SHA256(Principal SPKI).
roots is the trust root pool; if empty, falls back to bundle.CACerts. Returns an error if any verification step fails.
func VerifyDelegationAuth ¶
func VerifyDelegationAuth(aic *AIC, userCert *x509.Certificate) error
VerifyDelegationAuth verifies the validity of a DelegationAuthorization signature. aic must contain a non-empty DelegationAuthorization; userCert is the authorizing user's certificate. The signed content is the DelegationAuthTBS DER encoding (a specific subset, not the entire AIC).
func VerifyDelegationChain ¶
func VerifyDelegationChain(chain []*x509.Certificate, topPrincipal *x509.Certificate, maxDepth int) error
VerifyDelegationChain is a convenience entry point: creates a default verifier and verifies the chain. chain goes from top to bottom: chain[0]=top-level delegating Agent, chain[len-1]=bottom-level Agent. maxDepth is set by the top Principal.
func VerifyDelegationChainWithCaps ¶
func VerifyDelegationChainWithCaps(chain []*x509.Certificate, topPrincipal *x509.Certificate, principalCaps []pki.Capability, maxDepth, maxChainLen int) ([]pki.Capability, error)
VerifyDelegationChainWithCaps verifies a multi-level delegation chain and computes the effective capability intersection:
- Basic signature verification (bottom-up per level) and depth limits;
- Anti-cycle (serial number deduplication within chain);
- Anti-certificate-bomb (chain length upper bound);
- Per-level capability subset validation + C_eff recursive intersection.
maxDepth is set by the top Principal (≤0 means no limit); maxChainLen is the gateway-side hard upper bound (≤0 uses DefaultMaxChainLength). Returns C_eff = P ∩ C_1 ∩ … ∩ C_n.
func VerifyFnFromPublicKey ¶ added in v0.2.0
VerifyFnFromPublicKey returns the DSSE verification callback that VerifyEvidenceDir / VerifyEvidenceEnvelope expect, pinned to one trusted ECDSA public key (P-256 / P-384 / P-521). It closes the "who trusts the record signer" question with one line instead of hand-rolled crypto per call site: the key passed here is the trust anchor, the unauthenticated keyid label is deliberately ignored.
Both the raw r‖s form (the common DSSE/JSON-Sign convention, P-256 = 64 bytes) and ASN.1 DER are accepted, over the SHA-256 of the DSSE PAE.
func VerifyLayer2 ¶
func VerifyLayer2(chain []*x509.Certificate, cfg *PipelineConfig, roles []string) (AdmissionResult, *Layer2Result)
VerifyLayer2 performs Layer 2 representation verification: AIC parsing + PA parsing + delegation representation check (completed within CheckAdmission). Returns AdmissionResult and Layer2Result.
func VerifyPrincipalKeyHash ¶
func VerifyPrincipalKeyHash(agent, principal *x509.Certificate) error
VerifyPrincipalKeyHash verifies that AIC.PrincipalUid.KeyHash matches the SHA-256 of the principal certificate's SPKI. Returns an error (fail-close) if keyHash is missing or empty.
func VerifyProof ¶
VerifyProof verifies a Merkle audit proof.
func VerifyProofBounded ¶
VerifyProofBounded is like VerifyProof but enforces a maximum proof length (finding 22): a proof for a tree with n leaves has at most ceil(log2 n) steps, so any longer proof is rejected regardless of hash match.
func VerifySPIFFESAN ¶
func VerifySPIFFESAN(cert *x509.Certificate, expectedID string) bool
VerifySPIFFESAN validates that a certificate carries the expected SPIFFE ID in its SAN URIs.
func VerifySignedPolicy ¶
func VerifySignedPolicy(sigDER, policyData []byte, roots *x509.CertPool, requireAdminOU bool) (*x509.Certificate, error)
VerifySignedPolicy verifies a PKCS#7 detached signature (policyData is the raw policy bytes). Returns the signer certificate on success, for further OU/status checks by the caller.
Types ¶
type AdmissionConfig ¶
type AdmissionConfig struct {
// RequireAIC when set to true rejects connections without AIC extension.
RequireAIC bool
// RequiredProtocol requires the Agent to have the specified protocol capability (empty = no check).
RequiredProtocol string
// RequiredRuleId requires the Agent to have the specified CapabilityId permission (empty = no check).
RequiredRuleId string
// RequiredCapabilities requires the Agent to have all specified CapabilityIds (empty = no check).
RequiredCapabilities []string
// Operations are the concrete actions (capability id + parameters) this
// request wants to perform. When set, each is decided with the CLC core
// against the effective authority — the AIC capabilities intersected with
// the PrincipalAuthorization grants — so parameter bounds take part in the
// decision instead of matching capability ids alone.
// Operation decisions are recorded in AdmissionResult.OperationDecisions so
// upstream can surface verdict / reason / unresolved to downstream.
Operations []Operation
// UnresolvedEvaluator is the §8.4 residual-obligation release hook. When
// an operation's CLC decision lands on allow_unresolved (recognized but
// not-evaluated constraint, e.g. time:window), the default is fail-closed
// deny. This evaluator can confirm those obligations out-of-band: returning
// true releases the operation — the call site must still honor the residual
// obligations (semantics.Decision.Unresolved) at runtime. nil (default)
// keeps fail-closed deny.
UnresolvedEvaluator func(op Operation, unresolved []string) bool
// DischargeObligations enables the strict consumer-side obligation rule of
// CLC §8.4 with XACML 3.0 §2.13/§7.2.1: before an allow_unresolved decision
// can be released, every obligation identity must be one this deployment
// understands and can and will discharge (ObligationsUnderstood). An
// obligation it does not understand fails closed with obligation_unknown
// instead of being handed to UnresolvedEvaluator as opaque text. false
// (default) keeps the legacy path, where UnresolvedEvaluator alone decides.
DischargeObligations bool
// ObligationsUnderstood lists the obligation (scheme,type) identities this
// deployment understands and can and will discharge, e.g.
// "varwof/constraint-v1:time". Only consulted when DischargeObligations is
// true.
ObligationsUnderstood []string
// RequireFreshDecisionContext requires the deployment to pin a freshness
// context (RATS §10: explicit clock, nonce, or epoch id) and requires it to
// still be fresh when an operation is allowed. A missing, stale, or
// future-dated context denies with a stable reason code; false (default)
// keeps the pre-context behaviour.
RequireFreshDecisionContext bool
// DecisionContext is the pinned freshness input (semantics.DecisionContext).
// Required when RequireFreshDecisionContext is set.
DecisionContext *semantics.DecisionContext
// DisallowRepresentative when set to true rejects DelegationRepresentative mode connections.
DisallowRepresentative bool
// RequireUserPermission when set to true rejects connections without UserPermission extension.
RequireUserPermission bool
// RejectOverflow when set to true rejects AIC containing CapabilityIds not authorized by UserPermission.
RejectOverflow bool
// RequireUserAuth when set to true requires DelegationAuthorization signature verification in the AIC.
RequireUserAuth bool
// EnforceCapSizeConstraints when set to true validates Capability field lengths (schemeId 1-128, capabilityId 1-256, parameters 0-4096).
EnforceCapSizeConstraints bool
// NonceCache is used for DelegationAuthorization nonce replay protection.
// nil means skip nonce replay check (not recommended).
NonceCache *NonceCache
// EnforceSize32 when set to true validates Nonce is exactly 32 bytes.
EnforceSize32 bool
// UserCert is the authorized user's certificate for verifying DelegationAuthorization signatures.
// When nil, if UserCertResolver is not nil, resolves via KeyHash from varwof-core;
// otherwise falls back to the connection peer certificate (only agent == user can verify).
UserCert *x509.Certificate
// UserCertResolver resolves user certificates based on PrincipalUid.KeyHash (via varwof-core API).
// Automatically called when UserCert is nil and RequireUserAuth is true.
UserCertResolver func(keyHash []byte) (*x509.Certificate, error)
// EnforceConstraints when set to true enforces authorizationConstraints (CIDR/time window/concurrency).
EnforceConstraints bool
// StrictConstraints when set to true directly rejects connections with unregistered constraint types
// in authorizationConstraints (unknown capabilityId under constraint/constraint-v1 scheme),
// fail-closed. Default false only logs audit warnings for unknown constraints and ignores them
// (forward compatible, strict mode).
StrictConstraints bool
// ClientIP is used for authorizationConstraints allowed-cidr checks.
ClientIP string
// AuditLogger is used for authorization decision audit logging. When non-nil, logs warnings
// for unknown constraint types, etc.
AuditLogger *AuditLogger
// CheckDAAge when set to true validates DelegationAuthorization.timestamp freshness
// (|now - timestamp| ≤ DAAgeMax). Default off — specification delegates lifecycle validation
// to X.509 NotAfter (dev-docs/aic/06-delegation-auth.md §validation flow (gateway runtime));
// deployments requiring stricter time window defense can enable this.
CheckDAAge bool
// DAAgeMax is the DA timestamp freshness window (|now - timestamp| ≤ DAAgeMax).
// Only effective when CheckDAAge=true; <=0 uses DefaultDAAgeMax (30 seconds).
DAAgeMax time.Duration
// CredentialBundle is the client-submitted credential bundle (agent, principal and CA chains).
// When RequireUserAuth is true and UserCert is nil, prioritizes the Principal certificate
// from the credential bundle for DA signature verification (including keyHash cross-validation);
// falls back to UserCertResolver when missing.
CredentialBundle *CredentialBundle
}
AdmissionConfig is the configuration for the admission engine.
func (AdmissionConfig) Validate ¶
func (c AdmissionConfig) Validate() error
Validate checks the configuration for conflicting options.
type AdmissionFact ¶ added in v0.2.0
type AdmissionFact struct {
Type string `json:"type"`
Digest semantics.Digest `json:"digest"`
Note string `json:"note,omitempty"`
}
AdmissionFact is one content-addressed fact that decided the outcome: the client certificate, the AIC extension, the requested operations, and so on. Only digests travel here — the material stays where it was verified.
type AdmissionIdentity ¶ added in v0.2.0
type AdmissionIdentity struct {
Principal string `json:"principal,omitempty"`
AgentID string `json:"agentId,omitempty"`
Serial string `json:"serial,omitempty"`
SPIFFEID string `json:"spiffeId,omitempty"`
}
AdmissionIdentity is who the request claimed to be, as far as the pipeline got.
type AdmissionRecord ¶ added in v0.2.0
type AdmissionRecord struct {
Ver string `json:"ver"`
Stage string `json:"stage"`
Outcome string `json:"outcome"` // refused
Reason string `json:"reason,omitempty"`
RecorderID string `json:"recorderId,omitempty"`
At time.Time `json:"at"`
Identity AdmissionIdentity `json:"identity"`
Facts []AdmissionFact `json:"facts,omitempty"`
}
AdmissionRecord says what the pipeline decided before (or instead of) reaching the language layer, and why. It never claims a CLC verdict.
func NewAdmissionRecord ¶ added in v0.2.0
func NewAdmissionRecord(ctx EvidenceContext, err *AuthError, facts []AdmissionFact) AdmissionRecord
NewAdmissionRecord builds a pipeline-level record for a refusal.
func ParseAdmissionEnvelope ¶ added in v0.2.0
func ParseAdmissionEnvelope(env semantics.Envelope) (AdmissionRecord, error)
ParseAdmissionEnvelope decodes and structurally checks a pipeline-level record.
type AdmissionResult ¶
type AdmissionResult struct {
Decision DecisionResult
Reason string
AIC *AIC
PrincipalAuthorization *PrincipalAuthorization
PrincipalUid string
// EffectiveCaps is the P∩C (AIC declarations ∩ PA grants) intersection result,
// preserving full Capability (including SchemeId/Parameters). When no PA is present,
// equals the full AIC declarations. Phase two plugin evaluation only acts on this set
// — declarations outside the intersection (including unrelated schemes) do not participate
// in decisions or block connections (operation-level mapping).
EffectiveCaps []Capability
// OperationDecisions records the per-operation CLC verdicts when
// cfg.Operations was set. Populated on the allow path (and kept on the
// deny path for the operation that caused it) so upstream can surface
// verdict / reason / unresolved to downstream.
OperationDecisions []OperationDecision
// Sources is the authorization chain this admission can prove it rested on
// (CLC SourceChain): the verified leaf certificate, plus the principal
// certificate when configured. Nil when no certificate was presented.
Sources *semantics.SourceChain
}
AdmissionResult contains the complete admission check result.
func CheckAdmission ¶
func CheckAdmission(cert *x509.Certificate, cfg AdmissionConfig) AdmissionResult
- Parse GatewaySession extension
- Verify AgentType is in the allowed list
- Check protocol capability match
- Check RuleId permission
- Check required capability subset
- Check delegation mode
- Parse UserPermission extension
- Check merge with UserPermission
Returns AdmissionResult; caller decides whether to allow based on the Decision field.
type ApprovalRequester ¶
type ApprovalRequester interface {
// Request asks a human to approve (or deny) the operation described by
// risk. A nil result or a denied/pending decision denies the request.
Request(ctx context.Context, risk RiskAssessment) (*SupervisionResult, error)
}
ApprovalRequester is the mid-operation (runtime) human approval hook. It is invoked when the admission decision path decides a request needs on-site human approval. The default (nil) fails closed: the request is denied with approval_required.
type AuditAction ¶
type AuditAction string
AuditAction represents the action type of an audit event.
const ( // ActionConnected indicates the client has connected. ActionConnected AuditAction = "connected" // ActionDisconnected indicates the client has disconnected. ActionDisconnected AuditAction = "disconnected" // ActionDenied indicates the connection was rejected. ActionDenied AuditAction = "denied" // ActionRevoked indicates the certificate has been revoked. ActionRevoked AuditAction = "revoked" // ActionProxied indicates the proxy forwarding has been established. ActionProxied AuditAction = "proxied" // ActionCompleted indicates the proxy forwarding has completed. ActionCompleted AuditAction = "completed" // ActionNoRoute indicates no matching route was found. ActionNoRoute AuditAction = "no_route" // ActionWSConnect indicates a WebSocket has been connected. ActionWSConnect AuditAction = "ws_connect" // ActionWSClose indicates a WebSocket has been closed. ActionWSClose AuditAction = "ws_close" // ActionPluginDecision indicates a plugin decision has been executed. ActionPluginDecision AuditAction = "plugin_decision" // ActionUnknownConstraint indicates an unknown constraint type was ignored (forward compatibility). ActionUnknownConstraint AuditAction = "unknown_constraint" )
type AuditChain ¶
type AuditChain struct {
// contains filtered or unexported fields
}
AuditChain manages a sequence of Merkle tree batches for audit trail integrity.
func NewAuditChain ¶
func NewAuditChain(batchSize int, onSeal func(root []byte)) *AuditChain
NewAuditChain creates an audit chain.
func (*AuditChain) BatchCount ¶
func (c *AuditChain) BatchCount() int
BatchCount returns the number of sealed batches.
func (*AuditChain) Dump ¶
func (c *AuditChain) Dump() string
Dump exports a text summary of all batches.
func (*AuditChain) GetTree ¶
func (c *AuditChain) GetTree(batchNumber int) *SealedTree
GetTree returns the sealed tree for a given batch number.
func (*AuditChain) LatestRoot ¶
func (c *AuditChain) LatestRoot() string
LatestRoot returns the root hash (hex) of the most recent batch.
func (*AuditChain) LatestRootBytes ¶
func (c *AuditChain) LatestRootBytes() []byte
LatestRootBytes returns the root hash (raw bytes) of the most recent batch.
func (*AuditChain) Seal ¶
func (c *AuditChain) Seal(entries [][]byte, previousRoot string) *SealedTree
Seal seals a batch of audit entries into a Merkle tree.
func (*AuditChain) SealChecked ¶
func (c *AuditChain) SealChecked(entries [][]byte, previousRoot string) (*SealedTree, error)
SealChecked seals a batch like Seal but enforces chain continuity (finding 8): when the chain is non-empty, previousRoot must equal the latest root; passing an empty previousRoot for a non-empty chain is refused. Tamper-evidence is only meaningful if every batch links to its predecessor.
func (*AuditChain) VerifyContinuity ¶
func (c *AuditChain) VerifyContinuity() error
VerifyContinuity checks that every batch (after the first) links to its predecessor's root, so a reordered/inserted/deleted batch is detected (finding 8).
func (*AuditChain) VerifyJSON ¶
func (c *AuditChain) VerifyJSON(req *VerifyRequest) *VerifyResponse
VerifyJSON verifies an audit proof based on a JSON request.
type AuditEntry ¶
type AuditEntry struct {
Time string `json:"time"`
Action string `json:"action"`
SrcIP string `json:"src_ip"`
ClientCN string `json:"client_cn,omitempty"`
ClientSerial string `json:"client_serial,omitempty"`
Roles []string `json:"roles,omitempty"`
Mapping string `json:"mapping"`
Target string `json:"target"`
TargetID string `json:"target_id,omitempty"`
Duration string `json:"duration,omitempty"`
DenyReason string `json:"deny_reason,omitempty"`
BytesIn int64 `json:"bytes_in,omitempty"`
BytesOut int64 `json:"bytes_out,omitempty"`
TraceId string `json:"trace_id,omitempty"`
SessionId string `json:"session_id,omitempty"`
GatewayId string `json:"gateway_id,omitempty"`
Protocol string `json:"protocol,omitempty"`
AgentId string `json:"agent_id,omitempty"`
SPIFFEID string `json:"spiffe_id,omitempty"`
PrincipalUid string `json:"principal_uid,omitempty"`
DelegationMode int `json:"delegation_mode,omitempty"`
Decision string `json:"decision,omitempty"`
Capabilities []string `json:"capabilities,omitempty"`
// Level is the audit entry level (INFO/WARN/ERROR). Plugin decisions: allow=INFO,
// deny/execution error=WARN.
Level string `json:"level,omitempty"`
// DaHash is the SHA-256 hex hash of the DelegationAuthorization signatureValue
// (authorization evidence fingerprint, Task 4: binding authorization evidence to action records).
DaHash string `json:"da_hash,omitempty"`
// AICFingerprint is the SHA-256 hex of the AIC extension DER encoding (Task 4).
AICFingerprint string `json:"aic_fingerprint,omitempty"`
// PolicyVersion is the policy version effective at decision time (Task 5a: binding decision records to policy version).
// 0 when PolicyManager is not enabled (omitempty omits from output).
PolicyVersion uint64 `json:"policy_version,omitempty"`
// RecordDigest is the input digest (hex) of the evidence record this
// decision produced (refs[0].Digest). It pins the audit line to the
// replayable record chain so the two no longer drift apart; empty when the
// decision produced no record.
RecordDigest string `json:"record_digest,omitempty"`
}
AuditEntry is an audit log entry that records connection or decision events.
func NewAuditEntryDenied ¶
func NewAuditEntryDenied(srcIP, mappingName, target, reason string, cert *x509.Certificate) AuditEntry
NewAuditEntryDenied creates an audit entry for a denied connection.
func NewAuditEntryFromConn ¶
func NewAuditEntryFromConn(srcIP, mappingName, target string, cert *x509.Certificate) AuditEntry
NewAuditEntryFromConn creates an audit entry from connection information.
func ReadAuditEntries ¶
func ReadAuditEntries(file string, filter AuditFilter) ([]AuditEntry, error)
ReadAuditEntries reads audit entries by filter.
func (*AuditEntry) SetV12Fields ¶
func (e *AuditEntry) SetV12Fields(protocol, gatewayId, traceId, sessionId, decision string)
SetV12Fields sets the v1.2 spec extension fields for the audit log.
func (*AuditEntry) WithEvidenceFingerprints ¶
func (e *AuditEntry) WithEvidenceFingerprints(cert *x509.Certificate) *AuditEntry
WithEvidenceFingerprints populates the audit entry's authorization evidence fingerprint fields (Task 4). Returns the original entry for method chaining.
type AuditFilter ¶
type AuditFilter struct {
Since time.Time
Until time.Time
Limit int
Offset int
Sort string
Action string
ClientCN string
Serial string
Mapping string
}
AuditFilter is the audit log query filter.
type AuditLogger ¶
type AuditLogger struct {
// contains filtered or unexported fields
}
AuditLogger is the audit log writer that writes in JSON Lines format.
func NewAuditLogger ¶
NewAuditLogger creates an audit log writer (returns nil if file is empty).
func (*AuditLogger) Close ¶
func (l *AuditLogger) Close() error
Close closes the audit log writer, draining buffered entries. It is idempotent: closing an already-closed logger returns nil instead of re-draining the closed channel (which would spin and write garbage forever).
func (*AuditLogger) Dropped ¶
func (l *AuditLogger) Dropped() int64
Dropped returns the number of audit entries discarded because the buffer was full (M6). Data-plane Log calls must never block, so overflow is dropped and counted rather than stalling the caller.
func (*AuditLogger) File ¶
func (l *AuditLogger) File() string
File returns the audit log file path.
func (*AuditLogger) Log ¶
func (l *AuditLogger) Log(entry AuditEntry)
Log enqueues an audit entry. It never blocks the caller (M6): if the buffer is full the entry is dropped and counted. This prevents a slow audit sink (e.g. TSA) from stalling the data plane. Security-critical entries (WARN/ERROR, revocation/denial actions) are never dropped on the fast path: Log waits up to a bounded timeout for them so an attacker flooding the log cannot evict the evidence of its own activity (finding 15).
type AuditVerifier ¶
type AuditVerifier struct {
// contains filtered or unexported fields
}
AuditVerifier verifies audit log entries via TSA timestamps.
type AuthContext ¶
type AuthContext struct {
// ClientCert is the admitted client certificate (real mTLS peer or a
// synthesized carrier for Bearer AIC-JWT).
ClientCert *x509.Certificate
// Principal is the AIC PrincipalUid string.
Principal string
// AgentID is the AIC AgentId.
AgentID string
// SPIFFEID is the SPIFFE ID from the certificate SAN (empty if none).
SPIFFEID string
// Roles are the extracted policy roles.
Roles []string
// Capabilities lists the admitted AIC capability ids.
Capabilities []string
// AIC is the parsed AIC extension (nil for non-AIC certificates).
AIC *AIC
// Bearer reports whether the request was authenticated by a JWT bearer.
Bearer bool
// Serial is the normalized client certificate serial.
Serial string
// Verdict is the overall CLC verdict for the requested operations (B3):
// "allow" when every operation was authorized outright, "allow_unresolved"
// when one or more carried §8.4 residual obligations (released or pending).
// Empty when no Operations were configured.
Verdict string
// Reason is the aggregated CLC reason for the verdict.
Reason string
// Unresolved lists recognized-but-unevaluated constraints carried by an
// allow_unresolved verdict (§8.4 residual-obligation channel).
Unresolved []string
// OperationDecisions is the per-operation CLC verdict detail (B3).
OperationDecisions []OperationDecision
// Evidence points at the decision records this admission produced (digest,
// verdict and, for file-like sinks, where it was written). Empty when
// evidence emission is not configured.
Evidence []RecordRef
// Satisfaction is the evidence-requirement evaluation for this request
// (nil when no requirement is configured): satisfied / violated / unknown,
// with the per-constraint detail and the missing roles. It answers "was
// enough evidence presented", not "is this action authorized".
Satisfaction *semantics.RequirementResult
}
AuthContext is the verified identity attached to the request context after a successful admission.
func AuthContextFromHeaders ¶
func AuthContextFromHeaders(r *http.Request) *AuthContext
AuthContextFromHeaders reconstructs a minimal AuthContext from the identity headers the reverse proxy injects after admission (X-AIC-Agent-Id, X-AIC-Principal-Uid, X-AIC-Capabilities-Full, X-Agent-ID). It is meant for backends that run behind the aic-verifier proxy on a trusted network: the proxy strips this whole header namespace from client input and only re-emits values it verified, so a loopback-only backend may trust them. Returns nil when no identity headers are present, so callers can fail closed.
Capabilities are restored as full scheme:capabilityId identifiers into both Capabilities (the bare capability ids, as regular admission produces) and AIC.Capabilities (with SchemeId/CapabilityId split for FullID matching). The rest of the AuthContext (ClientCert, DA evidence, cross-signed chains) is not recoverable from headers and stays empty — authorization on such a backend must not depend on data the proxy did not forward.
func FromContext ¶
func FromContext(ctx context.Context) *AuthContext
FromContext retrieves the verified AuthContext from a request context. Returns nil when the request did not pass through a aic-verifier middleware.
type AuthError ¶
type AuthError struct {
Code ErrorCode
Status int
Message string
// Stage, when set, overrides the AdmissionRecord stage for this refusal
// (the default is Code.String()). The middleware's pre-language refusals
// keep the default; the reverse proxy names the layer that refused
// ("route_denied", "method_not_allowed", "capability_denied", ...) so one
// proxy can tell its refusals apart.
Stage string
// Evidence points at the records a refused request still produced, so the
// caller can log or forward them next to the challenge.
Evidence []RecordRef
// Problem, when set, is written as an RFC 9457 problem document instead of
// the SDK's compact JSON error — the carrier for CLC-CHALLENGE-v1.
Problem *ProblemDetails
// Satisfaction, when the refusal is an unsatisfied evidence requirement, is
// the machine-readable result (violated / unknown with the missing roles),
// so a caller can act on *why* the evidence bar was not met without parsing
// the challenge back out.
Satisfaction *semantics.RequirementResult
}
AuthError is the typed admission failure.
type AuthMode ¶
type AuthMode int
AuthMode selects which credential transports are accepted.
const ( // MTLSOnly accepts requests authenticated by an mTLS client certificate. MTLSOnly AuthMode = iota // BearerOnly accepts requests authenticated by an Authorization: Bearer AIC-JWT. BearerOnly // MTLSOrBearer accepts either an mTLS client certificate or a Bearer AIC-JWT, // matching gateway-core semantics. mTLS takes precedence when both are present. MTLSOrBearer )
type AuthorizationPolicy ¶
type AuthorizationPolicy struct {
Version string `json:"version"`
Roles map[string]PolicyRole `json:"roles"`
OUMapping map[string]string `json:"ou_mapping"`
GatewayNamespaces map[string]PolicyNamespace `json:"gateway_namespaces"`
// CapabilityParameters is the parameter default values map derived by gen-authz from
// capability.json. Key is "scheme:capability_id" (e.g., "varwof/gateway-v1:admin:config").
CapabilityParameters map[string]map[string]any `json:"capability_parameters,omitempty"`
}
AuthorizationPolicy is the runtime model for the gateway authorization policy (authz.json). Structurally identical to varwof-core auth/policy.go, but kept as an independent implementation to maintain lib's zero new external dependency policy.
func GetAuthorizationPolicy ¶
func GetAuthorizationPolicy() *AuthorizationPolicy
GetAuthorizationPolicy returns the current global authorization policy (may be nil).
func LoadAuthorizationPolicy ¶
func LoadAuthorizationPolicy(policyPath, sigSuffix string, opts *PolicyVerifyOptions) (*AuthorizationPolicy, error)
LoadAuthorizationPolicy loads the authorization policy from a file. If opts is non-nil and the signature file (policyPath+sigSuffix) exists, signature verification is performed first.
func ParseAuthorizationPolicy ¶
func ParseAuthorizationPolicy(data []byte) (*AuthorizationPolicy, error)
ParseAuthorizationPolicy parses the policy JSON.
func (*AuthorizationPolicy) HasGrant ¶
func (p *AuthorizationPolicy) HasGrant(role, capability string) bool
HasGrant checks whether a role has a given capability (supports wildcards).
func (*AuthorizationPolicy) HasParamDefault ¶
func (p *AuthorizationPolicy) HasParamDefault(scheme, capID, param string) (any, bool)
HasParamDefault checks whether a parameter has a default value (for overflow validation).
func (*AuthorizationPolicy) IntersectGrants ¶
func (p *AuthorizationPolicy) IntersectGrants(roles []string, aicCapIds []string) []string
IntersectGrants returns the subset of aicCapIds that matches any role grants.
func (*AuthorizationPolicy) ParamDefaults ¶
func (p *AuthorizationPolicy) ParamDefaults(scheme, capID string) map[string]any
ParamDefaults returns the parameter defaults for a given scheme:capability_id (gen-authz derived). Returns nil if not found.
func (*AuthorizationPolicy) RoleByOU ¶
func (p *AuthorizationPolicy) RoleByOU(ou string) string
RoleByOU maps a certificate OU to a role name.
func (*AuthorizationPolicy) RoleGrants ¶
func (p *AuthorizationPolicy) RoleGrants(role string) []string
RoleGrants returns the grants list for a role.
type CRLCache ¶
type CRLCache struct {
// contains filtered or unexported fields
}
CRLCache is a CRL cache that supports periodic refresh and forced reload.
func NewCRLCache ¶
func NewCRLCache(caCert *x509.Certificate, url string, refreshSec int, translator Translator, lang string) *CRLCache
NewCRLCache creates a CRL cache instance.
func (*CRLCache) ForceRefresh ¶
ForceRefresh forces an immediate CRL cache refresh.
func (*CRLCache) IsRevoked ¶
IsRevoked checks whether a given certificate serial number has been revoked.
func (*CRLCache) IsRevokedCert ¶
func (c *CRLCache) IsRevokedCert(cert *x509.Certificate) (bool, error)
IsRevokedCert checks whether the given certificate is revoked, matching the certificate's issuer against this cache's CA robustly (finding 13): the raw issuer bytes are compared first, falling back to a structural RDN comparison that tolerates RDN ordering/formatting differences. A certificate not issued by this cache's CA is not covered by it and returns not-revoked.
func (*CRLCache) LastRefresh ¶
LastRefresh returns the time of the last successful refresh.
type CRLRevokedFunc ¶
CRLRevokedFunc reports whether the certificate serial has been revoked by the CA identified by caDN. An error must fail closed (the OCSP crl fallback cannot prove the certificate valid when the CRL cannot be consulted).
type Capability ¶
type Capability = pki.Capability
── Type aliases ──
func ConstraintToCapability ¶ added in v0.2.0
func ConstraintToCapability(c string) (Capability, bool)
ConstraintToCapability is the inverse of ConstraintStrings: it maps a CLC constraint string back to the (scheme, capability, parameters) triple the connection-level registry evaluates. A string this SDK cannot map is reported as false so callers fail closed.
type CapabilityPlugin ¶
type CapabilityPlugin = pki.CapabilityPlugin
CapabilityPlugin is the interface for all capability plugins.
type CapabilityRegistry ¶
type CapabilityRegistry interface {
// ValidateCapability validates the full identifier "scheme:capability_id".
ValidateCapability(formatted string) error
// Enabled reports whether the registry has been loaded.
Enabled() bool
}
CapabilityRegistry is the capability registration validation interface (single source of truth). Gateway data plane validates AIC-declared capabilities against the registry during admission pipeline (RunAccessPipeline) phase one: unregistered scheme/capability is treated as an illegal declaration.
Injected by gateways (gateway-*/protocol modules): internally holds a register.Registry (embedded + disk override), atomically replaced after SIGHUP hot reload.
Returns nil when the capability is registered; returns an error when unregistered (caller rejects the connection).
func GetGlobalCapabilityRegistry ¶
func GetGlobalCapabilityRegistry() CapabilityRegistry
GetGlobalCapabilityRegistry returns the current package-level capability registry (may be nil).
type ChallengeCarrier ¶ added in v0.2.0
type ChallengeCarrier interface {
// Write renders problem as the response body. The header may be modified
// before the body is written; returning an error leaves the response
// partially written and the caller falls back to a minimal JSON error.
Write(w http.ResponseWriter, problem *ProblemDetails) error
}
ChallengeCarrier writes the HTTP response for a refusal that carries a challenge. The problem is the machine-readable "what is still needed"; the carrier decides how that is conveyed (RFC 9457 problem+json by default, but a deployment that routes through an aggregator may want its own shape). The SDK sets Retry-After from the challenge's retry lower bound before calling Write, so a non-standard carrier still keeps the bound visible. The carrier owns status, content type and body; Write must be safe for concurrent use.
var DefaultChallengeCarrier ChallengeCarrier = problemJSONCarrier{}
DefaultChallengeCarrier is the problem+json carrier used when a Config does not set ChallengeCarrier.
type ChallengeConfig ¶ added in v0.2.0
type ChallengeConfig struct {
// TTL bounds how long the challenge may drive a retry.
TTL time.Duration
// Audience names the relying party the challenge is addressed to.
Audience string
// RetryAfter, when set, becomes the challenge's retry lower bound (and the
// Retry-After header). It exists because retrying a refusal can amplify
// load: the client is told when a corrected presentation is welcome.
RetryAfter time.Duration
// ObtainHints tell the requester where the missing item can be obtained.
ObtainHints []semantics.ObtainHint
// Now, NewID and NewNonce are injectable for tests. Defaults: time.Now,
// and 16 random bytes hex-encoded (the nonce must be unpredictable; single
// use of the retry is the enforcement point's business).
Now func() time.Time
NewID func() string
NewNonce func() string
}
ChallengeConfig controls whether and how refusals carry a challenge.
type Config ¶
type Config struct {
// TLSCertFile / TLSKeyFile are the server certificate used to terminate TLS
// (required for the reverse-proxy server in TLS/mTLS mode).
TLSCertFile string
TLSKeyFile string
// CACertFile is the CA bundle for mTLS client certificate verification.
// Empty disables mTLS (Bearer-only or plaintext listeners).
CACertFile string
// JWTCAFile is a PEM file with the CA certificates used to build the AIC-JWT
// trust root. Empty disables Bearer authentication.
JWTCAFile string
// BackendRootCA, when non-empty, is the root CA bundle (one or more PEM
// files, comma/space separated) trusted for the reverse proxy's outbound
// TLS to HTTPS backends. Appended to the system roots when set; empty uses
// system roots only.
BackendRootCA string
// JWTIssuer, when non-empty, requires the AIC-JWT iss claim to match.
JWTIssuer string
// JWTAudience, when non-empty, requires the AIC-JWT aud claim to include one.
JWTAudience []string
// ReplayProtection enables one-time-use replay protection on bearer tokens
// (default true).
ReplayProtection *bool
// IdentityMode selects how much verified client identity is forwarded to
// backends (default IdentityForwardClientCert). This is fixed at startup
// and MUST come from configuration — it is never read from a client-supplied
// request header, so clients cannot downgrade the identity disclosed to a
// backend.
IdentityMode IdentityHeaderMode
// AuthMode selects accepted credential transports (default MTLSOrBearer).
AuthMode AuthMode
// RequireAIC rejects clients whose certificate carries no AIC extension.
RequireAIC bool
// RequiredCapabilities requires the agent to hold all listed CapabilityIds.
RequiredCapabilities []string
// RequiredOperations are the concrete actions (id + parameters) this
// service authorizes. Unlike RequiredCapabilities, parameter bounds are
// part of the decision (CLC): an operation asking for more than the grant
// allows is denied.
RequiredOperations []Operation
// UnresolvedEvaluator is the §8.4 residual-obligation release hook for
// allow_unresolved CLC decisions; forwarded to PipelineConfig → AdmissionConfig.
// Set programmatically — not parsed from JSON.
UnresolvedEvaluator func(op Operation, unresolved []string) bool
// DischargeObligations / ObligationsUnderstood enable the strict
// consumer-side obligation rule (CLC §8.4 + XACML §2.13/§7.2.1); forwarded
// to PipelineConfig → AdmissionConfig. Set programmatically — not parsed
// from JSON.
DischargeObligations bool
ObligationsUnderstood []string
// RequireFreshDecisionContext / DecisionContext pin the RATS §10 freshness
// input for allowed operations; forwarded to PipelineConfig →
// AdmissionConfig.
RequireFreshDecisionContext bool
DecisionContext *semantics.DecisionContext
// Challenges enables the CLC-CHALLENGE-v1 carrier: a denial caused by
// unmet §8.4 residual obligations is answered with 403 +
// application/problem+json carrying the challenge, and its retry lower
// bound is mapped to Retry-After. nil (default) keeps the compact JSON
// error.
Challenges *ChallengeConfig
// ChallengeCarrier shapes the HTTP response for a refusal that carries a
// challenge. nil (default) uses the built-in RFC 9457
// application/problem+json carrier. A custom carrier may change the body
// shape; the SDK still sets Retry-After from the challenge's retry bound
// before delegating, so the lower bound survives regardless.
ChallengeCarrier ChallengeCarrier
// EvidenceRequirement is the relying party's evidence sufficiency bar
// (CLC-REQUIREMENT-v1). The SDK never takes it from the request: it is this
// deployment's configuration. When set, every admitted request is also
// evaluated against it (via EvidenceFacts), a refusal carries the machine
// readable challenge, and the emitted records bind the requirement digest.
EvidenceRequirement *semantics.Requirement
// EvidenceFacts supplies the evidence facts presented with a request. The
// SDK does not parse evidence artifacts; the deployment hands over facts its
// own verifiers established (type, protected subject id, issuance time,
// whether it reached VERIFIED). An error is fail-closed.
EvidenceFacts func(r *http.Request, ac *AuthContext) ([]semantics.EvidenceFact, error)
// EvidenceProfile names the evidence shape this deployment emits, e.g.
// "clc-decision+admission+outcome@1". It is resolved at configuration time
// (an unknown name is a configuration error) and fills the shape parts of
// Evidence: which payloads are produced, whether records carry a freshness
// context and the source chain, and which requirement is bound. Changing
// the emitted shape is a value here, not an edit to the decision path.
EvidenceProfile string
// Evidence, when set, freezes a CLC decision record for every decided
// operation and hands it to a sink (structured log by default, or one DSSE
// envelope per record on disk). nil (default) emits nothing — a deployment
// that only decides online pays no bytes. Records are produced per source
// (AIC capabilities, principal authorization); the combined verdict stays in
// AuthContext.
Evidence *EvidenceConfig
// DisallowRepresentative rejects DelegationRepresentative-mode AIC.
DisallowRepresentative bool
// RequireUserAuth requires DelegationAuthorization signature verification.
RequireUserAuth bool
// EnforceConstraints enforces authorizationConstraints (CIDR / time window /
// concurrency).
EnforceConstraints bool
// UserCert is the authorized user certificate for DA signature verification.
// UserCertResolver resolves a user certificate by principal KeyHash when
// RequireUserAuth needs DA signature verification. If both are nil, only
// self-issued DA (agent == user) verifies.
UserCert *x509.Certificate
UserCertResolver func(keyHash []byte) (*x509.Certificate, error)
// CRLCache / OCSPCache plug in revocation checking. Nil disables that check.
CRLCache *CRLCache
OCSPCache *OCSPCache
// AuditLogger records admission decisions. Nil disables audit logging.
AuditLogger *AuditLogger
// NonceCache provides anti-replay protection for DA nonces. Nil disables it.
NonceCache *NonceCache
// PluginRegistry registers capability plugins consulted during admission.
// Nil disables phase-one plugin decisions.
PluginRegistry *PluginRegistry
// CapabilityRegistry validates AIC-declared capabilities are registered.
// Nil falls back to the global registry.
CapabilityRegistry CapabilityRegistry
// Logger is the structured logger (default slog.Default()).
Logger *slog.Logger
// LogFile, when non-empty, appends the SDK's own log output to this file
// (created with 0644 if missing) instead of stdout.
LogFile string
// StreamBody when false (default) limits the copied evaluation body to
// DefaultMaxBodyBytes. Services streaming large bodies should set this true.
StreamBody bool
// Hooks install lifecycle callbacks (see Hooks). This is the reserved
// extension point for callers that need to observe or veto decisions.
Hooks *Hooks
// ApprovalRequester is the mid-operation runtime human approval hook. A
// nil requester denies requests flagged by RequireApproval with
// approval_required.
ApprovalRequester ApprovalRequester
// OverrideRecorder records break-glass / override events. A nil recorder
// (together with AllowBreakGlass=true) is a startup error: an unlogged
// break-glass is never available.
OverrideRecorder OverrideRecorder
// SupervisionPolicy gates runtime approval, break-glass and evidence
// export (startup validation rules in newAuthenticator).
SupervisionPolicy SupervisionPolicy
// SupervisionStore persists supervision events (append-only JSONL). Nil
// skips event persistence; decision outcomes stay fail-closed regardless.
SupervisionStore *SupervisionStore
// EvidenceExporter exports evidence bundles for post-operation attribution.
EvidenceExporter EvidenceExporter
// RequireApproval, when non-nil, is consulted per request. Returning true
// routes the request through ApprovalRequester before it is admitted; nil
// (or false) admits directly. The same trigger point is reserved for the
// LLM semantic gate.
RequireApproval func(ctx *AuthContext, r *http.Request) bool
// AuditLogFile / AuditTSAURL / SupervisionLogFile are the file-based
// supervision inputs read from JSON configuration (see fileConfig). They
// are the configuration mirror of the SupervisionStore / audit chain: the
// built-in EvidenceExporter (FileEvidenceExporter) reads them together
// with the audit file to build evidence bundles. Code-injected stores
// (SupervisionStore, AuditLogger) take precedence when both are set.
AuditLogFile string
AuditTSAURL string
SupervisionLogFile string
// ServerOptions tunes the embedded http.Server used in proxy style
// (Server.ListenAndServe). Zero values fall back to safe defaults.
ServerOptions *ServerOptions
// contains filtered or unexported fields
}
Config is the full aic-verifier configuration. All fields are optional; the zero value disables every check except the raw transport decision pipeline.
func LoadConfigFile ¶
LoadConfigFile reads a JSON configuration file into a Config. Unknown fields are rejected so typos surface as errors instead of silently ignored options.
func ParseConfig ¶
ParseConfig decodes JSON configuration bytes into a Config.
func (*Config) AuthMiddleware ¶
AuthMiddleware mirrors Handler but panics on an invalid config, so it can be used inline in http.Server{Handler: ...}. Use Handler when errors must be handled explicitly.
func (*Config) CloseLogger ¶
CloseLogger releases the file opened for LogFile (no-op when none set).
type ConstraintContext ¶
type ConstraintContext struct {
// ClientIP is used for source-address-based constraints such as allowed-cidr / geo-fence.
ClientIP string
// Now is the evaluation time; defaults to current UTC time. Can be injected for testing and offline decisions.
Now time.Time
}
ConstraintContext is the runtime context provided during constraint evaluation.
type ConstraintEvaluator ¶
type ConstraintEvaluator interface {
// CapabilityId returns the constraint type identifier handled by this evaluator.
CapabilityId() string
// Evaluate evaluates a single constraint. The cap's SchemeId has already been filtered
// to constraint / constraint-v1 by the caller.
Evaluate(cap *Capability, ctx *ConstraintContext) error
}
ConstraintEvaluator evaluates a single constraint from authorizationConstraints. Returns a non-nil error if the constraint is not satisfied; the gateway denies the connection.
type ConstraintRegistry ¶
type ConstraintRegistry struct {
// contains filtered or unexported fields
}
ConstraintRegistry registers/looks up constraint evaluators by capabilityId. It provides an extensible constraint type registration mechanism: when adding a new constraint type, only the corresponding evaluator needs to be registered without modifying the certificate ASN.1 structure or gateway core routing code.
func NewConstraintRegistry ¶
func NewConstraintRegistry() *ConstraintRegistry
NewConstraintRegistry creates an empty registry.
func (*ConstraintRegistry) Find ¶
func (r *ConstraintRegistry) Find(capabilityId string) (ConstraintEvaluator, error)
Find looks up an evaluator by capabilityId.
func (*ConstraintRegistry) Keys ¶
func (r *ConstraintRegistry) Keys() []string
Keys returns the list of registered capabilityIds (for metrics/audit).
func (*ConstraintRegistry) Len ¶
func (r *ConstraintRegistry) Len() int
Len returns the number of registered constraint evaluators.
func (*ConstraintRegistry) Register ¶
func (r *ConstraintRegistry) Register(ev ConstraintEvaluator) error
Register registers a constraint evaluator. Returns an error if the same capabilityId is registered twice.
func (*ConstraintRegistry) Remove ¶
func (r *ConstraintRegistry) Remove(capabilityId string)
Remove removes an evaluator. After removal, unknown types revert to the "unknown constraint" semantics (ignored by default).
func (*ConstraintRegistry) Replace ¶
func (r *ConstraintRegistry) Replace(ev ConstraintEvaluator) error
Replace atomically replaces a registered evaluator (for hot updates). Registers the evaluator if not already registered.
func (*ConstraintRegistry) Reset ¶
func (r *ConstraintRegistry) Reset()
Reset clears the registry (for testing only).
type ContentInfo ¶
type ContentInfo struct {
ContentType asn1.ObjectIdentifier
Content asn1.RawValue `asn1:"explicit,tag:0"`
}
ContentInfo is CMS content information.
type CredentialBundle ¶
type CredentialBundle struct {
// AgentChain is the agent certificate chain containing AIC.
// chain[0]=Agent, subsequent entries are intermediate/root CAs.
AgentChain []*x509.Certificate
// PrincipalChain is the principal certificate chain containing PA.
// chain[0]=Principal (independently issued, not in the agent chain),
// subsequent entries are intermediate CAs (optional).
PrincipalChain []*x509.Certificate
// CACerts are optional CA certs carried in the bundle for informational
// purposes. They are never used as trust anchors (finding 20): callers must
// supply operator-configured roots via VerifyBundle.
CACerts []*x509.Certificate
}
CredentialBundle is the credential bundle submitted by the client (Agent certificate chain + Principal certificate chain + CA).
func NewCredentialBundle ¶
func NewCredentialBundle(agentChain, principalChain, caCerts []*x509.Certificate) (*CredentialBundle, error)
NewCredentialBundle constructs a credential bundle. Returns an error if either chain is empty.
func ParseCredentialBundlePEM ¶
func ParseCredentialBundlePEM(data []byte) (*CredentialBundle, error)
ParseCredentialBundlePEM parses a credential bundle from PEM data (order: agent chain first, principal second, CA chain last). Certificates are classified by extensions: AIC-containing → Agent chain, PA-containing → Principal chain, rest → CA. The parsed result must be verified via VerifyBundle before use.
func (*CredentialBundle) Agent ¶
func (b *CredentialBundle) Agent() *x509.Certificate
Agent returns the Agent certificate (contains AIC, chain[0]).
func (*CredentialBundle) Principal ¶
func (b *CredentialBundle) Principal() *x509.Certificate
Principal returns the Principal certificate (contains PA, chain[0]).
type DecisionResult ¶
type DecisionResult int
DecisionResult is the result of a connection admission decision.
const ( // DecisionAllow means admission is allowed. DecisionAllow DecisionResult = iota // DecisionDeny means admission is denied. DecisionDeny DecisionResult = iota // DecisionNeedAuth means additional authentication is required. DecisionNeedAuth )
type DelegationAuthorization ¶
type DelegationAuthorization = pki.DelegationAuthorization
── Type aliases ──
type DelegationChainVerifier ¶
type DelegationChainVerifier struct {
// MaxDepth is the maximum delegation depth allowed by the top Principal (including intermediate Agent B etc.).
MaxDepth int
// MaxChainLength is the hard upper limit to prevent certificate bomb attacks:
// ≤0 means no extra limit (only constrained by MaxDepth).
MaxChainLength int
}
DelegationChainVerifier verifies multi-level delegation chains (Zhang→Scheduler-A→Worker-B→…).
Multi-level delegation reuses the same DelegationAuthorization structure (spec dev-docs/aic/06-delegation-auth.md §Multi-level delegation chain, FUTURE reserved): each Agent's AIC contains a DelegationAuthorization signed by the previous level (delegator) certificate's private key. Verification proceeds bottom-up:
chain[i].AIC.DA signed by chain[i-1].cert → chain[i-1].AIC.DA signed by chain[i-2].cert → … → chain[0].AIC.DA signed by topPrincipal certificate
chainDepth is the number of delegation certificates in the chain (excluding the top Principal); maxDepth is set by the top Principal; exceeding it results in rejection. No new ASN.1 types needed; the entire chain is verifiable offline.
func (*DelegationChainVerifier) Verify ¶
func (v *DelegationChainVerifier) Verify(chain []*x509.Certificate, topPrincipal *x509.Certificate) error
Verify verifies the delegation chain bottom-up starting from workerCert. chain is the certificate list from top to bottom: chain[0]=Scheduler-A (top-level delegating Agent), chain[len-1]=Worker-B (bottom-level Agent). Each level's AIC.DA is signed by the previous level's certificate.
Verification steps:
- Each certificate must contain an AIC with non-empty DA;
- Each level's AIC.DA signer = previous certificate (SPKI hash cross-validation);
- Top-level chain[0].AIC.DA signer = topPrincipal certificate;
- Chain depth (len(chain)) must ≤ MaxDepth;
- Entire chain verified offline, no external service dependency.
type DelegationMode ¶
type DelegationMode = pki.DelegationMode
── Type aliases ──
const ( DelegationAuthorized DelegationMode = 0 DelegationRepresentative DelegationMode = 1 )
DelegationMode values.
type EvidenceAuditChain ¶
type EvidenceAuditChain struct {
Entries []EvidenceAuditEntry `json:"entries,omitempty"`
MerkleRoot string `json:"merkleRoot,omitempty"`
Anchor string `json:"anchor,omitempty"` // self | tsa | externalLog
TSA string `json:"tsa,omitempty"`
}
EvidenceAuditChain is the tamper-evidence chain (v0.1 §3 auditChain).
type EvidenceAuditEntry ¶
type EvidenceAuditEntry struct {
Seq int64 `json:"seq"`
Action string `json:"action"`
Actor string `json:"actor"`
Ts string `json:"ts"`
EntryHash string `json:"entryHash"`
PrevHash string `json:"prevHash"`
}
EvidenceAuditEntry is a chained audit line digest. prevHash of the first entry is empty.
type EvidenceAuthorization ¶
type EvidenceAuthorization struct {
DelegationMode string `json:"delegationMode,omitempty"`
Principal string `json:"principal,omitempty"`
GrantRef *EvidenceGrantRef `json:"grantRef,omitempty"`
Capabilities []EvidenceCapability `json:"capabilities,omitempty"`
Constraints string `json:"constraints,omitempty"`
Ceiling string `json:"ceiling,omitempty"`
ValidFrom string `json:"validFrom,omitempty"`
ExpiresAt string `json:"expiresAt,omitempty"`
}
EvidenceAuthorization records who authorized and the boundary (v0.1 §3 authorization).
type EvidenceBundle ¶
type EvidenceBundle struct {
Manifest EvidenceManifest `json:"manifest"`
Operation EvidenceOperation `json:"operation"`
Subject EvidenceSubject `json:"subject"`
Authorization EvidenceAuthorization `json:"authorization"`
Decision EvidenceDecision `json:"decision"`
Supervision []EvidenceSupervisionEvent `json:"supervision"`
AuditChain EvidenceAuditChain `json:"auditChain"`
Signatures map[string]json.RawMessage `json:"signatures"`
}
EvidenceBundle is the exported evidence package. Field layout follows the v0.1 specification: manifest/operation/subject/authorization/decision/ supervision/auditChain/signatures.
func (*EvidenceBundle) CheckDecisions ¶ added in v0.2.0
func (b *EvidenceBundle) CheckDecisions() error
CheckDecisions verifies that the bundle's decision section agrees with the record it carries (when it carries one). A bundle without a record is not wrong, but it is a report — not replayable evidence.
func (*EvidenceBundle) JSON ¶
func (b *EvidenceBundle) JSON() ([]byte, error)
JSON serializes the bundle as canonical JSON (keys sorted lexicographically at every level, no extraneous whitespace). The output is reproducible and safe to digest.
type EvidenceCapability ¶
type EvidenceCapability struct {
Scheme string `json:"scheme,omitempty"`
CapabilityID string `json:"capabilityId"`
Parameters string `json:"parameters,omitempty"`
}
EvidenceCapability is a capability sub-set entry with parameter boundary summary.
type EvidenceConfig ¶ added in v0.2.0
type EvidenceConfig struct {
// Sink receives every record. Nil uses SlogSink with the SDK logger.
Sink EvidenceSink
// Strict makes an emission failure deny the request (fail-closed). The
// default (false) logs and continues: the evidence layer is opt-in, and a
// broken sink should not silently take a service down — a deployment that
// needs the record more than the request sets this true.
Strict bool
// TTL, when non-zero, pins a RATS §10 explicit-clock freshness input on the
// record (At=now, MaxAgeSec=TTL). Zero records no freshness context.
TTL time.Duration
// Audience names the relying party the evidence is addressed to.
Audience string
// RecorderID identifies this admission point when several of them record
// the same traffic. It is a deployment property, not a language field.
RecorderID string
// Now is injectable for tests.
Now func() time.Time
// OnError is called when emission fails, so a deployment can count or page
// on evidence gaps instead of only finding them in a log. Nil logs at
// error level.
OnError func(ctx EvidenceContext, err error)
// Gaps, when set, counts every emission that failed to leave a record. It
// makes "the evidence layer itself lost evidence" a queryable meter (not a
// log-scan archaeology problem): gap events call OnError for paging and
// increment Gaps for reporting. Nil disables the counter.
Gaps *GapCounter
// Sign, when set, appends a DSSE signature over PAE(payloadType, payload) to
// every emitted envelope — decision, admission and outcome alike. nil
// (.default) leaves records unsigned: content-recomputable, but carrying no
// key that endorses "which admission point issued this". The signing
// primitive is caller-supplied (DSSE deliberately does not pick one); KeyID
// is an unauthenticated hint, never trusted on its own. A signing failure
// follows the existing emission rules: OnError is called, and Strict denies
// the request.
Sign func(pae []byte) ([]byte, error)
KeyID string
// EmitOutcome, when true, makes the reverse proxy report an outcome record
// after every forwarded request (a response → observed + status; a
// transport failure → indeterminate). It is an opt-in effect-side channel:
// classification of executed/failed stays with the deployment. The
// decision, admission and standing evidence budget are unaffected.
EmitOutcome bool
// Requirement, when set, is bound into each record as its requirement
// digest: a record then says *which* sufficiency bar was applied, not just
// what was decided.
Requirement *semantics.Requirement
// Profile is the declared evidence shape (EvidenceProfile). When set, every
// emitted envelope is tagged with the profile's content-addressed identity,
// so a consumer can tell which shape it received.
Profile *EvidenceProfile
// Recorder, when set, is published as an `evidence-recorder` subject: a
// consumer holding the descriptor can tell which admission point emitted a
// record, and one that does not can still tell whether two records came from
// the same recorder. RecorderID alone stays a display hint.
Recorder *RecorderDescriptor
// contains filtered or unexported fields
}
EvidenceConfig turns on record emission at this admission point.
type EvidenceContainer ¶ added in v0.2.0
type EvidenceContainer string
EvidenceContainer names the envelope shape.
const ( // ContainerDSSEInToto is the DSSE JSON envelope carrying an in-toto // Statement. It is the only container implemented today. ContainerDSSEInToto EvidenceContainer = "dsse+in-toto" )
type EvidenceContext ¶ added in v0.2.0
type EvidenceContext struct {
// Facts are the content-addressed facts the outcome rested on; pipeline
// records carry them into the statement's subjects.
Facts []AdmissionFact
RecorderID string
OperationID string
Method string
Path string
TraceID string
Principal string
AgentID string
Serial string
Outcome EvidenceOutcome
At time.Time
}
EvidenceContext is what a sink needs to *use* a record: which request it came from, who it was about, and whether that request was admitted or refused. The record itself carries the decision; this carries the correlation.
type EvidenceDecision ¶
type EvidenceDecision struct {
// Decision mirrors the CLC verdict. When Record is present this field is
// derived from it (allow | allow_unresolved | deny) and MUST NOT disagree:
// a bundle whose summary contradicts its record is not evidence.
Decision string `json:"decision,omitempty"`
// RecordDigest is the record's input digest (hex) — the stable identifier
// of "which decision this bundle is about".
RecordDigest string `json:"recordDigest,omitempty"`
// Record is the CLC Decision Record itself: frozen inputs, verdict and
// residual obligations, independently re-computable by any holder. The
// other fields in this section are a reader's summary of it; the record is
// the authority.
Record *semantics.DecisionRecord `json:"record,omitempty"`
MatchedPolicy string `json:"matchedPolicy,omitempty"`
ReasonCodes []string `json:"reasonCodes,omitempty"`
EvaluatedCapabilities []string `json:"evaluatedCapabilities,omitempty"`
PdpContext string `json:"pdpContext,omitempty"`
}
EvidenceDecision is the PDP/PEP decision record (v0.1 §3 decision).
func (*EvidenceDecision) ApplyRecord ¶ added in v0.2.0
func (d *EvidenceDecision) ApplyRecord(rec *semantics.DecisionRecord) error
ApplyRecord attaches a CLC decision record to this section and derives the summary fields from it. Deriving (rather than trusting the audit text) is what keeps the bundle from carrying a decision of its own.
type EvidenceExporter ¶
type EvidenceExporter interface {
// Export returns a self-contained, canonical-serializable evidence bundle.
Export(ctx context.Context, q EvidenceQuery) (*EvidenceBundle, error)
}
EvidenceExporter produces an evidence bundle (evidence-bundle v0.1) for a query, merging the audit chain with supervision events.
type EvidenceFailure ¶ added in v0.2.0
EvidenceFailure names one file that did not verify and why.
type EvidenceGrantRef ¶
EvidenceGrantRef is a reference + digest to the governing DA.
type EvidenceKind ¶ added in v0.2.0
type EvidenceKind string
EvidenceKind names which payload an envelope carries.
const ( // KindDecision is a language-layer CLC decision record. KindDecision EvidenceKind = "clc-decision" // KindAdmission is a pipeline-level admission record. KindAdmission EvidenceKind = "aic-admission" // KindOutcome is an execution-boundary outcome record. KindOutcome EvidenceKind = "aic-outcome" )
func CheckEvidenceEnvelope ¶ added in v0.2.0
func CheckEvidenceEnvelope(env semantics.Envelope) (EvidenceKind, error)
CheckEvidenceEnvelope validates either payload type through one entry point, dispatching on the statement's predicate type. A CLC record is checked by the language (structure, subject binding, re-computation); a pipeline record is parsed and shape-checked here.
func VerifyEvidenceEnvelope ¶ added in v0.2.0
func VerifyEvidenceEnvelope(env semantics.Envelope, verify func(keyID string, pae, sig []byte) error) (EvidenceKind, error)
VerifyEvidenceEnvelope validates an envelope of any of the three payload kinds. Structural shipping is always performed (dispatched on the statement's predicate type). When verify is non-nil, at least one signature must verify over the DSSE PAE — mirroring Envelope.VerifyRecord's multi-signature rule: the first signature that verifies wins, KeyID is passed through as an unauthenticated hint, and a tampered payload fails because its PAE no longer matches what was signed.
type EvidenceManifest ¶
type EvidenceManifest struct {
Schema string `json:"schema"`
Version string `json:"version"`
BundleID string `json:"bundleId"`
ExportedAt time.Time `json:"exportedAt"`
Generator string `json:"generator"`
HashAlg string `json:"hashAlg"`
PolicyRef string `json:"policyRef,omitempty"`
}
EvidenceManifest is the bundle's own metadata (v0.1 §3 manifest).
type EvidenceOperation ¶
type EvidenceOperation struct {
OperationID string `json:"operationId,omitempty"`
StartedAt string `json:"startedAt,omitempty"`
FinishedAt string `json:"finishedAt,omitempty"`
Resource string `json:"resource,omitempty"`
Action string `json:"action,omitempty"`
RequestedParams Summary `json:"requestedParams,omitempty"`
Outcome string `json:"outcome,omitempty"` // permit | deny | error
}
EvidenceOperation identifies the audited operation and its outcome (v0.1 §3 operation).
type EvidenceOutcome ¶ added in v0.2.0
type EvidenceOutcome string
EvidenceOutcome says what happened to the request the record belongs to.
const ( // EvidenceAdmitted means the request was admitted. EvidenceAdmitted EvidenceOutcome = "admitted" // EvidenceRefused means the request was refused (the record says why). EvidenceRefused EvidenceOutcome = "refused" )
type EvidenceProfile ¶ added in v0.2.0
type EvidenceProfile struct {
// Name is the profile identifier, e.g. "clc-decision+admission+outcome@1".
Name string
// Container is the envelope shape.
Container EvidenceContainer
// Decision emits CLC decision records (the language layer).
Decision bool
// Admission emits pipeline-level records for refusals that never reached the
// language layer.
Admission bool
// Outcome allows an execution-boundary sink to report effect records.
Outcome bool
// Sources includes the authorization source chain in decision records.
Sources bool
// Freshness pins a RATS §10 explicit clock on each record for this duration
// (0 = no context).
Freshness time.Duration
// Requirement binds the relying party's sufficiency bar into each record.
Requirement *semantics.Requirement
}
EvidenceProfile declares which evidence a deployment emits.
func LookupEvidenceProfile ¶ added in v0.2.0
func LookupEvidenceProfile(name string) (EvidenceProfile, error)
LookupEvidenceProfile resolves a named profile. An unknown name is an error: a deployment that asks for a shape we do not produce must hear about it at configuration time, not discover it later from missing records.
func (EvidenceProfile) Apply ¶ added in v0.2.0
func (p EvidenceProfile) Apply(cfg *EvidenceConfig) (*EvidenceConfig, error)
Apply fills an EvidenceConfig from the profile. The plumbing (sink, strict, error hook, recorder id) stays with the deployment; the shape comes from here.
func (EvidenceProfile) ID ¶ added in v0.2.0
func (p EvidenceProfile) ID() (semantics.Digest, error)
ID is the profile's identity, content-addressed over everything that shapes the emitted bytes. Two deployments that emit the same shape share an ID.
func (EvidenceProfile) Validate ¶ added in v0.2.0
func (p EvidenceProfile) Validate() error
Validate checks the profile's own consistency.
type EvidenceQuery ¶
type EvidenceQuery struct {
// OperationID is the preferred correlation key (server-side request id).
OperationID string
// DaHash correlates by delegation authorization fingerprint.
DaHash string
// AgentID correlates by agent identifier.
AgentID string
// TimeRange bounds the export window. Nil means no time bound.
TimeRange *TimeRange
// IncludeSupervision merges supervision events into the bundle (default
// false keeps the bundle audit-only).
IncludeSupervision bool
// Record is the CLC decision record this bundle is about. When set, it is
// attached to the bundle's decision section as the authority, and the
// summary fields (Decision, ReasonCodes) are derived from it rather than
// from the audit text. Callers obtain it from the evidence sink (see
// LoadEvidenceRecord).
Record *semantics.DecisionRecord
}
EvidenceQuery selects the evidence to export for post-operation attribution.
type EvidenceSink ¶ added in v0.2.0
type EvidenceSink interface {
// Emit receives a language-layer decision record.
Emit(ctx EvidenceContext, rec semantics.DecisionRecord, env semantics.Envelope) (RecordRef, error)
// EmitAdmission receives a pipeline-level record: a refusal that happened
// before the language layer (credential, chain, revocation, missing
// capability). It is a separate method because it is a separate payload
// type — a sink that only understands decisions can still implement it as
// "store the envelope and move on".
EmitAdmission(ctx EvidenceContext, rec AdmissionRecord, env semantics.Envelope) (RecordRef, error)
}
EvidenceSink receives a frozen decision record and its transport envelope, and reports where it kept it. Implementations must be safe for concurrent use.
type EvidenceSubject ¶
type EvidenceSubject struct {
AgentID string `json:"agentId,omitempty"`
KeyBinding string `json:"keyBinding,omitempty"`
TransportIdentity string `json:"transportIdentity,omitempty"`
PresentedCredentialRef string `json:"presentedCredentialRef,omitempty"`
}
EvidenceSubject records who executed (v0.1 §3 subject).
type EvidenceSupervisionEvent ¶
type EvidenceSupervisionEvent struct {
Type string `json:"type"` // consent | denied | stepUp | approval | breakGlass | override
OccurredAt string `json:"occurredAt"`
Actor string `json:"actor"`
DecisionRef string `json:"decisionRef,omitempty"`
EvidenceRef string `json:"evidenceRef,omitempty"`
}
EvidenceSupervisionEvent is one human-intervention event (v0.1 §3 supervision). decisionRef mirrors the shared operation_id correlation key.
type EvidenceVerifyReport ¶ added in v0.2.0
type EvidenceVerifyReport struct {
// Total is the number of record envelopes scanned.
Total int
// Decision / Admission / Outcome are the per-kind record counts.
Decision int
Admission int
Outcome int
// Failures lists every file that did not verify: malformed JSON, an
// envelope of an unknown predicate type, a decision that no longer
// recomputes, or (when verify was supplied) a record with no valid
// signature. Empty means the directory verified clean.
Failures []EvidenceFailure
}
EvidenceVerifyReport is the aggregated result of VerifyEvidenceDir.
func VerifyEvidenceDir ¶ added in v0.2.0
func VerifyEvidenceDir(dir string, verify func(keyID string, pae, sig []byte) error) (EvidenceVerifyReport, error)
VerifyEvidenceDir scans dir for evidence record envelopes and verifies each one, dispatching on its statement's predicate type: a CLC decision must re-compute, and an admission / outcome record must parse and be shape-invariant (see CheckEvidenceEnvelope). When verify is non-nil, every envelope must also carry at least one signature that verifies over its DSSE PAE (see VerifyEvidenceEnvelope). A deployment that already trusts the emission point's key passes VerifyFnFromPublicKey here and the key question is settled without writing crypto.
The scan is best-effort per file: a broken or unrecognized file is collected in Failures, not returned as an error — an evidence directory with one bad record must be auditable, not unreadable. Only a failure to read the directory itself surfaces as (Report{}, err). A directory without records verifies clean (all-zero report).
type FileEvidenceExporter ¶
type FileEvidenceExporter struct {
// AuditFile is the audit JSON Lines file (SignedAuditEntry stream).
AuditFile string
// SupervisionFile is the supervision event JSON Lines file. Empty means
// supervision is unavailable; querying with IncludeSupervision then yields
// no supervision events.
SupervisionFile string
// EvidenceDir is the evidence record directory (the FileSink directory).
// When set and no Record was passed in the query, the exporter resolves the
// anchor from the audit chain (record_digest), loads the decision record
// envelope by digest and attaches it to the bundle. A digest that resolves
// to nothing is a gap: the export fails instead of silently degrading to a
// report-only bundle.
EvidenceDir string
// Generator is the bundle.generator label; empty defaults to the package
// version.
Generator string
// Now overrides the export clock (tests).
Now func() time.Time
}
FileEvidenceExporter is the built-in minimal exporter: it reads the audit chain (SignedAuditEntry JSONL) and optionally the supervision event store, filters them by EvidenceQuery, and builds an evidence-bundle v0.1 with a Merkle root over the exported line digests.
func NewFileEvidenceExporter ¶
func NewFileEvidenceExporter(auditFile, supervisionFile string) *FileEvidenceExporter
NewFileEvidenceExporter builds the built-in exporter over the given audit and supervision JSONL files.
func (*FileEvidenceExporter) Export ¶
func (e *FileEvidenceExporter) Export(ctx context.Context, q EvidenceQuery) (*EvidenceBundle, error)
Export implements EvidenceExporter.
type FileSink ¶ added in v0.2.0
type FileSink struct {
Dir string
// RecorderID is prefixed to the file name when set.
RecorderID string
// contains filtered or unexported fields
}
FileSink writes one DSSE envelope per record, named by its input digest so the same decision recorded twice overwrites itself instead of accumulating.
func (*FileSink) Emit ¶ added in v0.2.0
func (s *FileSink) Emit(ctx EvidenceContext, rec semantics.DecisionRecord, env semantics.Envelope) (RecordRef, error)
Emit implements EvidenceSink.
func (*FileSink) EmitAdmission ¶ added in v0.2.0
func (s *FileSink) EmitAdmission(ctx EvidenceContext, rec AdmissionRecord, env semantics.Envelope) (RecordRef, error)
EmitAdmission implements EvidenceSink for pipeline-level records; the file name carries an "admission-" marker so the two payload types never collide.
func (*FileSink) EmitOutcome ¶ added in v0.2.0
func (s *FileSink) EmitOutcome(ctx EvidenceContext, rec OutcomeRecord, env semantics.Envelope) (RecordRef, error)
EmitOutcome implements OutcomeSink for pipelines that want every artifact on disk; the file name carries an "outcome-" marker so the payload types never collide.
type GapCounter ¶ added in v0.2.0
type GapCounter struct {
// contains filtered or unexported fields
}
GapCounter is an atomically counted evidence-gap meter: how often an emission that was supposed to leave a record failed to do so. Safe for concurrent use from any number of admission points.
func (*GapCounter) Count ¶ added in v0.2.0
func (g *GapCounter) Count() int64
Count returns how many emissions failed to leave a record since the counter was created.
func (*GapCounter) Inc ¶ added in v0.2.0
func (g *GapCounter) Inc()
Inc registers one failed emission.
type GeoResolver ¶
GeoResolver resolves a geographic region identifier (e.g. "CN-SHA") from a source IP. Registered with the geo-fence evaluator to make region resolution pluggable (built-in inline table; third-party databases like ip2region can self-register).
type Hooks ¶
type Hooks struct {
// Authenticated runs on every admitted request, before the upstream handler
// (middleware style) or backend proxy (proxy style) is invoked. Returning a
// non-nil error denies the request with 403 and the error text is logged.
Authenticated func(ctx *AuthContext, r *http.Request) error
// Denied runs when a request is rejected by the admission pipeline. err is
// the typed *AuthError carrying the error code and HTTP status.
Denied func(r *http.Request, err *AuthError)
// Forwarded runs after an admitted request returned from the backend (proxy
// style only). resp is non-nil when the upstream responded; it is nil when
// the reverse proxy failed before a response was produced.
Forwarded func(r *http.Request, resp *http.Response)
}
Hooks are optional lifecycle callbacks a service operator can plug into the request pipeline. Every hook is invoked synchronously; nil hooks are skipped. This is the reserved extension point for callers that need to observe or veto AIC decisions without writing a full capability plugin.
type IdentityHeaderMode ¶
type IdentityHeaderMode int
IdentityHeaderMode selects how much identity detail is forwarded to backends.
const ( // IdentityMinimal strips every identity header (backend trusts the proxy only). IdentityMinimal IdentityHeaderMode = iota // IdentityForwardClientCert forwards the full client certificate (DER) plus // the X-AIC-* structured views. IdentityForwardClientCert // IdentityXForwarded sets only X-Forwarded-Client-* (CN/O/OU/serial). IdentityXForwarded // IdentityAIC only sets the X-AIC-* structured headers. IdentityAIC )
type JWTVerifier ¶
type JWTVerifier struct {
// contains filtered or unexported fields
}
JWTVerifier verifies AIC-JWT bearer tokens against a trust root built from CA certificates (same kid convention as the X.509 carrier: base64url SHA-256 of the certificate SPKI). On success it returns a synthesized X.509 certificate carrying the token's AIC extension, so the existing pipeline (RunAccessPipeline / CheckAdmission) admits a bearer request exactly like a certificate-authenticated one.
A bare NewJWTVerifier only verifies the token signature/expiry. For production use the gateway must call SetBearerPolicy (issuer/audience binding + replay protection) and pass per-request proof-of-possession via VerifyBearer options; without those the bearer is replayable until exp by any holder (finding 5).
func LoadJWTVerifier ¶
func LoadJWTVerifier(caFiles ...string) (*JWTVerifier, error)
LoadJWTVerifier reads one or more PEM CA certificate files (comma or space separated paths) and builds a JWT verifier from them. An empty spec returns a nil verifier (bearer auth disabled).
func NewJWTVerifier ¶
func NewJWTVerifier(cas []*x509.Certificate) *JWTVerifier
NewJWTVerifier builds a verifier from CA certificates. kid for each CA is base64url(SHA-256(SubjectPublicKeyInfo)) — the same binding core publishes on /.well-known/jwks.json.
func (*JWTVerifier) SetBearerPolicy ¶
func (v *JWTVerifier) SetBearerPolicy(expectedIssuer string, expectedAudience []string, nonces aicjwt.NonceStore)
SetBearerPolicy installs the static bearer-token policy applied to every verification: the expected issuer, acceptable audiences, and a replay nonce store. Configure all three for production; leaving issuer/audience empty or the nonce store nil keeps those checks off (finding 5).
func (*JWTVerifier) VerifyBearer ¶
func (v *JWTVerifier) VerifyBearer(token string, now time.Time, opts ...JWTVerifyOptions) (*x509.Certificate, *aicjwt.OuterClaims, error)
VerifyBearer validates a Bearer AIC-JWT and returns a synthesized certificate carrying the AIC claims, plus the raw outer claims. opts carry per-request checks (proof-of-possession, revocation); the verifier's static policy (issuer/audience/replay) is always applied.
type JWTVerifyOptions ¶
type JWTVerifyOptions struct {
// ExpectedIssuer, when non-empty, requires outer.iss == ExpectedIssuer.
ExpectedIssuer string
// ExpectedAudience, when non-empty, requires the token aud to include one.
ExpectedAudience []string
// PresenterKey, when non-nil, enforces cnf proof-of-possession: the token
// must be bound to this public key (e.g. the mTLS peer cert key).
PresenterKey crypto.PublicKey
// NonceStore, when non-nil, provides one-time-use replay protection on the
// DA nonce (finding 5).
NonceStore aicjwt.NonceStore
// RequireJtiNonceMatch requires outer.jti == DA nonce.
RequireJtiNonceMatch bool
// StatusChecker, when non-nil, checks issuer/principal for revocation.
StatusChecker aicjwt.StatusChecker
}
JWTVerifyOptions carries the runtime verification parameters for a single bearer token. These activate the checks Validate supports but that a bare call leaves unset (finding 5).
type Layer1Result ¶
Layer1Result is the Layer 1 identity verification result (Agent Cert + CA Chain + RBAC).
func VerifyLayer1 ¶
func VerifyLayer1(chain []*x509.Certificate, cfg *PipelineConfig) *Layer1Result
VerifyLayer1 performs Layer 1 identity verification: certificate chain validity (validity period) + RBAC roles (AllowRoles matching). Cryptographic certificate chain verification is done by the TLS layer (VerifyPeerCertificate); this layer performs application-level identity checks (validity period + roles). Returns roles for use by subsequent layers.
type Layer2Result ¶
type Layer2Result struct {
Verified bool
Reason string
AIC *AIC
PrincipalAuthorization *PrincipalAuthorization
PrincipalUid string
}
Layer2Result is the Layer 2 representation verification result (+ Principal Cert + PA).
type Layer3Result ¶
Layer3Result is the Layer 3 online authorization verification result (OCSP/CRL/Policy Server).
func VerifyLayer3 ¶
func VerifyLayer3(chain []*x509.Certificate, cfg *PipelineConfig) *Layer3Result
VerifyLayer3 performs Layer 3 online authorization verification: CRL/OCSP revocation freshness check + optional PolicyServer online policy check.
type MerkleTree ¶
type MerkleTree struct {
// contains filtered or unexported fields
}
MerkleTree is a Merkle hash tree used for tamper-proof audit chains.
func NewMerkleTree ¶
func NewMerkleTree(leaves [][]byte) *MerkleTree
NewMerkleTree creates a Merkle tree from leaf data.
func (*MerkleTree) Proof ¶
func (m *MerkleTree) Proof(leafIndex int) ([]ProofStep, error)
Proof computes the audit proof path for a given leaf index.
func (*MerkleTree) Root ¶
func (m *MerkleTree) Root() []byte
Root returns the Merkle tree root hash.
func (*MerkleTree) RootHex ¶
func (m *MerkleTree) RootHex() string
RootHex returns the root hash as a hex-encoded string.
type MessageImprint ¶
type MessageImprint struct {
HashAlgorithm AlgorithmIdentifier
HashedMessage []byte
}
MessageImprint is a message digest imprint.
type NonceCache ¶
type NonceCache struct {
// contains filtered or unexported fields
}
NonceCache provides a nonce replay protection cache (v1.4 §3.2). Thread-safe with automatic cleanup of expired entries.
func NewNonceCache ¶
func NewNonceCache() *NonceCache
NewNonceCache creates a NonceCache and starts automatic cleanup (hourly, retaining entries within 24h).
func (*NonceCache) CheckAndAdd ¶
func (nc *NonceCache) CheckAndAdd(scope string, nonce []byte) bool
CheckAndAdd checks whether a nonce has been replayed by a different certificate (DA replay attack detection). scope is the certificate identity (issuer/serial), used to distinguish "same cert replaying the same nonce" (normal) from "DA evidence copied into a different cert" (attack). Returns true to allow.
func (*NonceCache) Len ¶
func (nc *NonceCache) Len() int
Len returns the current number of nonces in the cache (for testing and monitoring only).
type OCSPCache ¶
type OCSPCache struct {
// contains filtered or unexported fields
}
OCSPCache is an OCSP response cache with request coalescing.
func NewOCSPCache ¶
func NewOCSPCache(ttl time.Duration, fallback string, translator Translator, lang string) *OCSPCache
NewOCSPCache creates an OCSP cache instance.
func (*OCSPCache) Check ¶
func (c *OCSPCache) Check(cert, issuer *x509.Certificate) error
Check queries the OCSP responder and caches the result.
func (*OCSPCache) SetCRLChecker ¶
func (c *OCSPCache) SetCRLChecker(fn CRLRevokedFunc)
SetCRLChecker installs the CRL revocation lookup used by the "crl" OCSP fallback. It must be set for OCSPFallbackCRL to fail closed instead of silently allowing (finding 3).
type OfflineRBAC ¶
type OfflineRBAC struct {
// contains filtered or unexported fields
}
OfflineRBAC provides offline RBAC decision capability for degraded scenarios where the core is unreachable. Injected with a role list extracted from certificates or configuration at construction time via NewOfflineRBAC.
func NewOfflineRBAC ¶
func NewOfflineRBAC(roles []string) *OfflineRBAC
NewOfflineRBAC creates an offline RBAC instance storing the specified roles.
func NewOfflineRBACFromCert ¶
func NewOfflineRBACFromCert(cert *x509.Certificate) *OfflineRBAC
NewOfflineRBACFromCert creates an offline RBAC instance by extracting roles from a certificate's OU.
func (*OfflineRBAC) CheckRole ¶
func (r *OfflineRBAC) CheckRole(allowed []string) bool
CheckRole checks whether roles contains at least one role from allowed. If roles contains gateway:*, it passes immediately. Supports gateway:* wildcard in allowed.
type Operation ¶
Operation is a concrete action to authorize: a capability id plus the parameters the caller wants to use.
type OperationDecision ¶
type OperationDecision struct {
ID string `json:"id"`
Params map[string]any `json:"params,omitempty"`
Verdict string `json:"verdict"` // semantics.VerdictAllow / VerdictDeny / VerdictAllowUR
Reason string `json:"reason,omitempty"`
Unresolved []string `json:"unresolved,omitempty"`
// Grants are the effective grant set the operation was decided over: the
// AIC capability set on the delegated path, the principal's own grants on
// the direct path, each already carrying the connection's authorization
// constraints. Surfacing them here means a downstream consumer can recompute
// the verdict without parsing an evidence record file.
Grants []semantics.Grant `json:"grants,omitempty"`
// Released is true when an allow_unresolved operation was admitted because
// the deployment's UnresolvedEvaluator confirmed the residual obligations.
Released bool `json:"released,omitempty"`
}
OperationDecision records the CLC verdict for one requested operation, so admission results and the AuthContext can expose verdict / reason / unresolved (spec B3) instead of only the final allow/deny.
type OutcomeRecord ¶ added in v0.2.0
type OutcomeRecord struct {
Ver string `json:"ver"`
Outcome string `json:"outcome"`
At time.Time `json:"at"`
// DecisionDigest points back at the decision record this outcome followed
// (hex of that record's input digest). Empty means the linkage was not
// established — which a consumer should treat as a gap, not as consent.
DecisionDigest string `json:"decisionDigest,omitempty"`
// OperationID is the action this outcome is about.
OperationID string `json:"operationId,omitempty"`
// RecorderID identifies the execution boundary that reported it.
RecorderID string `json:"recorderId,omitempty"`
// StatusCode is the transport-level result when there was one (HTTP).
StatusCode int `json:"statusCode,omitempty"`
// Note is a bounded, deployment-supplied description.
Note string `json:"note,omitempty"`
// Identity is who executed it, as the boundary established it.
Identity AdmissionIdentity `json:"identity,omitempty"`
// Facts are the digests of the material the outcome rests on.
Facts []AdmissionFact `json:"facts,omitempty"`
}
OutcomeRecord says what the execution boundary observed, and which decision it followed. It is evidence about the effect, not about authority.
func ParseOutcomeEnvelope ¶ added in v0.2.0
func ParseOutcomeEnvelope(env semantics.Envelope) (OutcomeRecord, error)
ParseOutcomeEnvelope decodes and shape-checks an outcome record.
type OutcomeSink ¶ added in v0.2.0
type OutcomeSink interface {
EmitOutcome(ctx EvidenceContext, rec OutcomeRecord, env semantics.Envelope) (RecordRef, error)
}
OutcomeSink receives execution-boundary records. A deployment that has no execution boundary simply does not configure one.
type OverrideRecorder ¶
type OverrideRecorder interface {
// Record persists a break-glass / override event. It does not authorize
// the operation by itself.
Record(ctx context.Context, risk RiskAssessment, actor, reason string) (*SupervisionResult, error)
}
OverrideRecorder is the break-glass / override recording point. The SDK only provides the record point: whether break-glass is allowed is the operator's policy (SupervisionPolicy.AllowBreakGlass). When policy allows break-glass and the recorder is nil, startup validation fails, so an unlogged break-glass is never available.
type PKIStatusInfo ¶
type PKIStatusInfo struct {
Status int
}
PKIStatusInfo is PKI status information.
type ParameterValidator ¶
type ParameterValidator interface {
// Scheme returns the schemeId associated with this validator.
Scheme() string
// Validate checks whether declared (agent/certificate declaration) falls within
// granted (principal authorization boundary). Returns a non-nil error if out of bounds
// (includes the specific parameter name and boundary).
Validate(granted, declared Capability) error
}
ParameterValidator validates whether a capability declaration's parameters fall within the authorized boundary. Implemented per schemeId (one Scheme → one validator).
var MaxRowsValidator ParameterValidator = maxRowsValidator{}
MaxRowsValidator is an exported instance of the max_rows parameter boundary validator (for registration: NewParameterValidatorRegistry().Register(MaxRowsValidator)).
type ParameterValidatorRegistry ¶
type ParameterValidatorRegistry struct {
// contains filtered or unexported fields
}
ParameterValidatorRegistry manages registration and lookup of parameter boundary validators.
func BuiltinParameterValidators ¶
func BuiltinParameterValidators() *ParameterValidatorRegistry
BuiltinParameterValidators returns the built-in parameter boundary validator registry.
func NewParameterValidatorRegistry ¶
func NewParameterValidatorRegistry() *ParameterValidatorRegistry
NewParameterValidatorRegistry creates an empty parameter boundary validator registry.
func (*ParameterValidatorRegistry) Find ¶
func (r *ParameterValidatorRegistry) Find(schemeID string) (ParameterValidator, error)
Find looks up the parameter boundary validator for the given schemeId.
func (*ParameterValidatorRegistry) Keys ¶
func (r *ParameterValidatorRegistry) Keys() []string
Keys returns all registered scheme IDs.
func (*ParameterValidatorRegistry) Len ¶
func (r *ParameterValidatorRegistry) Len() int
Len returns the number of registered validators.
func (*ParameterValidatorRegistry) Register ¶
func (r *ParameterValidatorRegistry) Register(v ParameterValidator) error
Register registers a parameter boundary validator.
func (*ParameterValidatorRegistry) Reset ¶
func (r *ParameterValidatorRegistry) Reset()
Reset clears the registry.
func (*ParameterValidatorRegistry) ValidateCapability ¶
func (r *ParameterValidatorRegistry) ValidateCapability(granted, declared Capability) error
ValidateCapability validates a capability declaration against the authorized boundary using the given registry. Unregistered schemes are allowed (no parameter boundary rules; capability-level intersection was already performed upstream).
type PermissionLevel ¶
type PermissionLevel = pki.PermissionLevel
── Type aliases ──
const ( PermissionAuto PermissionLevel = 0 PermissionRequiresApproval PermissionLevel = 1 )
PermissionLevel constants.
type PipelineCheck ¶
type PipelineCheck int
PipelineCheck is the certificate chain check scope type.
const ( CheckFullChain PipelineCheck = iota CheckLeafOnly )
CheckFullChain/CheckLeafOnly are certificate chain check scope constants.
type PipelineConfig ¶
type PipelineConfig struct {
// CRLCache is the CRL cache instance.
CRLCache *CRLCache
// OCSPCache is the OCSP cache instance.
OCSPCache *OCSPCache
// AllowRoles is the list of allowed RBAC roles.
AllowRoles []string
// CheckScope controls the CA scope check mode.
CheckScope PipelineCheck
// MaxConnsPerCert is the maximum connections per certificate.
MaxConnsPerCert int
// RequireAIC requires the client to hold an AIC certificate.
RequireAIC bool
// RequireSPIFFE requires the client certificate to carry a SPIFFE ID
// SAN URI. When set, connections without a SPIFFE ID are rejected.
RequireSPIFFE bool
// AllowedSPIFFEIDs is an optional exact-match allowlist of SPIFFE IDs.
// Empty means no allowlist restriction.
AllowedSPIFFEIDs []string
// SPIFFETrustDomain when non-empty requires the client SPIFFE ID to
// belong to this trust domain (e.g. "varwof.com").
SPIFFETrustDomain string
// RequiredProtocol is the transport protocol required by the client.
RequiredProtocol string
// RequiredRuleId is the required matching route rule ID.
RequiredRuleId string
// RequiredCapabilities is the list of capabilities the client must possess.
RequiredCapabilities []string
// Operations are the concrete actions (capability id + parameters) this
// request wants to perform. When set, each one is decided with the CLC
// core against the effective authority — the AIC capabilities intersected
// with the PrincipalAuthorization grants — so parameter bounds take part in
// the decision instead of matching capability ids alone.
Operations []Operation
// UnresolvedEvaluator is the §8.4 residual-obligation release hook for
// allow_unresolved CLC decisions; forwarded to AdmissionConfig (see
// AdmissionConfig.UnresolvedEvaluator).
UnresolvedEvaluator func(op Operation, unresolved []string) bool
// DischargeObligations / ObligationsUnderstood enable the strict
// consumer-side obligation rule; forwarded to AdmissionConfig (see
// AdmissionConfig.DischargeObligations).
DischargeObligations bool
ObligationsUnderstood []string
// RequireFreshDecisionContext / DecisionContext pin the RATS §10 freshness
// input; forwarded to AdmissionConfig (see
// AdmissionConfig.RequireFreshDecisionContext).
RequireFreshDecisionContext bool
DecisionContext *semantics.DecisionContext
// DisallowRepresentative disallows delegated representative mode.
DisallowRepresentative bool
// RequireUserPermission requires user authorization signature.
RequireUserPermission bool
// RejectOverflow rejects when connection limit is exceeded.
RejectOverflow bool
// RequireUserAuth requires user authentication.
RequireUserAuth bool
// EnforceCapSizeConstraints enforces capability size constraints.
EnforceCapSizeConstraints bool
// EnforceSize32 enforces the 32-byte size constraint.
EnforceSize32 bool
// CapabilityPluginRegistry is the capability plugin registry.
CapabilityPluginRegistry *PluginRegistry
// CapabilityRegistry is the capability registration validation (single source of truth).
// When non-nil, phase one performs registration validation on AIC-declared capabilities:
// unregistered → reject connection.
// nil means disabled (backward compatible).
CapabilityRegistry CapabilityRegistry
// CapabilityPluginResolver selects a capability plugin registry by agent identifier
// (task 5b: branch control/canary).
// When non-nil, it takes precedence over CapabilityPluginRegistry: the returned registry
// is used for phase one plugin evaluation, and the returned version number (if non-zero)
// overrides PolicyVersion for audit binding. Agents matching a branch use the branch
// version policy; others fall back to the currently active version.
CapabilityPluginResolver func(agentID string) (version uint64, reg *PluginRegistry)
// AuditLogger is the audit log recorder.
AuditLogger *AuditLogger
// NonceCache is the anti-replay nonce cache.
NonceCache *NonceCache
// ClientIP is the client IP address for authorization constraint checks.
ClientIP string
// UserCert is the authorized user's certificate, used for DelegationAuthorization
// signature verification.
UserCert *x509.Certificate
// UserCertResolver resolves a user certificate by PrincipalUid.KeyHash
// (fetched via varwof-core API). Automatically called when UserCert is nil.
UserCertResolver func(keyHash []byte) (*x509.Certificate, error)
// EnforceConstraints, when true, enforces authorizationConstraints.
EnforceConstraints bool
// StrictConstraints, when true, fails-closed on unknown constraint types.
StrictConstraints bool
// ParameterValidators is the parameter boundary validator registry.
// When non-nil, after the P∩C intersection, parameters of AIC declarations and PA
// authorizations are compared one by one against the boundary; out-of-bounds → reject.
ParameterValidators *ParameterValidatorRegistry
// PolicyServer is the Layer 3 online authorization policy server.
// Called by VerifyLayer3/VerifyTrustLayers; not used by RunAccessPipeline.
PolicyServer PolicyServer
// CredentialBundle is the client-submitted credential bundle (agent, principal and CA chains).
// When RequireUserAuth is enabled, the Principal certificate is preferentially extracted
// from the credential bundle for DA signature verification.
CredentialBundle *CredentialBundle
// PolicyVersion is the policy version effective at decision time (task 5a: decision
// records bound to policy version).
// Filled by the gateway from PolicyManager.CurrentVersion() when constructing PipelineConfig;
// when 0, the audit entry omits this field.
PolicyVersion uint64
// RiskMonitor is the high-risk behavior monitor (2026-08-15). When non-nil, the pipeline
// automatically records violation signals at behavioral rejection points (parameter overflow /
// plugin deny / CIDR out-of-bounds); once a rule threshold is reached, the gateway's
// injected OnAction callback executes kick + revocation.
RiskMonitor *RiskMonitor
// OfflineMaxCertLifetime is the maximum remaining certificate validity enforced in offline
// mode (G2(b)).
// When >0 (e.g., 1h): revocation checks use fail-open (OCSP fallback=allow / CRL unreachable)
// in offline scenarios; client certificates with remaining validity exceeding this value are
// rejected — prevents offline fail-open windows from diluting "short-lived certificate"
// semantics with long-lived certificates. 0 = not enforced.
OfflineMaxCertLifetime time.Duration
// HTTPFacts carries per-request HTTP facts (method/path/query/
// headers) that are copied into the PluginContext for capability
// plugins. nil means no HTTP facts (TCP/TLS admission path).
HTTPFacts *HTTPFacts
}
PipelineConfig is the unified admission pipeline configuration.
type PipelineResult ¶
type PipelineResult struct {
Granted bool
DenyReason string
Roles []string
Principal string
Serial string
AgentId string
// SPIFFEID is the SPIFFE ID extracted from the client certificate SAN
// URI (empty when the certificate carries no SPIFFE URI).
SPIFFEID string
// AIC is the AIC extension carried by the admitted connection (parsed result). G3 long-lived
// connection periodic review requires its AuthorizationConstraints.
AIC *AIC
// PrincipalAuthorization is the principal authorization extension carried by the connection
// (source of the P∩C intersection).
PrincipalAuthorization *PrincipalAuthorization
// CLCVerdict / CLCReason / CLCUnresolved aggregate the per-operation CLC
// decisions when cfg.Operations was set: "allow" when every requested
// operation was authorized outright, "allow_unresolved" when one or more
// carried §8.4 residual obligations released by the deployment's
// UnresolvedEvaluator. Empty when no operations were configured.
CLCVerdict string
CLCReason string
CLCUnresolved []string
// OperationDecisions is the per-operation CLC verdict detail (B3).
OperationDecisions []OperationDecision
}
PipelineResult is the admission pipeline execution result.
func RunAccessPipeline ¶
func RunAccessPipeline(chain []*x509.Certificate, cfg *PipelineConfig) *PipelineResult
RunAccessPipeline executes the unified admission pipeline (CRL→OCSP→RBAC→decision engine).
func VerifyTrustLayers ¶
func VerifyTrustLayers(chain []*x509.Certificate, cfg *PipelineConfig) *PipelineResult
VerifyTrustLayers executes three-layer trust verification in combination (L1 → L2 → L3), equivalent to the explicit layered entry point of RunAccessPipeline. Returns denial on any layer failure.
type PluginAuditEntry ¶
type PluginAuditEntry struct {
Scheme string `json:"scheme"`
CapabilityID string `json:"capability_id"`
Decision string `json:"decision"`
Reason string `json:"reason"`
ClientCN string `json:"client_cn,omitempty"`
Principal string `json:"principal,omitempty"`
// Level is the audit level (INFO/WARN). Allow→INFO, deny/execution error→WARN.
// Empty value defaults to INFO.
Level string `json:"level,omitempty"`
// DaHash is the SHA-256 hash of the DelegationAuthorization signatureValue
// (Task 4: plugin decision entries also bind authorization evidence fingerprints).
DaHash string `json:"da_hash,omitempty"`
// PolicyVersion is the policy version effective at decision time (Task 5a).
PolicyVersion uint64 `json:"policy_version,omitempty"`
}
PluginAuditEntry records audit fields for plugin decision events.
type PluginContext ¶
type PluginContext = pki.PluginContext
PluginContext is the context during plugin execution.
type PluginDecision ¶
type PluginDecision = pki.PluginDecision
PluginDecision represents the decision result after plugin execution.
const ( PluginAllow PluginDecision = pki.PluginAllow PluginDeny PluginDecision = pki.PluginDeny PluginBypass PluginDecision = pki.PluginBypass )
PluginAllow/Deny/Bypass are plugin decision constants.
type PluginRegistry ¶
type PluginRegistry = pki.PluginRegistry
PluginRegistry manages plugin registration and lookup.
func NewPluginRegistry ¶
func NewPluginRegistry() *PluginRegistry
NewPluginRegistry creates a new empty registry.
type PluginResult ¶
type PluginResult = pki.PluginResult
PluginResult is the return result after plugin execution.
func CheckOperationCapability ¶
func CheckOperationCapability(reg *PluginRegistry, cap *Capability, ctx *PluginContext) (*PluginResult, error)
CheckOperationCapability executes phase two (operation layer) plugin decisions. Called by the gateway before processing a specific operation (e.g., HTTP route, TCP tunnel, UDP target), making decisions only for the capability corresponding to that operation:
- Operation scheme has no plugin → fail-closed reject (gateway declares service but no decision rule configured; cannot allow uncontrolled operations);
- Plugin deny → reject;
- Plugin allow/bypass → allow.
Complementary to phase one (P∩C intersection + scheme alignment inside RunAccessPipeline): phase one ignores unrelated schemes to allow connections; phase two is fail-closed for operations that will actually be executed.
Returns (PluginResult, error): error indicates only internal execution errors (e.g., webhook call failure); Decision==PluginDeny means the operation is rejected, and the caller should block the operation and record an audit entry accordingly.
func ExecutePlugin ¶
func ExecutePlugin(schemeID string, cap *pki.Capability, ctx *PluginContext) (*PluginResult, error)
ExecutePlugin is a convenience wrapper for findPlugin + Execute.
type PolicyNamespace ¶
type PolicyNamespace struct {
DisplayName string `json:"display_name"`
Prefix string `json:"prefix"`
Grants []string `json:"grants"`
}
PolicyNamespace defines the grants for a gateway namespace.
type PolicyRole ¶
type PolicyRole struct {
DisplayName string `json:"display_name"`
Profiles []string `json:"profiles"`
Grants []string `json:"grants"`
Scope []string `json:"scope,omitempty"`
}
PolicyRole defines a single role's display name, profiles, grants, and scope.
type PolicyServer ¶
type PolicyServer interface {
// Name returns the policy server name (for auditing).
Name() string
// CheckOnline checks online authorization (leaf certificate + parsed AIC).
// Returns nil for authorization granted; non-nil for denial (with reason).
CheckOnline(leaf *x509.Certificate, aic *AIC) error
}
PolicyServer is the Layer 3 online authorization policy server interface (Layer 3). Online authorization verification includes revocation freshness (OCSP/CRL, handled by PipelineConfig's cache instances) and policy server policy checks (if configured).
type PolicySigningConfig ¶
type PolicySigningConfig struct {
// Enabled enables policy signature verification.
Enabled bool `json:"enabled,omitempty"`
// CAFile is the trusted CA chain PEM (defaults to tls_client_ca).
CAFile string `json:"ca_file,omitempty"`
// RequireAdminOU requires the signer to have admin OU (nil=defaults to true).
RequireAdminOU *bool `json:"require_admin_ou,omitempty"`
// Require: true=reject if signature is missing; false=degrade with warning if missing.
Require bool `json:"require,omitempty"`
// SigSuffix is the signature file suffix (default ".sig").
SigSuffix string `json:"sig_suffix,omitempty"`
}
PolicySigningConfig configures PKCS#7 detached signature verification for the gateway authz.json policy file. Structurally identical to varwof-core's policy_signing configuration.
func (*PolicySigningConfig) BuildPolicyVerifyOptions ¶
func (ps *PolicySigningConfig) BuildPolicyVerifyOptions(tlsClientCA string) (*PolicyVerifyOptions, error)
BuildPolicyVerifyOptions builds signature verification parameters from PolicySigningConfig. Returns nil if signature verification is not enabled (signing disabled). tlsClientCA is used as the default fallback when CAFile is empty.
type PolicySigningIdentity ¶
type PolicySigningIdentity struct {
Cert *x509.Certificate
Key crypto.Signer
}
PolicySigningIdentity describes the admin identity used to sign the policy file.
func LoadPolicySigningIdentity ¶
func LoadPolicySigningIdentity(certPEM, keyPEM string) (*PolicySigningIdentity, error)
LoadPolicySigningIdentity loads the signer certificate and private key from PEM files.
type PolicyVerifyOptions ¶
type PolicyVerifyOptions struct {
// Roots is the trusted CA chain (Issuing CA + Root CA) for verifying the signer certificate.
Roots *x509.CertPool
// RequireAdminOU, when true, requires the signer certificate OU to contain the admin role.
RequireAdminOU bool
}
PolicyVerifyOptions describes the external parameters needed for signature verification.
type PrincipalAuthorization ¶
type PrincipalAuthorization = pki.PrincipalAuthorization
── Type aliases ──
func ParseUserPermissionExtension ¶
func ParseUserPermissionExtension(cert *x509.Certificate) (*PrincipalAuthorization, error)
ParseUserPermissionExtension delegates to pki-types.
type PrincipalUid ¶
type PrincipalUid = pki.PrincipalUid
── Type aliases ──
func MakePrincipalUidFromCert ¶
func MakePrincipalUidFromCert(realm, identifier string, certDER []byte) PrincipalUid
MakePrincipalUidFromCert constructs a PrincipalUid from a certificate DER (KeyHash = SPKI SHA-256, per spec §PrincipalUid).
func ParsePrincipalUid ¶
func ParsePrincipalUid(s string) (PrincipalUid, error)
ParsePrincipalUid delegates to pki-types.
type ProblemDetails ¶ added in v0.2.0
type ProblemDetails struct {
Type string `json:"type"`
Title string `json:"title,omitempty"`
Status int `json:"status"`
Detail string `json:"detail,omitempty"`
Instance string `json:"instance,omitempty"`
Challenge *semantics.Challenge `json:"challenge,omitempty"`
}
ProblemDetails is an RFC 9457 problem document carrying a CLC challenge.
type ProofStep ¶
ProofStep is a single step in a Merkle audit proof, containing a sibling hash and direction.
type ProofStepJSON ¶
ProofStepJSON is the JSON representation of an audit proof step.
type RecordRef ¶ added in v0.2.0
type RecordRef struct {
Digest string `json:"digest"` // hex of the record's input digest
Verdict string `json:"verdict"` // the CLC verdict recorded
Path string `json:"path,omitempty"`
}
RecordRef is where a record ended up, so a caller can point at it (audit entry, downstream log, response) without carrying the whole record.
func EmitDecisionRecords ¶ added in v0.2.0
func EmitDecisionRecords(cfg *EvidenceConfig, sinkCtx EvidenceContext, cert *x509.Certificate, aic *AIC, pa *PrincipalAuthorization, userCert *x509.Certificate, ops []OperationDecision) ([]RecordRef, error)
EmitDecisionRecords freezes one record per (source, operation) and hands it to the sink, returning where each record went. The verdict in each record is recomputed by RecordWith from the same grants and operation, so a record that does not reproduce is impossible by construction.
sinkCtx carries the correlation (which request, who, admitted or refused); the per-operation fields are filled in here.
func ReportOutcome ¶ added in v0.2.0
func ReportOutcome(sink OutcomeSink, cfg *EvidenceConfig, ctx EvidenceContext, rec OutcomeRecord) (RecordRef, error)
ReportOutcome sends an outcome record through the deployment's sink, filling in the recorder and timestamp when the caller left them out.
type RecorderDescriptor ¶ added in v0.2.0
type RecorderDescriptor struct {
ID string `json:"id"`
Kind string `json:"kind,omitempty"`
Note string `json:"note,omitempty"`
}
RecorderDescriptor identifies the admission point that emitted a record. It is published out of band (one descriptor per deployment); the envelope then carries its digest as a subject, so a consumer holding the descriptor can tell which recorder produced the record — and one that does not hold it can still tell that two records came from the same or different recorders.
type RiskAssessment ¶
type RiskAssessment struct {
// OperationID is the server-side per-request tracking id (correlation key).
OperationID string `json:"operation_id"`
// AgentID is the AIC agent identifier.
AgentID string `json:"agent_id,omitempty"`
// PrincipalUid is the verified principal UID.
PrincipalUid string `json:"principal_uid,omitempty"`
// DAHash is the sha256 hex hash of the signed DelegationAuthorization.
DAHash string `json:"da_hash,omitempty"`
// Capabilities are the admitted capability ids.
Capabilities []string `json:"capabilities,omitempty"`
// Operation is a short human-readable operation label (e.g. "POST /trade").
Operation string `json:"operation"`
// RequestedParams is the redacted summary of the request parameters.
RequestedParams Summary `json:"requested_params,omitempty"`
// Violations list deterministic-rule risks not yet covered semantically.
Violations []string `json:"violations,omitempty"`
// Semantic is the optional LLM semantic verdict for this request.
Semantic *SemanticVerdict `json:"semantic,omitempty"`
// PolicyVersion is the policy version effective at decision time.
PolicyVersion uint64 `json:"policy_version,omitempty"`
}
RiskAssessment is the snapshot of a request that needs human supervision. It is passed to ApprovalRequester.Request, OverrideRecorder.Record and attached to supervision events. JSON is snake_case and defaults to omitempty so a minimal assessment stays small.
type RiskMonitor ¶
type RiskMonitor struct {
// contains filtered or unexported fields
}
RiskMonitor maintains per-agent violation counts and enforces rules automatically. Thread-safe; nil receiver methods are no-ops (safe to call when gateway is not configured).
func NewRiskMonitor ¶
func NewRiskMonitor(cfg RiskMonitorConfig) *RiskMonitor
NewRiskMonitor creates a risk monitor.
func (*RiskMonitor) RecordViolation ¶
func (m *RiskMonitor) RecordViolation(v RiskViolation) bool
RecordViolation records a behavioral violation and evaluates rules; triggers the enforcement callback when the threshold is reached. Returns whether enforcement was triggered. Returns false for nil receiver.
func (*RiskMonitor) Rules ¶
func (m *RiskMonitor) Rules() []RiskRule
Rules returns a copy of the current rule list.
func (*RiskMonitor) SetRules ¶
func (m *RiskMonitor) SetRules(rules []RiskRule)
SetRules hot-swaps the rule set (called during SIGHUP hot-reload).
func (*RiskMonitor) Violations ¶
func (m *RiskMonitor) Violations(agentId string) int
Violations returns the cumulative violation count for an agent within the window (used for monitoring display when no enforcement has been triggered). Returns 0 for nil receiver.
type RiskMonitorConfig ¶
type RiskMonitorConfig struct {
// Rules is the list of risk rules.
Rules []RiskRule `json:"rules"`
// OnAction is the enforcement callback (gateway injects: execute disconnect + revoke).
// When nil, only logging is performed.
OnAction func(agentId, action, reason string)
// Logger is the structured logger; uses slog.Default() when nil.
Logger *slog.Logger
}
RiskMonitorConfig is the configuration for RiskMonitor.
type RiskRule ¶
type RiskRule struct {
// Name is the rule name.
Name string `json:"name"`
// Signals is the list of behavioral signal types that trigger the rule (any hit counts).
Signals []string `json:"signals"`
// Threshold is the violation count threshold within the window; reaching it triggers enforcement.
Threshold int `json:"threshold"`
// WindowSeconds is the counting window in seconds, default 60.
WindowSeconds int `json:"window_seconds,omitempty"`
// Action is the enforcement action: disconnect (kick) or revoke (kick + revoke).
Action string `json:"action"`
// Reason is the risk reason description for audit records.
Reason string `json:"reason"`
}
RiskRule is a single risk rule.
type RiskViolation ¶
type RiskViolation struct {
// AgentId is the violating agent.
AgentId string `json:"agent_id,omitempty"`
// Signal is the risk signal type (e.g. cap_overflow / abnormal_rate / out_of_window).
Signal string `json:"signal"`
// CapabilityId is the associated capability identifier (optional).
CapabilityId string `json:"capability_id,omitempty"`
// Details provides supplementary description.
Details string `json:"details,omitempty"`
// At is the violation time (Unix seconds).
At int64 `json:"at"`
}
RiskViolation describes a recorded behavioral violation (risk signal).
type RotatingFile ¶
type RotatingFile struct {
// contains filtered or unexported fields
}
RotatingFile is an auto-rotating file that supports size-based rotation and backup count limits.
func NewRotatingFile ¶
func NewRotatingFile(path string, maxSize int64, maxBak int) (*RotatingFile, error)
NewRotatingFile creates an auto-rotating file instance.
type Route ¶
type Route struct {
// Path is the URL path prefix to match (e.g. "/api/v1"). Requests under this
// prefix are forwarded to Target with their path preserved.
Path string
// Target is the backend base URL (e.g. "http://127.0.0.1:8080").
Target *url.URL
// AllowMethods, when non-empty, restricts accepted HTTP methods.
AllowMethods []string
// RequiredCapabilities requires the admitted agent to hold these capability
// ids before the request is forwarded.
RequiredCapabilities []string
}
Route is a reverse-proxy routing rule.
type SPIFFEID ¶
SPIFFEID represents a parsed SPIFFE identity.
func ExtractSPIFFEID ¶
func ExtractSPIFFEID(cert *x509.Certificate) *SPIFFEID
ExtractSPIFFEID extracts a parsed SPIFFE ID from a certificate's SAN URIs. Returns nil if no valid SPIFFE URI is found.
func ParseSPIFFEID ¶
ParseSPIFFEID parses a SPIFFE ID string into its components (trust domain, path).
type SealedTree ¶
type SealedTree struct {
BatchNumber int `json:"batch"`
Timestamp string `json:"timestamp"`
Previous string `json:"previous_root"`
Root string `json:"root"`
Size int `json:"size"`
}
SealedTree is a sealed Merkle tree batch with root hash and predecessor link.
type SemanticVerdict ¶
type SemanticVerdict struct {
// Model is the model identifier that produced the verdict.
Model string `json:"model"`
// Version is the model/policy version.
Version string `json:"version"`
// PromptHash is a hex digest of the evaluation prompt (evidence binding).
PromptHash string `json:"prompt_hash"`
// InputDigest is a hex digest of the evaluated input (optional).
InputDigest string `json:"input_digest,omitempty"`
// OutputJSON is the raw model output (JSON document, base64 in JSON).
OutputJSON []byte `json:"output_json,omitempty"`
// Decision is the semantic decision: allow | deny | refer.
Decision string `json:"decision"`
// Confidence is the model-reported confidence (0..1), optional.
Confidence float64 `json:"confidence,omitempty"`
// Failed reports that the semantic evaluation itself errored (timeout,
// transport failure). A failed gate must never fail-open; it maps to a
// deny/refer at the caller.
Failed bool `json:"failed,omitempty"`
// Err is the evaluation error text when Failed is set.
Err string `json:"err,omitempty"`
}
SemanticVerdict is the output of an optional LLM semantic gate. The SDK does not call an LLM itself; integrations that do (or that enforce semantic policies) populate this and attach it to a RiskAssessment for the human approver.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is an AIC-protected HTTP reverse proxy. It terminates TLS (optionally mTLS), runs the admission pipeline on every request, and forwards admitted requests to the matching backend, injecting the verified client identity.
func (*Server) Handler ¶
Handler exposes the server as a plain http.Handler (for embedding into an existing http.Server). Callers managing TLS themselves should use this.
func (*Server) ListenAndServe ¶
ListenAndServe starts the reverse-proxy server listening on addr. When TLSCertFile/TLSKeyFile are configured the listener terminates TLS (mTLS when CACertFile is also set).
type ServerOptions ¶
type ServerOptions struct {
ReadTimeout time.Duration
ReadHeaderTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int
}
ServerOptions tunes the embedded http.Server of the reverse proxy.
type SignedAuditEntry ¶
type SignedAuditEntry struct {
Entry AuditEntry `json:"entry"`
TST string `json:"tst,omitempty"`
}
SignedAuditEntry is an audit entry with TSA timestamp signature.
type SignedSupervisionEvent ¶
type SignedSupervisionEvent struct {
Event pki.SupervisionEvent `json:"event"`
TST string `json:"tst,omitempty"`
}
SignedSupervisionEvent is a supervision event with an optional RFC 3161 TSA timestamp attestation, mirroring SignedAuditEntry.
type SlogSink ¶ added in v0.2.0
SlogSink writes a compact summary of each record to a structured logger.
func (SlogSink) Emit ¶ added in v0.2.0
func (s SlogSink) Emit(ctx EvidenceContext, rec semantics.DecisionRecord, env semantics.Envelope) (RecordRef, error)
Emit implements EvidenceSink.
func (SlogSink) EmitAdmission ¶ added in v0.2.0
func (s SlogSink) EmitAdmission(ctx EvidenceContext, rec AdmissionRecord, env semantics.Envelope) (RecordRef, error)
EmitAdmission implements EvidenceSink for pipeline-level records.
func (SlogSink) EmitOutcome ¶ added in v0.2.0
func (s SlogSink) EmitOutcome(ctx EvidenceContext, rec OutcomeRecord, env semantics.Envelope) (RecordRef, error)
EmitOutcome implements OutcomeSink for the structured-logger fallback.
type Summary ¶
type Summary string
Summary is a redacted summary of the requested operation parameters. It is derived from a digest form and never carries raw parameter values, reusing the audit mask concept (see mask.go). The empty Summary means nothing was included.
func NewSummaryFromBody ¶
NewSummaryFromBody produces a masked digest summary of a request body. An empty body yields an empty Summary.
type SupervisionPolicy ¶
type SupervisionPolicy struct {
// RequireRuntimeApproval mandates that requests flagged by RequireApproval
// go through the ApprovalRequester. When true and ApprovalRequester is
// nil, startup validation fails.
RequireRuntimeApproval bool
// AllowBreakGlass enables break-glass overrides. When true and
// OverrideRecorder is nil, startup validation fails.
AllowBreakGlass bool
// RequireEvidenceExport mandates an EvidenceExporter. When true and
// EvidenceExporter is nil, startup validation fails.
RequireEvidenceExport bool
}
SupervisionPolicy gates the supervision features. Pre-operation (DA) supervision is aic-agent side and intentionally absent here.
type SupervisionQuery ¶
type SupervisionQuery struct {
OperationID string
DaHash string
AgentID string
Type pki.SupervisionEventType
TimeRange *TimeRange
Limit int
}
SupervisionQuery filters supervision events read from the store. Empty fields are ignored. Type filters on a specific SupervisionEventType.
type SupervisionResult ¶
type SupervisionResult struct {
// Decision is approved | denied | pending.
Decision string `json:"decision"`
// Approver identifies the human that decided.
Approver string `json:"approver,omitempty"`
// Reason explains the decision.
Reason string `json:"reason,omitempty"`
// EvidenceRef references an external approval/review ticket.
EvidenceRef string `json:"evidence_ref,omitempty"`
// DecidedAt is when the decision was made.
DecidedAt time.Time `json:"decided_at"`
}
SupervisionResult is the outcome of a human (or system) supervision step.
func (*SupervisionResult) Approved ¶
func (r *SupervisionResult) Approved() bool
Approved reports whether the result is an explicit approval.
type SupervisionStore ¶
type SupervisionStore struct {
// contains filtered or unexported fields
}
SupervisionStore is the append-only supervision event store: JSON Lines with the same durability rules as the audit chain (audit.go). Critical events are written synchronously and can be TSA-attested. A nil store disables event persistence; the decision path stays fail-closed regardless.
func NewSupervisionStore ¶
func NewSupervisionStore(file string, tsa *TSAClient, maxSize int64, maxBak int) (*SupervisionStore, error)
NewSupervisionStore creates an append-only event store at file. When file is empty it returns a nil store (no persistence). tsa, when non-nil, signs every recorded event with a timestamp token.
func (*SupervisionStore) Close ¶
func (s *SupervisionStore) Close() error
Close closes the store file. Further Record calls fail.
func (*SupervisionStore) File ¶
func (s *SupervisionStore) File() string
File returns the store file path (empty for a nil store).
func (*SupervisionStore) Query ¶
func (s *SupervisionStore) Query(filter SupervisionQuery) ([]pki.SupervisionEvent, error)
Query reads supervision events from the store in append order and filters them. Events are returned newest-correlation-friendly in file order; an empty result is a non-nil empty slice. A nil store returns an error.
func (*SupervisionStore) Record ¶
func (s *SupervisionStore) Record(ev *pki.SupervisionEvent) error
Record validates and synchronously appends a supervision event. TSA attestation is best-effort: a signing failure records the event unsigned and returns the underlying TSA error only if the write itself also failed.
type TSAClient ¶
type TSAClient struct {
// URL is the TSA service address.
URL string
// CACert is the CA certificate (for verifying TSA response signatures).
CACert *x509.Certificate
// HTTPClient is the HTTP client.
HTTPClient *http.Client
// SignFunc is a custom signing function (replaces HTTP calls).
SignFunc func(data []byte) ([]byte, error)
// contains filtered or unexported fields
}
TSAClient is an RFC 3161 timestamp client.
func NewTSAClient ¶
NewTSAClient creates a timestamp client.
func (*TSAClient) SetMaxTSTAge ¶
SetMaxTSTAge overrides the accepted TST age window. Defaults to 1h.
type TSTInfo ¶
type TSTInfo struct {
Version int
Policy asn1.ObjectIdentifier
MessageImprint MessageImprint
SerialNumber int
GenTime time.Time
Accuracy asn1.RawValue `asn1:"optional"`
Ordering bool `asn1:"optional,default:false"`
Nonce *int `asn1:"optional"`
TSA asn1.RawValue `asn1:"optional,explicit,tag:0"`
}
TSTInfo is the timestamp token information.
func UnmarshalTimestampToken ¶
UnmarshalTimestampToken parses a timestamp token from DER.
type TimeRange ¶
type TimeRange struct {
Start time.Time `json:"start,omitempty"`
End time.Time `json:"end,omitempty"`
}
TimeRange bounds a query window. Zero values are unbounded on that end.
type TimeStampReq ¶
type TimeStampReq struct {
Version int
MessageImprint MessageImprint
ReqPolicy asn1.ObjectIdentifier `asn1:"optional"`
Nonce *int `asn1:"optional"`
CertReq bool `asn1:"optional,default:false"`
Extensions []asn1.RawValue `asn1:"optional,set"`
}
TimeStampReq is an RFC 3161 timestamp request.
type TimeStampResp ¶
type TimeStampResp struct {
Status PKIStatusInfo
TimeStampToken asn1.RawValue `asn1:"optional"`
}
TimeStampResp is an RFC 3161 timestamp response.
type Translator ¶
Translator is the internationalization translation interface.
type TrustLayer ¶
type TrustLayer int
TrustLayer represents a layer in the three-layer trust model.
const ( Layer1Identity TrustLayer = iota Layer2Representation Layer3OnlineAuthorization )
Layer1/2/3 three-layer trust model layer constants.
type VerifyRequest ¶
type VerifyRequest struct {
Batch int `json:"batch"`
Leaf string `json:"leaf"`
Proof []ProofStepJSON `json:"proof"`
}
VerifyRequest is an audit verification request.
type VerifyResponse ¶
VerifyResponse is an audit verification response.
Source Files
¶
- admission_record.go
- aic.go
- aicverifier.go
- audit.go
- capregistry.go
- challenge.go
- clc.go
- config.go
- constraints.go
- credential_bundle.go
- crl.go
- decision.go
- delegation_chain.go
- evidence.go
- evidence_profile.go
- evidence_signing.go
- evidence_sink.go
- evidence_verify.go
- hooks.go
- identity.go
- jwt.go
- mask.go
- merkle.go
- nonce_cache.go
- ocsp.go
- outcome_record.go
- parameters.go
- pipeline.go
- plugin.go
- policy.go
- rbac.go
- riskmonitor.go
- serial.go
- server.go
- sources.go
- spiffe.go
- supervision.go
- supervision_store.go
- tls.go
- trust_model.go
- tsa.go
- user_permission.go
- version.go
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
bearer-jwt-backend
command
Command bearer-jwt-backend demonstrates the aic-verifier SDK protecting a real HTTP API:
|
Command bearer-jwt-backend demonstrates the aic-verifier SDK protecting a real HTTP API: |
|
bearer-jwt-backend/gen-bearer
command
Command gen-bearer generates a CA keypair and signs a sample AIC-JWT bearer token for the bearer-jwt-backend example.
|
Command gen-bearer generates a CA keypair and signs a sample AIC-JWT bearer token for the bearer-jwt-backend example. |
|
inspect-record
command
Command inspect-record reads a decision record written by the SDK and prints what was decided, why, and the digest a third party recomputes.
|
Command inspect-record reads a decision record written by the SDK and prints what was decided, why, and the digest a third party recomputes. |
|
mcp-behind-proxy
command
Command mcp-behind-proxy runs the AIC-gated MCP server in its canonical deployment topology: the aic-verifier reverse proxy terminates TLS, runs the bearer AIC-JWT admission pipeline and forwards each admitted request to a loopback-only MCP backend, which trusts the proxy's server-asserted X-AIC-* identity headers (aic-verifier/mcp TrustProxy mode) instead of re-verifying a credential.
|
Command mcp-behind-proxy runs the AIC-gated MCP server in its canonical deployment topology: the aic-verifier reverse proxy terminates TLS, runs the bearer AIC-JWT admission pipeline and forwards each admitted request to a loopback-only MCP backend, which trusts the proxy's server-asserted X-AIC-* identity headers (aic-verifier/mcp TrustProxy mode) instead of re-verifying a credential. |
|
mcp-server
command
Command mcp-server demonstrates an AIC-gated MCP (Model Context Protocol) server on top of aic-verifier.
|
Command mcp-server demonstrates an AIC-gated MCP (Model Context Protocol) server on top of aic-verifier. |
|
mtls-backend
command
Command mtls-backend demonstrates the aic-verifier SDK protecting a real HTTP API with mTLS client certificate + AIC authorization:
|
Command mtls-backend demonstrates the aic-verifier SDK protecting a real HTTP API with mTLS client certificate + AIC authorization: |
|
mtls-backend/gen-cert
command
Command gen-mtls generates a demo CA, a server certificate, and an mTLS client certificate carrying an AIC extension, for the mtls-backend example.
|
Command gen-mtls generates a demo CA, a server certificate, and an mTLS client certificate carrying an AIC extension, for the mtls-backend example. |
|
smoke-verify
command
Command smoke-verify runs a minimal aic-verifier-protected HTTP service against a real varwof PKI (no demo CA), for smoke testing the SDK.
|
Command smoke-verify runs a minimal aic-verifier-protected HTTP service against a real varwof PKI (no demo CA), for smoke testing the SDK. |
|
supervision-demo
Package superv provides the shared supervision demonstration helpers used by the mtls-backend and bearer-jwt-backend examples: a DemoApprover that implements aicverifier.ApprovalRequester and a wire helper that turns the aic-verifier supervision config (policy + store + evidence export + trigger) into a runnable demo.
|
Package superv provides the shared supervision demonstration helpers used by the mtls-backend and bearer-jwt-backend examples: a DemoApprover that implements aicverifier.ApprovalRequester and a wire helper that turns the aic-verifier supervision config (policy + store + evidence export + trigger) into a runnable demo. |
|
Package mcp implements an AIC-gated MCP (Model Context Protocol) server for aic-verifier: an HTTP/Streamable transport carrying an embedded MCP server (github.com/mark3labs/mcp-go), guarded by the aic-verifier admission pipeline.
|
Package mcp implements an AIC-gated MCP (Model Context Protocol) server for aic-verifier: an HTTP/Streamable transport carrying an embedded MCP server (github.com/mark3labs/mcp-go), guarded by the aic-verifier admission pipeline. |