Documentation
¶
Overview ¶
Package capability issues and verifies local cap1/v1 grants. Start with DefaultLimits, an explicit signer and issuer-owned NewKeySet. Parsing, Verify, Grant.Authorize and Grant.Consume are separate trust boundaries: verification alone does not authorize business effects or consume a use.
Core code owns no network, database, worker or clock lifecycle and adds no cgo. Optional HTTP/memory/PostgreSQL/Valkey packages compose caller-owned resources. Tokens encode visible bearer authority, not encryption, JWT/JWS or RFC 9421. Local policies and pinned standards are in docs/specification-decisions.md.
Constructors copy secrets without secure erasure. Maps/slices and callbacks stay borrowed during calls: use safe immutable collaborators and bounded context-aware I/O. Cancellation cannot forcibly preempt synchronous callbacks.
Index ¶
- Variables
- func CanonicalPayload(payload Payload, limits Limits) ([]byte, error)
- func Issue(ctx context.Context, payload Payload, signer Signer, limits Limits) (string, error)
- func SignURL(ctx context.Context, payload Payload, request URLRequest, profile URLProfile, ...) (string, error)
- type Algorithm
- type BoundedResolver
- type BoundedResolverOptions
- type Consumption
- type ConsumptionResult
- type ConsumptionStore
- type ConsumptionStoreFunc
- type Grant
- type Header
- type Key
- type KeySet
- type Limits
- type Parsed
- type Payload
- type Proof
- func (proof Proof) CommandDigest(key [32]byte, fieldPath string, maximumBytes int) ([32]byte, error)
- func (Proof) Format(state fmt.State, _ rune)
- func (Proof) LogValue() slog.Value
- func (proof Proof) Transfer(ctx context.Context, callback func(context.Context, []byte) error) (err error)
- func (proof Proof) ValidateBounds(minimum, maximum int) error
- type Purpose
- type Reference
- type ResolvedKey
- type Resolver
- type ResolverFunc
- type RevocationChecker
- type RevocationCheckerFunc
- type RevocationQuery
- type Service
- type ServiceConfig
- type Signer
- type URLProfile
- type URLRequest
- type Use
- type Verifier
- type VerifyOptions
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrProofTransferFailed redacts a failed or panicking custody callback. ErrProofTransferFailed = errors.New("capability: proof transfer failed") // ErrCapacity reports refusal before state insertion or consumption. Trusted // adapters must only return this classification when no use was committed. ErrCapacity = errors.New("capability: state capacity reached") // ErrInvalidConfiguration reports an unsafe or incomplete caller policy. ErrInvalidConfiguration = errors.New("capability: invalid configuration") // ErrInvalidPayload reports a malformed or semantically invalid payload. ErrInvalidPayload = errors.New("capability: invalid payload") // ErrNonCanonical reports a valid representation that is not the unique v1 encoding. ErrNonCanonical = errors.New("capability: non-canonical encoding") // ErrInvalidToken reports malformed token framing or protected metadata. ErrInvalidToken = errors.New("capability: invalid token") // ErrInvalidSignature reports that the protected token did not authenticate. ErrInvalidSignature = errors.New("capability: invalid signature") // ErrSigningFailed reports a signer failure without exposing its diagnostic. ErrSigningFailed = errors.New("capability: signing failed") // ErrKeyResolution reports a resolver failure without exposing its diagnostic. ErrKeyResolution = errors.New("capability: key resolution failed") // ErrAlgorithmMismatch reports an algorithm and key-type mismatch. ErrAlgorithmMismatch = errors.New("capability: algorithm mismatch") // ErrUnknownKey reports an unresolved key identifier. ErrUnknownKey = errors.New("capability: unknown key") // ErrKeyDisabled reports a key that policy has disabled without revoking it. ErrKeyDisabled = errors.New("capability: key disabled") // ErrKeyRevoked reports a key that policy has revoked. ErrKeyRevoked = errors.New("capability: key revoked") // ErrKeyNotActive reports a key outside its configured activation interval. ErrKeyNotActive = errors.New("capability: key not active") // ErrNotYetValid reports a capability used before its validity interval. ErrNotYetValid = errors.New("capability: not yet valid") // ErrExpired reports a capability used after its validity interval. ErrExpired = errors.New("capability: expired") ErrUnauthorized = errors.New("capability: unauthorized use") // ErrInvalidURL reports a URL or profile with ambiguous or unsafe semantics. ErrInvalidURL = errors.New("capability: invalid URL") // ErrURLBinding reports a valid capability presented for a different URL use. ErrURLBinding = errors.New("capability: URL binding mismatch") // ErrReplayExhausted reports a capability whose bounded uses were consumed. ErrReplayExhausted = errors.New("capability: use limit exhausted") // ErrReplayConflict reports inconsistent state for one capability identity. ErrReplayConflict = errors.New("capability: replay state conflict") // ErrConsumptionUnknown reports a store failure whose commit outcome is unknown. ErrConsumptionUnknown = errors.New("capability: consumption outcome unknown") // ErrRevoked reports authority matched by a configured revocation boundary. ErrRevoked = errors.New("capability: revoked") // ErrRevocationUnknown reports a revocation check that could not complete. ErrRevocationUnknown = errors.New("capability: revocation status unknown") // ErrAdapterProtocol reports a malformed durable-adapter response. ErrAdapterProtocol = errors.New("capability: adapter protocol violation") )
Functions ¶
func CanonicalPayload ¶
CanonicalPayload returns owned local fixed-order JSON under DEC-003. It copies/sorts audiences and maps before admission, rejecting duplicate audiences and invalid text/time/bounds. Unicode bytes are not normalized; equivalent Unicode spellings remain different authority. This is not RFC 8785 canonicalization or authentication.
func Issue ¶
Issue validates/signs local cap1/v1 under DEC-001–003 with non-nil ctx/signer. Payload collections are copied, but signer methods remain trusted synchronous code with stable key IDs no longer than 256 bytes. It generates no ID or randomness. Signatures must be 1–512 bytes; output length is checked after encoding. Signer errors retain only ErrSigningFailed and safe context categories; panics/typed nil are not contained.
Example ¶
package main
import (
"context"
"fmt"
"time"
"github.com/faustbrian/go-capability/v2"
)
func main() {
now := time.Date(2026, 8, 9, 12, 0, 0, 0, time.UTC)
key := []byte("0123456789abcdef0123456789abcdef")
signer, _ := capability.NewHMACSHA256Signer("example-key", key)
verifier, _ := capability.NewHMACSHA256Verifier(key)
payload := capability.Payload{
Version: 1, Issuer: "https://issuer.example",
Audiences: []string{"download"}, Bearer: true,
Resource: "reports/42", Operation: "download",
IssuedAt: now, NotBefore: now, ExpiresAt: now.Add(time.Minute),
ID: "example-capability",
}
token, _ := capability.Issue(context.Background(), payload, signer, capability.DefaultLimits())
keys, _ := capability.NewKeySet([]capability.Key{{Issuer: "https://issuer.example", ID: "example-key", Verifier: verifier}})
grant, _ := capability.Verify(context.Background(), token, keys, capability.VerifyOptions{Issuer: "https://issuer.example",
Now: now, Limits: capability.DefaultLimits(),
})
err := grant.Authorize(capability.Use{Issuer: "https://issuer.example",
Audience: "download", Resource: "reports/42", Operation: "download",
})
fmt.Println(err == nil)
}
Output: true
func SignURL ¶
func SignURL( ctx context.Context, payload Payload, request URLRequest, profile URLProfile, signer Signer, limits Limits, ) (string, error)
SignURL owns empty Payload.Resource/Operation, canonicalizes the request and adds copied reserved profile/digest caveats, which must be absent. No caller collection is mutated. The returned URL contains visible bearer credentials; callers prevent disclosure. It generates no ID, reads no body and performs no egress (DEC-006/007).
Example ¶
package main
import (
"context"
"fmt"
"time"
"github.com/faustbrian/go-capability/v2"
)
func main() {
now := time.Date(2026, 8, 9, 12, 0, 0, 0, time.UTC)
signer, _ := capability.NewHMACSHA256Signer(
"example-key", []byte("0123456789abcdef0123456789abcdef"),
)
payload := capability.Payload{
Version: 1, Issuer: "https://issuer.example",
Audiences: []string{"download"}, Bearer: true,
IssuedAt: now, NotBefore: now, ExpiresAt: now.Add(time.Minute),
ID: "example-url",
}
profile := capability.URLProfile{
Name: "download-v1", SignatureParameter: "cap",
AllowedSchemes: []string{"https"},
AllowedAuthorities: []string{"files.example"},
QueryParameters: []string{"download"},
}
signed, _ := capability.SignURL(context.Background(), payload, capability.URLRequest{
Method: "GET", RawURL: "https://files.example/report/42?download=1",
}, profile, signer, capability.DefaultLimits())
fmt.Println(len(signed) > len("https://files.example/report/42?download=1"))
}
Output: true
Types ¶
type Algorithm ¶
type Algorithm string
Algorithm identifies a reviewed signature construction and its required key type.
type BoundedResolver ¶
type BoundedResolver struct {
// contains filtered or unexported fields
}
BoundedResolver constrains remote key lookups by algorithm, identifier size, and deadline. Construct with NewBoundedResolver; zero is unusable. It owns no cache or goroutine and cannot forcibly stop a source ignoring context. Shared use requires safe borrowed source/verifier methods.
func NewBoundedResolver ¶
func NewBoundedResolver(options BoundedResolverOptions) (*BoundedResolver, error)
NewBoundedResolver copies a nonempty unique algorithm allowlist while borrowing Source. Invalid bounds return nil and ErrInvalidConfiguration. It does not call the source, start workers or create a timer until Resolve.
func (*BoundedResolver) Resolve ¶
func (resolver *BoundedResolver) Resolve(ctx context.Context, keyID string, algorithm Algorithm) (ResolvedKey, error)
Resolve admits key/algorithm before one timed source call and rechecks issuer text plus verifier binding. Verify still owns lifecycle and selected-issuer equality. Source errors retain safe key/algorithm/context categories, not diagnostics. It does not forcibly interrupt or independently reject a source returning successfully after its deadline.
type BoundedResolverOptions ¶
type BoundedResolverOptions struct {
// Source is mandatory borrowed policy; it must honor context and return promptly.
Source Resolver
// Timeout must be positive and bounds a cooperative source, not arbitrary callback
// execution.
Timeout time.Duration
// AllowedAlgorithms must be nonempty/unique built-in constructions and is copied into
// an allowlist.
AllowedAlgorithms []Algorithm
// MaxKeyIDBytes must be positive and at most 256, checked before a source call.
MaxKeyIDBytes int
}
BoundedResolverOptions limits a caller-provided remote key source. Source must honor context cancellation; this adapter creates no hidden goroutines.
type Consumption ¶
type Consumption struct {
// Issuer is required authenticated namespace; never merge equal IDs across issuers.
Issuer string
// CapabilityID is required exact signed identity, not an application transaction ID.
CapabilityID string
// ExpiresAt is signed expiry bound; each store applies its own clock and supported
// precision.
ExpiresAt time.Time
// MaxUses must be positive for direct store calls and remains immutable for live
// identity.
MaxUses uint32
}
Consumption binds an exact authenticated issuer and capability ID to one immutable expiry and maximum-use bound. Issuer is required; stores must not merge different issuer namespaces even when their capability IDs match.
type ConsumptionResult ¶
type ConsumptionResult struct {
// Use is the one-based committed ordinal for bounded uses; zero for reusable grants.
Use uint32
// Remaining is the allowance after this committed use, not a reservation for future
// operations.
Remaining uint32
// Reusable is true only for the core unbounded-use shortcut; Use/Remaining are then
// zero.
Reusable bool
}
ConsumptionResult is the committed use ordinal and remaining allowance.
type ConsumptionStore ¶
type ConsumptionStore interface {
// Consume atomically compares exact issuer/ID, live expiry/max and count before
// increment. Honor ctx; document server clock/precision, retention and unknown
// commit windows.
Consume(context.Context, Consumption) (ConsumptionResult, error)
}
ConsumptionStore atomically increments a capability only when its committed use count remains below MaxUses. ErrCapacity, like ErrReplayExhausted and ErrReplayConflict, asserts that no use was committed. Custom adapters are trusted to return those classifications only for known no-consume outcomes. Any non-policy error may represent an unknown commit outcome and callers must fail closed rather than retry blindly.
type ConsumptionStoreFunc ¶
type ConsumptionStoreFunc func(context.Context, Consumption) (ConsumptionResult, error)
ConsumptionStoreFunc adapts a function to ConsumptionStore.
func (ConsumptionStoreFunc) Consume ¶
func (function ConsumptionStoreFunc) Consume(ctx context.Context, consumption Consumption) (ConsumptionResult, error)
Consume implements ConsumptionStore.
type Grant ¶
type Grant struct {
// contains filtered or unexported fields
}
Grant is authenticated capability authority. Its contents are copied so caller mutation cannot widen a previously verified grant. Zero Grant cannot authorize or consume. Value copies support concurrent reads with immutable Use inputs; they do not recheck time, revocation or key policy. Reverify before later effects as application policy requires.
func Verify ¶
func Verify(ctx context.Context, token string, resolver Resolver, options VerifyOptions) (Grant, error)
Verify reparses/authenticates the token, returning zero Grant on failure. Trusted selected issuer must match claim and key owner before cryptography. Key activation is inclusive/exclusive at unskewed Now; payload expiry is exclusive with Skew. Revocation runs after crypto/time success. Errors strip collaborator diagnostics; callbacks remain borrowed/cooperative. Authorize and Consume remain explicit; no cache/worker is added (DEC-004/005/009/010).
func VerifyURL ¶
func VerifyURL( ctx context.Context, request URLRequest, profile URLProfile, resolver Resolver, options VerifyOptions, ) (Grant, error)
VerifyURL authenticates the embedded capability and independently binds canonical method/URL/profile/digest. Success removes reserved URL caveats from its copied Grant; application dimensions still require Authorize and explicit Consume. Refusal returns zero Grant. It neither reads bodies nor derives trusted issuer/origin from forwarding headers.
func (Grant) Authorize ¶
Authorize compares exact required authority under DEC-005. Any mismatch returns ErrUnauthorized; extra use caveats are ignored and bearer grants do not check subject. No clock, consumption, revocation or protected business operation runs here.
func (Grant) Consume ¶
func (grant Grant) Consume(ctx context.Context, store ConsumptionStore) (ConsumptionResult, error)
Consume forwards authenticated issuer/ID/expiry/max for one atomic bounded use; reusable grants need no store. Non-nil ctx and verified grant are required. It performs no Authorize, clock or revocation recheck, and trusts successful custom results. Store errors retain only no-consume policy/capacity/context categories or ErrConsumptionUnknown, not arbitrary graphs/text. Callbacks/typed nil remain trusted; business effects are not coupled (DEC-008).
func (Grant) EnvelopeDigest ¶ added in v2.1.0
EnvelopeDigest returns SHA-256 of the complete canonical token authenticated by Verify, including its signature. The value is immutable, retains no bearer bytes, and is zero for a zero Grant. It differs from Reference.Digest and is not a keyed replay identifier, consumption receipt, or fresh validity evidence. Matching this metadata never replaces signature, authority, or lifecycle checks.
type Header ¶
type Header struct {
// Version must be 1 for local cap1 framing.
Version int `json:"v"`
// Type must be capability, not a JWT/JWS media type.
Type string `json:"typ"`
// Algorithm is protected but cannot select a different trusted verifier construction.
Algorithm Algorithm `json:"alg"`
// KeyID is protected public key identity with a fixed 256-byte canonical-header
// ceiling.
KeyID string `json:"kid"`
}
Header is protected token metadata. Algorithm is authenticated but is also checked against the trusted verifier returned by Resolver.
type Key ¶
type Key struct {
// Issuer is required independently established key ownership, never inferred from a
// token.
Issuer string
// ID is required globally unique exact key identity with a 256-byte ceiling.
ID string
// Verifier is mandatory borrowed trusted code; concurrent resolution/use requires it to
// be safe.
Verifier Verifier
// Disabled refuses verified use without marking compromise.
Disabled bool
// Revoked refuses verified use regardless of Disabled.
Revoked bool
// NotBefore is inclusive unskewed activation; zero is open.
NotBefore time.Time
// NotAfter is exclusive unskewed expiry; zero is open, otherwise must follow nonzero
// NotBefore.
NotAfter time.Time
}
Key is immutable verifier policy for one globally unique key ID owned by exactly one explicit issuer namespace.
type KeySet ¶
type KeySet struct {
// contains filtered or unexported fields
}
KeySet is an immutable in-process resolver suitable for explicit rotation overlap. Construct with NewKeySet; zero Resolve returns ErrUnknownKey. Policy values are copied, Verifiers remain borrowed and must support concurrent use. Publish a new set for rotation; no refresh, worker or close lifecycle is owned.
func NewKeySet ¶
NewKeySet copies nonempty key policy into a snapshot but borrows Verifier objects. Duplicate globally unique IDs are rejected even across algorithms. No key-count cap or typed-nil detection is added; invalid identity/lifecycle returns nil and ErrInvalidConfiguration.
func (*KeySet) Resolve ¶
func (set *KeySet) Resolve(ctx context.Context, keyID string, algorithm Algorithm) (ResolvedKey, error)
Resolve returns copied local policy plus its borrowed Verifier. Non-nil ctx is required; missing keys return ErrUnknownKey and algorithm mismatch returns ErrAlgorithmMismatch. It does not enforce key activation/revocation or selected issuer equality; Verify owns those checks.
type Limits ¶
type Limits struct {
// MaxTokenBytes caps encoded payload input/output and complete token bytes; positive,
// not an exact heap quota.
MaxTokenBytes int
// MaxFieldBytes caps bounded text fields; positive. Protected key IDs retain a separate
// 256-byte ceiling.
MaxFieldBytes int
// MaxAudiences is the positive maximum audience count.
MaxAudiences int
// MaxCaveats is the maximum caveat count; zero permits none, negative is invalid.
MaxCaveats int
// MaxCaveatBytes is the positive byte limit for each caveat value.
MaxCaveatBytes int
// MaxLifetime is positive; expiry minus the earlier issued/not-before second must fit
// its whole seconds.
MaxLifetime time.Duration
// MaxUses is the positive ceiling on signed bounded use counts; payload zero still
// means reusable.
MaxUses uint32
}
Limits defines explicit payload/token admission. Zero is invalid; start with DefaultLimits. CanonicalPayload copies collections before validation, and URL profile counts remain caller-owned. Finite defaults are not an exact heap quota; very large custom limits require allocation/arithmetic review.
func DefaultLimits ¶
func DefaultLimits() Limits
DefaultLimits returns the reviewed v1 resource limits.
type Parsed ¶
type Parsed struct {
// Header is decoded untrusted metadata, not trusted resolver policy.
Header Header
// Payload is decoded untrusted authority with caller-owned slices/maps; changing it
// does not verify anything.
Payload Payload
// contains filtered or unexported fields
}
Parsed is a structurally valid canonical token. It is not authenticated and grants no authority until passed to Verify. Exported decoded fields remain untrusted and mutable; returned collections belong to the caller. Verify reparses token text rather than trusting this value.
func Parse ¶
Parse bounds complete input before splitting/decoding and requires canonical decoded header/payload JSON, without authenticating authority. Refusal returns zero Parsed. Go Strict RawURLEncoding rejects padding/nonzero trailing bits but permits CR/LF during decoding; this is not a separate unique-token-text re-encoding check.
type Payload ¶
type Payload struct {
// Version must be local wire version 1, independent of the Go module major.
Version int
// Issuer is required exact issuer text; verification separately binds trusted key
// ownership.
Issuer string
// Audiences is required borrowed text; canonicalization copies/sorts it and rejects
// duplicate values.
Audiences []string
// Subject is the exact independently authenticated subject; it is mutually exclusive
// with Bearer.
Subject string
// Bearer permits use without subject matching and requires an empty Subject; tokens
// remain transferable.
Bearer bool
// Resource is required exact application authority; SignURL owns it instead.
Resource string
// Operation is required exact application operation; SignURL substitutes its HTTP
// method.
Operation string
// IssuedAt encodes nonnegative Unix seconds; subsecond precision is discarded.
IssuedAt time.Time
// NotBefore encodes nonnegative Unix seconds; it may precede IssuedAt, but expiry must
// follow both.
NotBefore time.Time
// ExpiresAt is exclusive signed Unix-second expiry; Verify applies caller skew
// separately.
ExpiresAt time.Time
// ID is required public capability identity; callers generate collision-resistant
// values per issuer.
ID string
// Tenant is exact optional authority; empty is an explicit empty boundary.
Tenant string
// CorrelationID is optional diagnostic attribution, never authorization.
CorrelationID string
// MaxUses zero is reusable; positive requires explicit atomic consumption, not just
// verification.
MaxUses uint32
// Caveats is an optional borrowed map of exact required pairs; Use may supply extras.
Caveats map[string]string
}
Payload is the authority encoded by a v1 capability. Subject and Bearer are mutually exclusive. MaxUses zero means reusable; a positive value is an upper bound enforced by a replay store, not by signature verification alone. Zero is invalid. Collections are borrowed during calls and must not be concurrently mutated; times encode UTC Unix seconds, discarding fractions.
func ParsePayload ¶
ParsePayload bounds input before decoding and accepts only exact local canonical JSON. Re-encoding rejects alternate order, duplicates, whitespace and trailing data. Returned slices/maps are owned; refusal returns zero Payload. It authenticates nothing (DEC-003).
type Proof ¶ added in v2.1.0
type Proof struct {
// contains filtered or unexported fields
}
Proof owns canonical token bytes through shared private lifetime state. Copies alias that state; zero and transferred values are invalid. Transfer clears owned/borrowed allocation capacity, not malicious caller copies or arbitrary runtime memory. Concurrent Transfer/Verify is safe.
func ParseProof ¶ added in v2.1.0
ParseProof bounds ingress through Parse before copying canonical token bytes. Parsing establishes structure only, not signature, current validity or use authority. Failure returns a zero proof. Limits must be explicitly valid.
func (Proof) CommandDigest ¶ added in v2.1.0
func (proof Proof) CommandDigest(key [32]byte, fieldPath string, maximumBytes int) ([32]byte, error)
CommandDigest binds the exact original canonical token, including signature, to a trusted coordinator's versioned secret key and field path without exposing or transferring custody. Supply a nonzero 32-byte key, an exact 1..4096-byte valid UTF-8 path and a positive maximumBytes chosen by the consumer's trusted transport profile. Parser limits remain unchanged; use Limits.MaxTokenBytes to retain a configured ingress profile. No normalization, key selection, signature/time/revocation check or consumption occurs.
Framing matches identity-command-v1: ASCII "identity-command-secret-v1" without NUL, big-endian uint32 path byte length, path bytes, big-endian uint64 token byte length and exact token bytes, authenticated with HMAC-SHA-256. The caller owns the outer command fingerprint, stored key version and retained-key recovery. This digest is sensitive correlation data, not a capability, reference digest, current authority or consumption receipt. Never expose a digest oracle or log the key, token or returned binding.
Invalid key/path/maximum returns zero and ErrInvalidConfiguration. Zero, transferred or oversized proofs return zero and ErrInvalidToken, retaining original custody on failure. Copies share the existing lifetime: a snapshot acquired before Transfer may finish, but later observations fail. No callback or hashing runs under the custody lock. Work/allocation is bounded by the admitted token size; the private snapshot is cleared on every exit. Complete erasure of cryptographic state/runtime memory cannot be guaranteed. Concurrent calls and Transfer are safe; no IO, clocks, caches or goroutines are owned.
Example ¶
package main
import (
"fmt"
"os"
"strings"
"github.com/faustbrian/go-capability/v2"
)
func main() {
// The published synthetic fixture and demonstration key are not production
// credentials. Trusted composition supplies its private versioned key.
bytes, err := os.ReadFile("testdata/v1-hmac.token")
if err != nil {
return
}
proof, err := capability.ParseProof(strings.TrimSpace(string(bytes)), capability.DefaultLimits())
if err != nil {
return
}
digest, err := proof.CommandDigest([32]byte{1}, "password-reset-proof", capability.DefaultLimits().MaxTokenBytes)
fmt.Println(err == nil && digest != ([32]byte{}))
fmt.Println(proof.ValidateBounds(32, 4096) == nil)
}
Output: true true
func (Proof) Format ¶ added in v2.1.0
Format redacts every fmt verb, including pointer and container formatting.
func (Proof) LogValue ¶ added in v2.1.0
LogValue prevents structured log reflection from exposing private storage.
func (Proof) Transfer ¶ added in v2.1.0
func (proof Proof) Transfer(ctx context.Context, callback func(context.Context, []byte) error) (err error)
Transfer invalidates every alias before invoking one synchronous callback outside locks. The callback borrows bytes only until it returns; all exposed capacity is cleared on success, error or panic. Failures never retry, consume authority or imply transport success. Pre-cancellation preserves custody. Explicit transport copies remain the caller's secret-custody responsibility.
func (Proof) ValidateBounds ¶ added in v2.1.0
ValidateBounds checks inclusive canonical token byte bounds without copying, exposing, transferring or consuming the proof. Both bounds must be positive and minimum must not exceed maximum; invalid bounds return ErrInvalidConfiguration. Zero, transferred or out-of-range proofs return ErrInvalidToken. Consumer bounds do not change ParseProof's configured limits. This is a synchronized custody observation, not authentication or authority: concurrent Transfer may invalidate the proof immediately after success.
type Purpose ¶ added in v2.1.0
type Purpose string
Purpose names one literal operation. Dots are punctuation, not hierarchy; no wildcard, prefix, case folding or cross-purpose matching is supported. Its zero value is invalid.
func NewPurpose ¶ added in v2.1.0
NewPurpose accepts 1..128 ASCII bytes matching [A-Za-z0-9][A-Za-z0-9._-]*. It never normalizes caller input.
type Reference ¶ added in v2.1.0
type Reference struct {
// CapabilityID is the original bounded grant ID, not proof of existence.
CapabilityID string
// Purpose is the exact literal operation bound by the producer.
Purpose Purpose
// Digest identifies original canonical token framing, not use authority.
Digest [32]byte
// ExpiresAt is the exclusive original expiry at UTC whole-second precision.
ExpiresAt time.Time
}
Reference is non-secret lookup metadata, never authority. It is a mutable value: applications must validate reconstructed values before storage/use.
func NewReference ¶ added in v2.1.0
NewReference validates a bounded ID, literal purpose, nonzero caller-supplied digest and positive UTC-second expiry. It cannot authenticate the digest's preimage or prove that a capability exists. Service.Issue derives its digest from the documented domain-separated canonical token framing.
type ResolvedKey ¶
type ResolvedKey struct {
// Issuer is the trusted namespace owning this key, not a token-derived claim.
Issuer string
// Verifier is mandatory borrowed trusted cryptographic code; typed nil is not detected.
Verifier Verifier
// Disabled refuses use without declaring the key revoked.
Disabled bool
// Revoked refuses all use and takes precedence over Disabled.
Revoked bool
// NotBefore is inclusive key activation at unskewed verification time; zero is open.
NotBefore time.Time
// NotAfter is exclusive key expiry at unskewed verification time; zero is open.
NotAfter time.Time
}
ResolvedKey is trusted key-policy state returned by a Resolver.
type Resolver ¶
type Resolver interface {
// Resolve supplies independently trusted issuer/key policy for exact global
// ID/algorithm; honor ctx and return safe ErrUnknownKey/ErrAlgorithmMismatch
// categories.
Resolve(context.Context, string, Algorithm) (ResolvedKey, error)
}
Resolver finds a trusted verifier and its current lifecycle policy. Shared implementations must be concurrency-safe, bounded and context-aware. No core worker, timeout, cache or panic boundary owns collaborator execution.
type ResolverFunc ¶
ResolverFunc adapts a function to Resolver.
func (ResolverFunc) Resolve ¶
func (function ResolverFunc) Resolve(ctx context.Context, keyID string, algorithm Algorithm) (ResolvedKey, error)
Resolve implements Resolver.
type RevocationChecker ¶
type RevocationChecker interface {
// Check returns true for revoked authority, false only after successful policy
// read. Errors fail Verify closed as ErrRevocationUnknown; document
// propagation/cache staleness.
Check(context.Context, RevocationQuery) (bool, error)
}
RevocationChecker checks capability, key, subject, resource, and issued-before policy. Shared implementations must be concurrency-safe, bounded and context-aware. No core worker, timeout, cache or panic boundary owns collaborator execution.
type RevocationCheckerFunc ¶
type RevocationCheckerFunc func(context.Context, RevocationQuery) (bool, error)
RevocationCheckerFunc adapts a function to RevocationChecker.
func (RevocationCheckerFunc) Check ¶
func (function RevocationCheckerFunc) Check(ctx context.Context, query RevocationQuery) (bool, error)
Check implements RevocationChecker.
type RevocationQuery ¶
type RevocationQuery struct {
// Issuer is authenticated exact namespace for every matching boundary.
Issuer string
// Tenant scopes resource revocation; empty remains exact.
Tenant string
// CapabilityID is authenticated signed ID for individual revocation.
CapabilityID string
// KeyID is authenticated protected key identity for issuer/key revocation.
KeyID string
// Subject is authenticated subject; memory skips this boundary when empty.
Subject string
// Resource is authenticated exact application authority, not a filesystem or egress
// permission.
Resource string
// IssuedAt is signed issue time; memory issued-before cutoffs match strictly earlier
// times.
IssuedAt time.Time
}
RevocationQuery is the complete authenticated identity checked after signature and time validation.
type Service ¶ added in v2.1.0
type Service interface {
// Issue signs one payload under the selected issuer and returns owned proof
// custody plus reference metadata. Expiry must be explicit; no use is consumed.
Issue(context.Context, Payload) (Proof, Reference, error)
// Verify authenticates a private proof snapshot and exact purpose/audience.
// It returns zero on failure and never consumes or transfers the proof.
Verify(context.Context, Proof, Purpose, string) (Grant, error)
}
Service is the canonical issuing and purpose/audience verification boundary. Verification never consumes a grant or commits a protected operation.
func NewService ¶ added in v2.1.0
func NewService(config ServiceConfig) (Service, error)
NewService constructs an immutable policy holder. A nil signer, resolver or clock, invalid limits or negative skew fails with ErrInvalidConfiguration. Collaborator panics are not recovered; only Transfer callback panics are.
Example ¶
package main
import (
"context"
"fmt"
"time"
"github.com/faustbrian/go-capability/v2"
)
func main() {
now := time.Unix(1786276800, 0).UTC()
signer, err := capability.NewHMACSHA256Signer("example", make([]byte, 32))
if err != nil {
panic(err)
}
verifier, err := capability.NewHMACSHA256Verifier(make([]byte, 32))
if err != nil {
panic(err)
}
keys, err := capability.NewKeySet([]capability.Key{{Issuer: "example", ID: "example", Verifier: verifier}})
if err != nil {
panic(err)
}
service, err := capability.NewService(capability.ServiceConfig{Issuer: "example", Signer: signer, Resolver: keys, Clock: func() time.Time { return now }, Limits: capability.DefaultLimits()})
if err != nil {
panic(err)
}
proof, reference, err := service.Issue(context.Background(), capability.Payload{Version: 1, Issuer: "example", Audiences: []string{"service"}, Bearer: true, Resource: "document", Operation: "download", ExpiresAt: now.Add(time.Minute), ID: "example-capability", MaxUses: 1})
if err != nil {
panic(err)
}
grant, err := service.Verify(context.Background(), proof, reference.Purpose, "service")
if err != nil {
panic(err)
}
// This proves verification only. The protected transaction owner must
// separately authorize and atomically consume the one-time effect.
fmt.Println(grant.Payload().ID)
err = proof.Transfer(context.Background(), func(_ context.Context, borrowed []byte) error {
// Send synchronously through a confidential, no-store transport here.
// Do not retain borrowed; explicit copies have caller-owned custody.
fmt.Println(len(borrowed) > 0)
return nil
})
fmt.Println(err == nil)
}
Output: example-capability true true
type ServiceConfig ¶ added in v2.1.0
type ServiceConfig struct {
// Issuer is independently selected trusted deployment policy, not a token
// claim. It fixes both issuance and verification to this exact key owner.
Issuer string
// Signer is the borrowed mandatory issuer-owned signing collaborator.
Signer Signer
// Resolver supplies independently trusted key ownership and lifecycle policy.
Resolver Resolver
// Clock is a borrowed fast local trusted clock, invoked outside custody locks.
Clock func() time.Time
// Limits bounds parsing and issuance; zero selects no implicit defaults.
Limits Limits
// Skew is nonnegative payload time tolerance, never key-lifecycle tolerance.
Skew time.Duration
// Revocations optionally checks current policy after signature/time validation.
// Nil is permitted only when deployment policy allows no revocation source.
Revocations RevocationChecker
}
ServiceConfig pins explicit policy. Clock must be a fast local trusted clock; all collaborators must support concurrent calls if the service is shared. Limits has no implicit defaults: use DefaultLimits explicitly. Revocations may be nil only when the application's policy permits no revocation source.
type Signer ¶
type Signer interface {
// Algorithm returns the stable trusted construction, never untrusted caller
// selection.
Algorithm() Algorithm
// KeyID returns stable public identity as valid nonempty text <=256 bytes.
KeyID() string
// Sign authenticates exact borrowed bytes, honoring context and returning owned
// signature storage.
Sign(context.Context, []byte) ([]byte, error)
}
Signer signs canonical protected bytes with an explicitly bound algorithm and key ID. Shared implementations must be concurrency-safe, bounded and context-aware. No core worker, timeout, cache or panic boundary owns collaborator execution.
func NewEd25519Signer ¶
func NewEd25519Signer(keyID string, key ed25519.PrivateKey) (Signer, error)
NewEd25519Signer copies a standard-library sized private key and valid nonempty <=256-byte ID, returning nil/ErrInvalidConfiguration on refusal. It validates size, not key consistency/entropy. Concurrent Sign checks context before synchronous RFC 8032 work; copies are not erased.
func NewHMACSHA256Signer ¶
NewHMACSHA256Signer copies >=32 secret bytes and valid nonempty <=256-byte key ID into a concurrent signer. Entropy is caller-owned; copies are not erased. Failure returns nil/ErrInvalidConfiguration. Sign checks context before synchronous work (RFC 2104/4231, local DEC-002).
type URLProfile ¶
type URLProfile struct {
// Name is required versioned local policy identity stored in a reserved caveat.
Name string
// SignatureParameter is required token query name and must not also be allowlisted.
SignatureParameter string
// AllowRelative permits path/query-only authority; the application must supply trusted
// origin context.
AllowRelative bool
// AllowedSchemes is borrowed sorted/unique http/https names; empty is allowed only for
// relative-only policy.
AllowedSchemes []string
// AllowedAuthorities is borrowed sorted/unique canonical lowercase ASCII authorities
// paired with schemes.
AllowedAuthorities []string
// QueryParameters is borrowed sorted/unique allowed query names, excluding
// SignatureParameter.
QueryParameters []string
// RequireBodyDigest requires exactly 32 caller-supplied SHA-256 bytes covered through a
// reserved caveat.
RequireBodyDigest bool
}
URLProfile fixes every URL component whose interpretation affects authority. QueryParameters is an allowlist; duplicate parameters are always rejected. Zero profile is invalid. Slices remain borrowed and immutable during calls/verifier lifetime. Names version local DEC-006/007, not DNS/SSRF admission, downstream authorization or provider interoperability.
func (URLProfile) Validate ¶
func (profile URLProfile) Validate(limits Limits) error
Validate checks local names, sorted unique allowlists and canonical authorities under valid Limits. Refusal returns ErrInvalidConfiguration. It adds no defensive copies, profile slice-count cap or I/O.
type URLRequest ¶
type URLRequest struct {
// Method is an exact nonempty uppercase HTTP token, not normalized.
Method string
// RawURL is borrowed URL text; verification requires canonical presentation, signing
// normalizes it.
RawURL string
// BodyDigest is borrowed precomputed SHA-256 data; omit it when the profile does not
// require a digest.
BodyDigest []byte
}
URLRequest contains the already selected external request representation. BodyDigest, when required, is exactly a SHA-256 digest rather than body bytes.
type Use ¶
type Use struct {
// Issuer must match the application-selected nonempty issuer exactly.
Issuer string
// Audience must equal one authenticated audience.
Audience string
// Subject must match subject-bound grants; bearer grants do not check it.
Subject string
// Resource must equal authenticated resource bytes exactly.
Resource string
// Operation must equal authenticated operation bytes exactly.
Operation string
// Tenant must equal authenticated tenant, including empty.
Tenant string
// Caveats is borrowed attempted-use data; every encoded caveat must match, extras are
// ignored.
Caveats map[string]string
}
Use describes one concrete application operation to authorize. Use fields must come from trusted application context rather than echoed token claims. Subject must be independently authenticated; Caveats stays borrowed during Authorize.
type Verifier ¶
type Verifier interface {
// Algorithm returns the stable trusted construction, never untrusted caller
// selection.
Algorithm() Algorithm
// Verify returns nil only for authentic exact borrowed bytes; honor ctx, retain
// no inputs and return ErrInvalidSignature on refusal.
Verify(context.Context, []byte, []byte) error
}
Verifier authenticates canonical protected bytes with one bound algorithm.
func NewEd25519Verifier ¶
NewEd25519Verifier copies a standard-library sized public key, returning nil/ErrInvalidConfiguration on wrong length. Concurrent Verify checks context before synchronous work; issuer/key lifecycle remains resolver-owned.
func NewHMACSHA256Verifier ¶
NewHMACSHA256Verifier copies >=32 secret bytes, returning nil/ErrInvalidConfiguration for a short key. It is concurrently usable, checks ctx before work and compares MACs with hmac.Equal, not constant total latency. Keys are not erased.
type VerifyOptions ¶
type VerifyOptions struct {
// Issuer is selected by trusted application configuration. It is required
// and must match both the signed claim and the resolved key's owner.
Issuer string
// Now is required trusted processing time; no clock is implicitly sampled.
Now time.Time
// Skew is nonnegative tolerance on payload validity, not key activation or store
// expiry.
Skew time.Duration
// Limits is explicit finite parser policy; zero is invalid.
Limits Limits
// Revocations is optional borrowed policy invoked after signature/time validation; nil
// disables that check.
Revocations RevocationChecker
}
VerifyOptions defines trusted issuer selection, time, skew, and parser limits.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
adapters
|
|
|
http
Package capabilityhttp integrates verified signed-URL grants with net/http without hiding application authorization or bounded-use consumption.
|
Package capabilityhttp integrates verified signed-URL grants with net/http without hiding application authorization or bounded-use consumption. |
|
memory
Package capabilitymemory provides process-local replay and revocation adapters.
|
Package capabilitymemory provides process-local replay and revocation adapters. |
|
Package caphttp is the compatibility facade for the target-oriented HTTP adapter.
|
Package caphttp is the compatibility facade for the target-oriented HTTP adapter. |
|
Package memory is the compatibility facade for the target-oriented process-local replay and revocation adapter.
|
Package memory is the compatibility facade for the target-oriented process-local replay and revocation adapter. |
|
Package postgres provides durable atomic capability consumption through database/sql and PostgreSQL row locks.
|
Package postgres provides durable atomic capability consumption through database/sql and PostgreSQL row locks. |
|
Package valkey provides atomic bounded-use consumption through one Valkey EVAL operation.
|
Package valkey provides atomic bounded-use consumption through one Valkey EVAL operation. |