From 2aeefd04a8412a1467790e14e058193c019f4a63 Mon Sep 17 00:00:00 2001 From: AJ Beck Date: Sun, 27 Sep 2026 15:36:06 +0100 Subject: [PATCH] docs: complete exported API documentation Give every authn, iamproof and authz sentinel its own doc comment, including the HTTP status that authn's Error carries. Add doc links for unambiguous identifier references across the public packages, name the VerifyFunc and CheckFunc parameters, and name Claim.Bool's results. Record decision 0026 as implemented. --- adapter.go | 2 +- authn/authenticate.go | 10 +++--- authn/cognito.go | 8 ++--- authn/doc.go | 4 +-- authn/errors.go | 38 +++++++++++++++++----- authz/doc.go | 2 +- authz/predicates.go | 14 ++++---- authz/rule.go | 35 +++++++++++++------- docs/decisions/0026-exported-api-review.md | 16 +++++++++ docs/decisions/README.md | 2 +- docs/plan.md | 2 +- iamproof/doc.go | 2 +- iamproof/errors.go | 20 +++++++++--- iamproof/generate.go | 2 +- iamproof/verify.go | 6 ++-- identity/caller.go | 4 +-- identity/claims.go | 8 ++--- identity/claims_json.go | 4 +-- identity/claims_typed.go | 8 ++--- identity/context.go | 6 ++-- identity/errors.go | 2 +- identity/iam.go | 2 +- identity/jwt.go | 8 ++--- streaming_adapter.go | 6 ++-- 24 files changed, 136 insertions(+), 75 deletions(-) diff --git a/adapter.go b/adapter.go index 289a5a4..af277bb 100644 --- a/adapter.go +++ b/adapter.go @@ -52,7 +52,7 @@ func WithGatewayIdentity(enabled bool) Option { // WithIdentityClaimsBudget sets the gateway identity claims allowance in bytes. // The default is 256 KiB; -// New and NewStreaming reject settings outside 1..6 MiB. +// New and [NewStreaming] reject settings outside 1..6 MiB. // Each claim value costs 64 bytes plus name, string and exact-number text bytes. // Dedicated gateway scopes consume the same allowance. // Raw claim JSON is also limited to this byte length. diff --git a/authn/authenticate.go b/authn/authenticate.go index a80b5db..b62fa9b 100644 --- a/authn/authenticate.go +++ b/authn/authenticate.go @@ -10,11 +10,11 @@ import ( ) // VerifyFunc authenticates a credential without its HTTP scheme. -// It must return ErrInvalidCredentials for a definite rejection; +// It must return [ErrInvalidCredentials] for a definite rejection; // unknown errors are treated as unavailable. // Success requires JWT/SourceLocallyVerifiedToken for Bearer or IAM/SourceVerifiedIAMProof for EdgeIAM. // Constructors alone do not verify tokens. -type VerifyFunc func(context.Context, string) (identity.Caller, error) +type VerifyFunc func(ctx context.Context, credential string) (identity.Caller, error) // Config supplies explicit verifier dependencies and HTTP framing policy. type Config struct { @@ -44,7 +44,7 @@ type Authenticator struct { } // New validates and copies cfg without I/O or invoking a verifier. -// It returns a nil Authenticator and ErrInvalidConfiguration for invalid configuration. +// It returns a nil Authenticator and [ErrInvalidConfiguration] for invalid configuration. func New(cfg Config) (*Authenticator, error) { if cfg.Bearer == nil && cfg.IAMProof == nil || cfg.MaxAuthorizationBytes < 0 || cfg.MaxAuthorizationBytes > 1<<20 { return nil, ErrInvalidConfiguration @@ -79,10 +79,10 @@ func New(cfg Config) (*Authenticator, error) { // Callers must not mutate headers concurrently. // Every failure returns an anonymous zero caller. // -// An established caller fails with identity.ErrConflict before credential work. +// An established caller fails with [identity.ErrConflict] before credential work. // Runtime failures are *Error with sanitized categories; // caller cancellation returns ctx.Err directly. -// Nil contexts and nil/zero Authenticators return ErrInvalidConfiguration. +// Nil contexts and nil/zero Authenticators return [ErrInvalidConfiguration]. // No provider diagnostic or context cause is exposed. func (a *Authenticator) Authenticate(ctx context.Context, headers http.Header) (identity.Caller, error) { if a == nil || a.cfg.MaxAuthorizationBytes == 0 || ctx == nil { diff --git a/authn/cognito.go b/authn/cognito.go index 06fcf23..085abf7 100644 --- a/authn/cognito.go +++ b/authn/cognito.go @@ -75,7 +75,7 @@ type CognitoVerifier struct { } // NewCognitoVerifier validates and copies cfg without I/O. -// Invalid configuration returns a nil verifier and ErrInvalidConfiguration. +// Invalid configuration returns a nil verifier and [ErrInvalidConfiguration]. // It trusts the configured issuer endpoint; // it does not establish that the operator supplied an AWS URL. func NewCognitoVerifier(cfg CognitoConfig) (*CognitoVerifier, error) { @@ -139,8 +139,8 @@ func NewCognitoVerifier(cfg CognitoConfig) (*CognitoVerifier, error) { // Verify returns one locally verified caller without installing it in context. // It accepts access tokens only, with integer exp/iat and optional nbf, and a string aud when present. -// Rejections match ErrInvalidCredentials; -// unavailable JWKS matches ErrUnavailable. +// Rejections match [ErrInvalidCredentials]; +// unavailable JWKS matches [ErrUnavailable]. // Caller cancellation returns ctx.Err, never its cause. // Every failure returns a zero caller and sanitized diagnostics. func (v *CognitoVerifier) Verify(ctx context.Context, token string) (identity.Caller, error) { @@ -160,7 +160,7 @@ func (v *CognitoVerifier) Verify(ctx context.Context, token string) (identity.Ca // Warm obtains a fresh usable JWKS snapshot, sharing any required fetch. // It obeys refresh/backoff gates; // it does not force refresh, validate a token or start a background refresh loop. -// Failures are ErrUnavailable, caller ctx.Err or ErrInvalidConfiguration for a nil context or unconfigured verifier. +// Failures are [ErrUnavailable], caller ctx.Err or [ErrInvalidConfiguration] for a nil context or unconfigured verifier. func (v *CognitoVerifier) Warm(ctx context.Context) error { if v == nil || v.http == nil || ctx == nil { return ErrInvalidConfiguration diff --git a/authn/doc.go b/authn/doc.go index 678fb62..9f6c212 100644 --- a/authn/doc.go +++ b/authn/doc.go @@ -1,5 +1,5 @@ // Package authn selects one configured Bearer or EdgeIAM verifier from an HTTP Authorization header and transports the verified caller to protected handlers. -// NewCognitoVerifier provides local RS256 Cognito access-token verification with direct JSON v2 and bounded JWKS caching. +// [NewCognitoVerifier] provides local RS256 Cognito access-token verification with direct JSON v2 and bounded JWKS caching. // It does not check token revocation, authorize actions, or infer trust from a gateway, a TLS field, an action header or forwarded headers. // // Use New to configure explicit verifier functions. @@ -8,7 +8,7 @@ // Public routes omit that middleware; // CORS and preflight policy belong to the gateway or outer middleware. // -// Verifiers must authenticate credentials before returning a caller and classify rejection with ErrInvalidCredentials. +// Verifiers must authenticate credentials before returning a caller and classify rejection with [ErrInvalidCredentials]. // Adapt iamproof.ErrInvalidProof explicitly when wiring an IAM proof verifier; // this package does not import AWS packages. // Never retry another verifier after rejection. diff --git a/authn/errors.go b/authn/errors.go index 2fe9d31..4db5cc9 100644 --- a/authn/errors.go +++ b/authn/errors.go @@ -7,15 +7,35 @@ import ( ) // Inspectable failure categories never contain credentials or provider errors. +// Match them with [errors.Is]. +// Request-time failures are wrapped in an [*Error] whose [Error.StatusCode] is noted below. var ( + // ErrInvalidConfiguration means a constructor rejected its configuration, or a method was called on a nil or zero value or with a nil context. + // It is returned directly, not wrapped in an [*Error]. ErrInvalidConfiguration = errors.New("authn: invalid configuration") - ErrMissingCredentials = errors.New("authn: missing credentials") + + // ErrMissingCredentials means the request has no Authorization field (401 with all configured challenges). + ErrMissingCredentials = errors.New("authn: missing credentials") + + // ErrMalformedCredentials means the Authorization field is repeated, comma-combined, empty or not valid scheme framing (400). ErrMalformedCredentials = errors.New("authn: malformed credentials") - ErrUnsupportedScheme = errors.New("authn: unsupported scheme") - ErrHeaderTooLarge = errors.New("authn: authorization header too large") - ErrInvalidCredentials = errors.New("authn: invalid credentials") - ErrUnavailable = errors.New("authn: verification unavailable") - ErrVerifierContract = errors.New("authn: invalid verifier result") + + // ErrUnsupportedScheme means the credential uses a well-formed scheme that is not configured (401 with all configured challenges). + ErrUnsupportedScheme = errors.New("authn: unsupported scheme") + + // ErrHeaderTooLarge means the Authorization field exceeds [Config.MaxAuthorizationBytes] (431, without a challenge). + ErrHeaderTooLarge = errors.New("authn: authorization header too large") + + // ErrInvalidCredentials means the selected verifier definitely rejected the credential (401 with that scheme's challenge). + // A [VerifyFunc] returns it to classify a rejection. + ErrInvalidCredentials = errors.New("authn: invalid credentials") + + // ErrUnavailable means verification could not complete, for example because a dependency failed or a verifier returned an unclassified error (503, without a challenge). + ErrUnavailable = errors.New("authn: verification unavailable") + + // ErrVerifierContract means a verifier reported success with an unacceptable caller, such as the wrong kind or source (500). + // It indicates a composition fault, not a client error. + ErrVerifierContract = errors.New("authn: invalid verifier result") ) // Error describes a sanitized HTTP authentication failure. @@ -31,7 +51,7 @@ type Error struct { // Error returns a diagnostic containing only the sanitized category. func (e *Error) Error() string { return e.Unwrap().Error() } -// Unwrap exposes only a package category or identity.ErrConflict. +// Unwrap exposes only a package category or [identity.ErrConflict]. func (e *Error) Unwrap() error { if e == nil || e.category == nil { return ErrVerifierContract @@ -39,7 +59,7 @@ func (e *Error) Unwrap() error { return e.category } -// StatusCode returns the recommended HTTP status, or 500 for a zero Error. +// StatusCode returns the recommended HTTP status, or 500 for a zero [Error]. func (e *Error) StatusCode() int { if e == nil || e.status == 0 { return http.StatusInternalServerError @@ -55,7 +75,7 @@ func (e *Error) Challenges() []string { return slices.Clone(e.challenges) } -// GoString returns the same sanitized diagnostic used by Error. +// GoString returns the same sanitized diagnostic used by [Error.Error]. func (e *Error) GoString() string { return e.Error() } func (a *Authenticator) failure(category error, status int, selected scheme) *Error { diff --git a/authz/doc.go b/authz/doc.go index e90ee50..a541f7a 100644 --- a/authz/doc.go +++ b/authz/doc.go @@ -3,7 +3,7 @@ // // Construct rules once and share them across requests. // Custom checks must be concurrency-safe, honor their context and return false, nil for ordinary denial. -// Every callback error becomes ErrUnavailable. +// Every callback error becomes [ErrUnavailable]. // All and Any evaluate left to right and stop on an observed error; // keep mandatory application guards outside alternative permission branches. // diff --git a/authz/predicates.go b/authz/predicates.go index ae855b3..4994b41 100644 --- a/authz/predicates.go +++ b/authz/predicates.go @@ -10,7 +10,7 @@ import ( ) // Sources matches any supplied nonanonymous producer. -// Empty lists, SourceNone and unknown sources return ErrInvalidConfiguration. +// Empty lists, SourceNone and unknown sources return [ErrInvalidConfiguration]. // Source is trusted-code attribution, not an unforgeable credential. // Combine this broad predicate with permission checks. // The input slice is copied. @@ -32,7 +32,7 @@ func Sources(allowed ...identity.Source) (Rule, error) { } // JWTSubject matches an exact issuer and subject. -// Both must be nonempty UTF-8 or construction returns ErrInvalidConfiguration. +// Both must be nonempty UTF-8 or construction returns [ErrInvalidConfiguration]. // A subject does not prove a human identity. // Wrong kind or missing subject does not match. func JWTSubject(issuer, subject string) (Rule, error) { @@ -47,7 +47,7 @@ func JWTSubject(issuer, subject string) (Rule, error) { } // JWTClient matches an exact issuer and client_id, without interpreting audience or subject as a client ID or inferring a machine grant. -// Both arguments must be nonempty UTF-8 or construction returns ErrInvalidConfiguration. +// Both arguments must be nonempty UTF-8 or construction returns [ErrInvalidConfiguration]. func JWTClient(issuer, clientID string) (Rule, error) { if !validIdentifier(issuer) || !validIdentifier(clientID) { return Rule{}, ErrInvalidConfiguration @@ -62,7 +62,7 @@ func JWTClient(issuer, clientID string) (Rule, error) { // JWTScopes requires all supplied exact OAuth scope tokens from the exact issuer. // It does not interpret wildcards. // Missing or unavailable scopes do not match. -// Issuer must be nonempty UTF-8 and required must contain at least one valid RFC 6749 scope token, otherwise construction returns ErrInvalidConfiguration. +// Issuer must be nonempty UTF-8 and required must contain at least one valid RFC 6749 scope token, otherwise construction returns [ErrInvalidConfiguration]. // The input slice is copied. // Use Any for alternative scope requirements. func JWTScopes(issuer string, required ...string) (Rule, error) { @@ -73,7 +73,7 @@ func JWTScopes(issuer string, required ...string) (Rule, error) { // Flattened gateway strings remain unavailable and never match. // Groups are distinct from scopes and application grants. // Issuer and each required value must be nonempty UTF-8; -// an empty list or invalid value returns ErrInvalidConfiguration. +// an empty list or invalid value returns [ErrInvalidConfiguration]. // The input slice is copied. func CognitoGroups(issuer string, required ...string) (Rule, error) { return jwtSet(issuer, required, identity.JWT.CognitoGroups, validIdentifier) @@ -128,7 +128,7 @@ func validScope(s string) bool { // IAMPrincipal matches the full caller ARN literally, including path and session. // A root ARN matches only that root caller, not the account. // Valid literal * and ? in user paths retain their meaning as characters. -// Unsupported caller forms (including bare role ARNs) return ErrInvalidConfiguration, following NewIAM's grammar. +// Unsupported caller forms (including bare role ARNs) return [ErrInvalidConfiguration], following NewIAM's grammar. // This does not evaluate AWS IAM permissions or bind a unique identity across deletion and recreation of the principal. func IAMPrincipal(principalARN string) (Rule, error) { if _, err := identity.NewIAM(principalARN, identity.SourceCustomAssertion); err != nil { @@ -141,7 +141,7 @@ func IAMPrincipal(principalARN string) (Rule, error) { } // IAMRoleSessions matches assumed-role callers with the exact partition, account and role name, allowing any valid session name. -// Invalid components, wildcards and role paths return ErrInvalidConfiguration. +// Invalid components, wildcards and role paths return [ErrInvalidConfiguration]. // This is a role-name policy: recreating the same role name can match again. // It does not bind a unique role ID, identify the original actor, fabricate a role ARN or evaluate IAM policies. func IAMRoleSessions(partition, accountID, roleName string) (Rule, error) { diff --git a/authz/rule.go b/authz/rule.go index 1f42a44..305a881 100644 --- a/authz/rule.go +++ b/authz/rule.go @@ -10,12 +10,25 @@ import ( ) // Authorization errors contain no request facts or callback diagnostics. +// Match them with [errors.Is]. var ( + // ErrInvalidConfiguration means a rule constructor rejected its arguments, or [Rule.Authorize] was called on a zero Rule or with a nil context. ErrInvalidConfiguration = errors.New("authz: invalid configuration") - ErrInvalidRequest = errors.New("authz: invalid request") - ErrUnauthenticated = errors.New("authz: unauthenticated") - ErrDenied = errors.New("authz: denied") - ErrUnavailable = errors.New("authz: unavailable") + + // ErrInvalidRequest means the [Request] has an empty or non-UTF-8 action or a non-UTF-8 resource. + // No check runs. + ErrInvalidRequest = errors.New("authz: invalid request") + + // ErrUnauthenticated means the request caller is anonymous. + // No check runs, even a permissive custom one. + ErrUnauthenticated = errors.New("authz: unauthenticated") + + // ErrDenied means the rule evaluated without error and did not match. + ErrDenied = errors.New("authz: denied") + + // ErrUnavailable means a [CheckFunc] returned an error, whatever its boolean result. + // Evaluation stops at that node rather than trying another branch. + ErrUnavailable = errors.New("authz: unavailable") ) // Request supplies facts passed by value to each check. @@ -32,11 +45,11 @@ type Request struct { // CheckFunc decides whether a request matches. // Return false, nil for denial; -// any error becomes ErrUnavailable, even when accompanied by true. +// any error becomes [ErrUnavailable], even when accompanied by true. // Checks run synchronously, must honor ctx and must be safe for concurrent use when shared. // The caller owns dependency deadlines. // Panics propagate. -type CheckFunc func(context.Context, Request) (bool, error) +type CheckFunc func(ctx context.Context, request Request) (bool, error) // Rule holds immutable private configuration. // Copies may be shared concurrently. @@ -66,7 +79,7 @@ type node struct { } // Check constructs a rule from a nonnil callback. -// A nil callback returns ErrInvalidConfiguration. +// A nil callback returns [ErrInvalidConfiguration]. // Reusing a check does not deduplicate its evaluation. func Check(check CheckFunc) (Rule, error) { if check == nil { @@ -77,14 +90,14 @@ func Check(check CheckFunc) (Rule, error) { // All requires every child to match, evaluating left to right. // It stops on the first nonmatch or error. -// Empty, unconfigured or oversized trees return ErrInvalidConfiguration. +// Empty, unconfigured or oversized trees return [ErrInvalidConfiguration]. // The input slice is copied. func All(rules ...Rule) (Rule, error) { return combine(opAll, rules) } // Any requires one child to match, evaluating left to right. // It stops on the first match or error; // an error never falls through to another permission path. -// Empty, unconfigured or oversized trees return ErrInvalidConfiguration. +// Empty, unconfigured or oversized trees return [ErrInvalidConfiguration]. // The input slice is copied. // Mandatory guards belong in an outer All. func Any(rules ...Rule) (Rule, error) { return combine(opAny, rules) } @@ -109,8 +122,8 @@ func combine(op operation, rules []Rule) (Rule, error) { // Authorize returns nil only for an explicit allow. // It checks rule/context configuration, cancellation, request validity and a nonanonymous caller in that order. // No check runs for an anonymous or invalid request. -// Nonmatches return ErrDenied; -// all callback errors are sanitized to ErrUnavailable. +// Nonmatches return [ErrDenied]; +// all callback errors are sanitized to [ErrUnavailable]. // Cancellation is checked between nodes and before returning, and returns ctx.Err(), never context.Cause. // No results are cached or installed in context. func (r Rule) Authorize(ctx context.Context, request Request) error { diff --git a/docs/decisions/0026-exported-api-review.md b/docs/decisions/0026-exported-api-review.md index 6fb7334..9726653 100644 --- a/docs/decisions/0026-exported-api-review.md +++ b/docs/decisions/0026-exported-api-review.md @@ -390,3 +390,19 @@ The Cognito verifier stays in `authn`. E1 moves the selector and renames its type and constructor; E2 then renames the moved sentinels, so each PR is a single kind of change. This is compatible with decision 0011's intent: the new package holds only the existing selector, and action metadata remains consumer-owned. + +## Implementation evidence + +Each item landed as its own stacked PR, in order E1 to E6, and each passed gofmt, vet, module verification, the full race suite, the external consumer check, both Linux builds and the whitespace check on Go 1.27.1. + +- E1: `go list -deps ./examples/dispatcher/native` no longer includes the root adapter or `aws-lambda-go/events`, and `actionheader` depends only on the standard library. + Parsing behavior and tests moved unchanged; + the gateway-format and composition tests remain in `edge`. +- E2 and E3 are renames with unchanged behavior. + Decision 0012 carries a dated note about the E3 rename. +- E4 adds `Operation` and `Resource` constants, used by every internal call site; + error text is unchanged. +- E5 adds `String` methods with tests for every declared value, one undeclared value per type and the sanitized `Caller` text. +- E6 documents every sentinel (including `authn`'s HTTP statuses, checked against the code), names callback parameters and `Claim.Bool` results, and adds doc links. + Links are added only for camel-case identifiers, methods and qualified module names, to avoid linking ordinary words such as `Any` or `Request`; + a go/doc-based check found every link in the six public packages resolves. diff --git a/docs/decisions/README.md b/docs/decisions/README.md index b68c58a..cea77ed 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -33,4 +33,4 @@ Keep related decisions small enough to review and implement independently. | [0023: Consumer readiness](0023-consumer-readiness.md) | R1–R5 accepted; local implementation complete; external release prerequisites open | What complete consumer example, test support, deferred scope and release preparation should follow the implemented packages? | | [0024: Release automation](0024-release-automation.md) | Accepted; PR-only validation and organization credentials; App installation verified, release execution pending | How should release PRs, validation, credentials and initial versioning work? | | [0025: Main-branch protection](0025-main-branch-protection.md) | Proposed; awaiting owner review | Should the existing four CI checks and PR workflow be enforced before merging to main? | -| [0026: Exported API review](0026-exported-api-review.md) | E1–E6 accepted, E7 rejected; implementation in progress | Which exported API changes does Google's Go style guidance support before the first release? | +| [0026: Exported API review](0026-exported-api-review.md) | E1–E6 accepted and implemented in stacked PRs; E7 rejected | Which exported API changes does Google's Go style guidance support before the first release? | diff --git a/docs/plan.md b/docs/plan.md index 289073d..86ff3b1 100644 --- a/docs/plan.md +++ b/docs/plan.md @@ -381,7 +381,7 @@ these are placement, naming, typing and documentation changes made before the fi - [x] E3: rename `identity.WithCaller` to `identity.NewContext`. - [x] E4: type the `InvocationError` operation and limit names with exported constants. - [x] E5: add `String` methods to the `identity` enumerations. -- [ ] E6: complete sentinel docs, doc links, callback parameter names and `Claim.Bool` result names. +- [x] E6: complete sentinel docs, doc links, callback parameter names and `Claim.Bool` result names. Acceptance: each PR passes the full AGENTS.md validation set, and examples, guides and the external consumer check use the new names. diff --git a/iamproof/doc.go b/iamproof/doc.go index c13b195..2f31ea1 100644 --- a/iamproof/doc.go +++ b/iamproof/doc.go @@ -3,7 +3,7 @@ // It neither authorizes application actions nor installs callers in a context; // compose those responsibilities in the application. // -// Send Token.Value as the credential in Authorization: EdgeIAM , over HTTPS. +// Send [Token.Value] as the credential in Authorization: EdgeIAM , over HTTPS. // A proof is reusable within its freshness window and does not sign the application request body, method or action. // Do not log credentials or signed STS requests. // Generation uses the caller's AWS credentials; diff --git a/iamproof/errors.go b/iamproof/errors.go index a8f79e6..b399d52 100644 --- a/iamproof/errors.go +++ b/iamproof/errors.go @@ -3,10 +3,22 @@ package iamproof import "errors" // Package errors contain no credentials, signed URLs or dependency diagnostics. +// Match them with [errors.Is]. var ( - ErrInvalidConfiguration = errors.New("iamproof: invalid configuration") - ErrInvalidProof = errors.New("iamproof: invalid proof") - ErrUnavailable = errors.New("iamproof: verification unavailable") + // ErrInvalidConfiguration means [NewGenerator] or [NewVerifier] rejected its arguments or options, or a method was called on a nil or zero value or with a nil context. + ErrInvalidConfiguration = errors.New("iamproof: invalid configuration") + + // ErrInvalidProof means [Verifier.Verify] rejected a malformed, expired, misdirected or STS-rejected proof. + // When wiring the verifier into authn, map it to authn.ErrInvalidCredentials. + ErrInvalidProof = errors.New("iamproof: invalid proof") + + // ErrUnavailable means [Verifier.Verify] could not reach a verdict because of a dependency failure, an internal timeout or an unexpected STS response. + ErrUnavailable = errors.New("iamproof: verification unavailable") + + // ErrCredentialsUnavailable means [Generator.Generate] could not obtain usable credentials from its provider or could not presign with them. ErrCredentialsUnavailable = errors.New("iamproof: credentials unavailable") - ErrProofTooLarge = errors.New("iamproof: proof too large") + + // ErrProofTooLarge means [Generator.Generate] produced a proof larger than the protocol's 8 KiB bound. + // Credentials are never truncated to fit. + ErrProofTooLarge = errors.New("iamproof: proof too large") ) diff --git a/iamproof/generate.go b/iamproof/generate.go index 0f9d760..1cb444a 100644 --- a/iamproof/generate.go +++ b/iamproof/generate.go @@ -57,7 +57,7 @@ func NewGenerator(region, audience string, credentials aws.CredentialsProvider) // Generate retrieves fresh credentials and presigns locally, without calling STS. // Failure returns a zero Token and a sanitized package or caller-context error. -// Oversized proofs fail with ErrProofTooLarge; +// Oversized proofs fail with [ErrProofTooLarge]; // credentials are not cut. func (g *Generator) Generate(ctx context.Context) (Token, error) { if g == nil || g.credentials == nil || ctx == nil { diff --git a/iamproof/verify.go b/iamproof/verify.go index 57d4fc7..61ad50f 100644 --- a/iamproof/verify.go +++ b/iamproof/verify.go @@ -84,13 +84,13 @@ func NewVerifier(region, audience string, opts ...VerifierOption) (*Verifier, er }, nil } -// Verify accepts Token.Value, without an HTTP authentication scheme. +// Verify accepts [Token.Value], without an HTTP authentication scheme. // It returns an IAM caller with SourceVerifiedIAMProof only after STS confirms the proof, response invariants hold and the proof remains fresh. // It does not install the caller in a context. // Every failure returns an anonymous zero caller. // -// Malformed or rejected credentials match ErrInvalidProof. -// Dependency failures, internal timeouts and unexpected responses match ErrUnavailable. +// Malformed or rejected credentials match [ErrInvalidProof]. +// Dependency failures, internal timeouts and unexpected responses match [ErrUnavailable]. // Caller cancellation/deadline errors are preserved, without unsafe dependency causes. func (v *Verifier) Verify(ctx context.Context, token string) (identity.Caller, error) { if v == nil || v.client == nil || ctx == nil { diff --git a/identity/caller.go b/identity/caller.go index 20bdb64..0d9d72e 100644 --- a/identity/caller.go +++ b/identity/caller.go @@ -37,7 +37,7 @@ type Caller struct { iam *IAM } -// Kind returns the identity representation, or KindAnonymous for the zero value. +// Kind returns the identity representation, or [KindAnonymous] for the zero value. func (c Caller) Kind() Kind { if c.jwt != nil { return KindJWT @@ -48,7 +48,7 @@ func (c Caller) Kind() Kind { return KindAnonymous } -// Source returns the attributed producer, or SourceNone for an anonymous caller. +// Source returns the attributed producer, or [SourceNone] for an anonymous caller. func (c Caller) Source() Source { return c.source } // JWT reports whether the caller has a JWT identity and returns an immutable view. diff --git a/identity/claims.go b/identity/claims.go index b02ed48..2a55fdd 100644 --- a/identity/claims.go +++ b/identity/claims.go @@ -56,7 +56,7 @@ type Claims struct { } // Claim is an immutable view of a value. -// The zero value has kind ClaimInvalid. +// The zero value has kind [ClaimInvalid]. // Its representation describes the available input, not authentication strength. type Claim struct { node claimNode @@ -87,7 +87,7 @@ type claimsConfig struct { // WithClaimsBudget sets the weighted resource allowance in bytes, defaulting to 256 KiB. // Constructors reject a final setting outside 1..6 MiB. // Each value costs 64 bytes plus object-name, string and exact-number text bytes. -// ParseClaims also limits the original JSON byte length to this maximum. +// [ParseClaims] also limits the original JSON byte length to this maximum. // The allowance is not an AWS quota or a heap cap. // Nesting is limited separately to 64 containers, including the root, and cannot be disabled. func WithClaimsBudget(bytes int) ClaimsOption { @@ -144,8 +144,8 @@ func (c Claim) Text() (string, bool) { return c.node.text, true } -// Bool reports whether the claim is a boolean. -func (c Claim) Bool() (bool, bool) { +// Bool reports whether the claim is a boolean and, if so, returns its value. +func (c Claim) Bool() (value, ok bool) { if c.node.kind != ClaimBoolean { return false, false } diff --git a/identity/claims_json.go b/identity/claims_json.go index 2d2f7b7..68de24c 100644 --- a/identity/claims_json.go +++ b/identity/claims_json.go @@ -11,8 +11,8 @@ import ( // It preserves numeric token text, but not whitespace, member order or string escape spelling. // It rejects duplicate names, invalid UTF-8 and trailing values. // Input bytes must not change during construction and are not retained afterward. -// Invalid input matches ErrInvalidClaims; -// byte exhaustion is a ClaimsLimitError. +// Invalid input matches [ErrInvalidClaims]; +// byte exhaustion is a [ClaimsLimitError]. // The representation is received JSON, not proof of original JWT fidelity. func ParseClaims(data jsontext.Value, opts ...ClaimsOption) (Claims, error) { maximum, err := claimsBudget(opts) diff --git a/identity/claims_typed.go b/identity/claims_typed.go index 559a088..0304fd2 100644 --- a/identity/claims_typed.go +++ b/identity/claims_typed.go @@ -18,16 +18,16 @@ import ( // A nil interface is null. // Inputs must not change during construction; // afterward they may be mutated. -// Invalid input matches ErrInvalidClaims; -// byte exhaustion is a ClaimsLimitError. +// Invalid input matches [ErrInvalidClaims]; +// byte exhaustion is a [ClaimsLimitError]. func NewClaims(values map[string]any, opts ...ClaimsOption) (Claims, error) { return newTypedClaims(values, RepresentationDecoded, opts) } // NewTextClaims owns gateway text without parsing embedded JSON or numbers. -// All values retain RepresentationGatewayText. +// All values retain [RepresentationGatewayText]. // Nil means an empty object. -// Ownership, options and errors follow NewClaims. +// Ownership, options and errors follow [NewClaims]. func NewTextClaims(values map[string]string, opts ...ClaimsOption) (Claims, error) { return newTypedClaims(values, RepresentationGatewayText, opts) } diff --git a/identity/context.go b/identity/context.go index 1d1329d..56b35b5 100644 --- a/identity/context.go +++ b/identity/context.go @@ -11,10 +11,10 @@ func FromContext(ctx context.Context) Caller { return caller } -// NewContext returns a derived context carrying caller, for retrieval with FromContext. +// NewContext returns a derived context carrying caller, for retrieval with [FromContext]. // Anonymous on an empty context is a no-op. -// Any attempted installation over a nonanonymous caller, including an identical caller or anonymous, returns ErrConflict and a nil context. -// A nil context returns ErrInvalidCaller. +// Any attempted installation over a nonanonymous caller, including an identical caller or anonymous, returns [ErrConflict] and a nil context. +// A nil context returns [ErrInvalidCaller]. // Unrelated values are preserved. func NewContext(ctx context.Context, caller Caller) (context.Context, error) { if ctx == nil { diff --git a/identity/errors.go b/identity/errors.go index 0635cd7..f88150b 100644 --- a/identity/errors.go +++ b/identity/errors.go @@ -23,7 +23,7 @@ type ClaimsLimitError struct { // Error returns a sanitized description of the failure. func (e *ClaimsLimitError) Error() string { return "identity: claims budget exceeded" } -// Unwrap returns ErrClaimsLimit. +// Unwrap returns [ErrClaimsLimit]. func (e *ClaimsLimitError) Unwrap() error { return ErrClaimsLimit } // Maximum returns the configured allowance in bytes, or zero for a nil error. diff --git a/identity/iam.go b/identity/iam.go index 829a265..cb2d098 100644 --- a/identity/iam.go +++ b/identity/iam.go @@ -67,7 +67,7 @@ func WithIAMPrincipalID(id string) IAMOption { // Bare role ARNs and policy patterns are not caller forms. // Literal punctuation in a valid IAM user path is preserved without wildcard interpretation. // Source must be gateway, custom assertion or verified IAM proof. -// Errors match ErrInvalidCaller and contain no supplied identifiers. +// Errors match [ErrInvalidCaller] and contain no supplied identifiers. func NewIAM(principalARN string, source Source, opts ...IAMOption) (Caller, error) { if source != SourceGatewayAssertion && source != SourceCustomAssertion && source != SourceVerifiedIAMProof { return Caller{}, ErrInvalidCaller diff --git a/identity/jwt.go b/identity/jwt.go index c3f05e6..af794c8 100644 --- a/identity/jwt.go +++ b/identity/jwt.go @@ -35,12 +35,12 @@ type jwtConfig struct { } // WithGatewayScopes supplies API Gateway's dedicated scopes collection. -// It is valid only with SourceGatewayAssertion. +// It is valid only with [SourceGatewayAssertion]. // Nil means unavailable; // a nonnil empty slice means known empty. // Interpretable claim scopes must agree as exact sets. // The supplied collection consumes the claims' remaining weighted budget before copying, even when its values duplicate a claim. -// Do not mutate it during NewJWT. +// Do not mutate it during [NewJWT]. func WithGatewayScopes(scopes []string) JWTOption { return func(c *jwtConfig) error { if c.scopesSet { @@ -55,8 +55,8 @@ func WithGatewayScopes(scopes []string) JWTOption { // It requires a nonempty issuer and a subject or client_id, without conflating those fields. // Allowed sources are gateway, locally verified token and custom assertion. // It does not verify signatures or expiry. -// Invalid inputs match ErrInvalidCaller; -// a dedicated-scope budget failure additionally matches ErrClaimsLimit. +// Invalid inputs match [ErrInvalidCaller]; +// a dedicated-scope budget failure additionally matches [ErrClaimsLimit]. func NewJWT(claims Claims, source Source, opts ...JWTOption) (Caller, error) { if source != SourceGatewayAssertion && source != SourceLocallyVerifiedToken && source != SourceCustomAssertion { return Caller{}, ErrInvalidCaller diff --git a/streaming_adapter.go b/streaming_adapter.go index 1905c47..84c17ab 100644 --- a/streaming_adapter.go +++ b/streaming_adapter.go @@ -11,7 +11,7 @@ import ( ) // StreamingAdapter serves REST API proxy events through an ordinary HTTP handler and returns a metadata-prefixed body stream. -// Construct it with NewStreaming; +// Construct it with [NewStreaming]; // its zero value is unusable. // Configuration is immutable; // the application owns handler concurrency safety. @@ -26,12 +26,12 @@ type StreamingAdapter struct { } // WithStreamErrorReporter reports a sanitized terminal failure once after producer cleanup and before stream completion. -// It applies only to NewStreaming; +// It applies only to [NewStreaming]; // New rejects it. // A final nil callback is invalid. // The callback receives the invocation context, possibly canceled, and must return promptly. // Pre-handoff failures are returned directly and not reported. -// A callback panic is recovered and adds a sanitized ErrStream fault without replacing the original failure. +// A callback panic is recovered and adds a sanitized [ErrStream] fault without replacing the original failure. // It must not read or close the same stream: reporting precedes its completion. func WithStreamErrorReporter(report func(context.Context, error)) Option { return func(c *config) {