htpx

package
v0.17.3 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: Apache-2.0, Apache-2.0 Imports: 62 Imported by: 0

README

htpx — HTTP Client & Proxy (Go) + API Server + (Planned) Forward/Forward-Forward Spec Inference

This repository contains:

  1. htpx library: composable HTTP client + reverse proxy primitives.
  2. API server skeleton (Gin + optional OIDC).
  3. Planned forward/forward-forward proxy to infer API specs from traffic (OpenAPI/WS/GQL/gRPC).

Module path: devnw.dev/htpx. License & headers are intentionally empty at this time.

Install

go get devnw.dev/htpx

Quick Start — Client

package main

import (
  "context"
  "encoding/json"
  "fmt"
  "net/url"
  "os"

  "devnw.dev/htpx"
)

type User struct {
  ID   string `json:"id"`
  Name string `json:"name"`
}

func main() {
  c, _ := htpx.NewClient(context.Background(), htpx.WithRoot("https://api.example.com"))
  resp, _ := c.Get("/v1/users", url.Values{"q": {"alice"}})
  users, _ := htpx.To[[]User](c, resp)
  enc := json.NewEncoder(os.Stdout); enc.SetIndent("", "  ")
  _ = enc.Encode(users)
}

Proxy (library)

ln, _ := net.Listen("tcp", "127.0.0.1:8443")
ctx, cancel := context.WithCancel(context.Background()); defer cancel()
_ = htpx.Proxy(ctx, ln,
  htpx.WithTLSConfig(&tls.Config{MinVersion: tls.VersionTLS13}),
  htpx.WithCertificate("", ""), // self-signed fallback
)

Upcoming changes: preserve request method/body, propagate multi-value headers, and define CONNECT behavior.

API Server

  • TLS listener, CORS, OpenAPI validation, optional OIDC JWT.
  • Only install JWT middleware when OIDC config is set to avoid nil middleware.

Forward/Forward-Forward Proxy (Spec Inference)

  • Privacy-first capture (header names + body structure by default).
  • Planned modules: Recorder → Inference → Exporters → CLI/UI (deterministic). See requirements.md for acceptance tests.
CLI (apimap)

Run a capture proxy (passthrough mode):

go run ./cmd/apimap proxy run \
  -addr :8080 \
  -mode passthrough \
  -sample 1.0 \
  -capture-bodies=true \
  -capture-body-values=false \
  -redact-headers Authorization,Cookie,Set-Cookie,X-Api-Key \
  -redact-json-fields email,password,token

Key flags:

  • -sample: floating (0..1) sample rate; unsampled requests are proxied, not recorded.
  • -capture-bodies: enable body shape capture (structure only unless -capture-body-values).
  • -capture-body-values: include literal JSON primitive types/values (replaces placeholders) – higher privacy impact.
  • -max-body-bytes: hard cap on bytes read per body (default 1MB) for capture.
  • -redact-headers: comma list of header names that will be marked redacted (names still listed; values never stored).
  • -redact-json-fields: comma list of top-level JSON response fields to replace with "redacted" marker.
  • -hash-json-fields: comma list of JSON fields (top-level or dot.paths) whose values are replaced by a stable truncated SHA-256 hash stored under hashed.
  • -hash-headers: comma list of header names whose values are hashed (request as header:<Name>, response as header-resp:<Name>) and never stored raw.
  • -privacy: preset shorthand for grouped privacy behavior: strict (structure only, force value redaction); balanced (default); open (captures JSON primitive values too).
  • -ws-max-frames: limit WebSocket frames captured per connection.
  • -mitm / -root-ca: enable TLS MITM (inspect mode) with generated or provided root CA.

Metrics (when compiled with prom build tag) are exposed via the dedicated metrics listener if -metrics is true (default) at -metrics-addr (default :9090) with Prometheus format.

Example scrape stanza:

- job_name: apimap
  static_configs:
    - targets: ['localhost:9090']

### Privacy Modes Example

Given a JSON response: `{ "email": "alice@example.com", "token": "s3cr3t", "count": 5 }`

Flags & outcomes (body shape capture enabled):

1. Default (balanced):
  - `-capture-body-values=false`
  - Output shape: `email: string`, `token: string`, `count: integer`
2. With redaction: `-redact-json-fields token`
  - `token` -> `redacted`
3. With hashing: `-hash-json-fields email`
  - `hashed.email=<stable 16 hex>`; no raw email stored
4. Open mode: `-privacy open` (or `-capture-body-values=true`)
  - Literals preserved (`email=alice@example.com`, `count=5`)
5. Combined: `-redact-json-fields token -hash-json-fields email -privacy open`
  - `token` redacted, `email` hashed (still no raw email), other literals visible

Header hashing works similarly via `-hash-headers Authorization,Set-Cookie` producing `hashed.header:Authorization` entries without raw values.

Dev

go test ./... -v

Documentation

Overview

Package htpx is the HTTP application toolkit that backs apic-generated services. It wires a Gin-based TLS/mTLS server (driven by the OpenAPI spec via oapi-codegen middleware, CORS, and slog request logging), a buffered outbound HTTP Client with retry, rate limiting, and a response-body size ceiling, and a TLS-terminating reverse Proxy with pluggable rules, subnet filters, and request rewriters. It also provides the request-authentication surfaces: API keys (with pluggable stores and scope checks), FIDO/WebAuthn, HMAC request signatures, and OIDC. Structured logging flows through pkg/log.

Index

Constants

View Source
const (
	DefaultRead       = Timeout(5 * time.Second)
	DefaultWrite      = Timeout(10 * time.Second)
	DefaultReadHeader = Timeout(5 * time.Second)

	DefaultClient   = Timeout(10 * time.Second)
	DefaultResponse = Timeout(10 * time.Second)

	DefaultTLSHandshake = Timeout(10 * time.Second)

	DefaultKeepAlive = Timeout(30 * time.Second)
	DefaultExpect    = Timeout(1 * time.Second)
)
View Source
const (
	// AuthScopes is the context key for the authentication scopes that are defined
	// for a specific API path that must be present in the JWT token in order to
	// access the path.
	OAuth2Scopes = "oauth2_scopes"
	ENV          = "GIN_MODE" // runnning environment
	DEVENV       = "debug"    // development environment
	INSECURE_DEV = "APIC_INSECURE_DEV"
)
View Source
const DefaultMaxResponseBytes int64 = 32 << 20

DefaultMaxResponseBytes is the secure-default ceiling on buffered response bodies (32 MiB). An unauthenticated/compromised upstream returning a multi-gigabyte body would otherwise OOM the client via io.ReadAll (PERF-0024). Raise or disable per-client with WithMaxResponseBytes.

View Source
const MaxSignatureBodyBytes int64 = 32 << 20 // 32 MiB

MaxSignatureBodyBytes caps the request body size that signature signing and verification will hash for the Digest header. Bodies larger than this are rejected with ErrSignatureBodyTooLarge instead of being buffered into memory. The cap matches PERF-0014's response body cap so signed flows on either side of the wire share the same backpressure ceiling.

Variables

View Source
var (
	// ErrAPIKeyNotFound is returned by APIKeyStore implementations when
	// a lookup or delete targets a key that is not present. Replaces the
	// six "API key not found" anonymous errors.New sites in apikey.go.
	ErrAPIKeyNotFound = errors.New("htpx: api key not found")
	// ErrAPIKeyInvalid is returned by APIKeyManager.ValidateKey when the
	// provided key string does not hash to a known stored key. The
	// message intentionally avoids leaking whether the key was unknown
	// versus mismatched (to limit oracle behavior under brute-force).
	ErrAPIKeyInvalid = errors.New("htpx: invalid api key")
	// ErrAPIKeyDisabled is returned by APIKeyManager.ValidateKey when
	// the key exists but is not enabled.
	ErrAPIKeyDisabled = errors.New("htpx: api key is disabled")
	// ErrAPIKeyExpired is returned by APIKeyManager.ValidateKey when the
	// key exists, is enabled, but its ExpiresAt is in the past.
	ErrAPIKeyExpired = errors.New("htpx: api key is expired")

	// ErrFIDOUserNotFound is returned by FIDOStore implementations when
	// a user lookup misses. Returned by GetUser, GetUserByName, and
	// GetCredentials in MemoryFIDOStore.
	ErrFIDOUserNotFound = errors.New("htpx: fido user not found")
	// ErrFIDOConfigRequired is returned by NewFIDOServer when the
	// supplied configuration pointer is nil.
	ErrFIDOConfigRequired = errors.New("htpx: fido config is required")

	// ErrOIDCEndpointEmpty is returned by the API server constructor
	// when an authenticated build is requested without an OIDC endpoint
	// AND insecure-dev startup is not allowed.
	ErrOIDCEndpointEmpty = errors.New("htpx: oidc endpoint is empty")
	// ErrValidationSpecMissing is returned by WithValidation when OIDC
	// auth is configured but no OpenAPI spec was supplied. The OpenAPI
	// request validator mounted from the spec is the only component
	// that invokes the authentication function, so without a spec every
	// `security:`-declared route would be served unauthenticated
	// (fail-open). htpx has no embedded spec to fall back on; pass
	// WithSwaggerSpec before WithValidation. Sibling of appsec N-01
	// fixed in api/ (73d6568).
	ErrValidationSpecMissing = errors.New(
		"htpx: oidc auth is configured but no OpenAPI spec was supplied;" +
			" pass WithSwaggerSpec before WithValidation so the auth" +
			" enforcer can be mounted",
	)
	// ErrRegisterFunctionNil is returned by api.New when called without
	// a route registration function.
	ErrRegisterFunctionNil = errors.New("htpx: register function is nil")
	// ErrInsecureDevNonLoopback is returned by newListener when
	// APIC_INSECURE_DEV is set but the host is not a loopback address.
	ErrInsecureDevNonLoopback = errors.New("htpx: APIC_INSECURE_DEV is only allowed on loopback hosts")
	// ErrMissingTLSPaths is returned by newListener when neither
	// insecure-dev nor a cert/key path pair is provided.
	ErrMissingTLSPaths = errors.New("htpx: missing TLS cert/key paths")

	// ErrTransportNil is returned by WithTransport when the supplied
	// http.RoundTripper is nil.
	ErrTransportNil = errors.New("htpx: transport is nil")
	// ErrClientNil is returned by WithDoer when the supplied
	// HTTPClient is nil.
	ErrClientNil = errors.New("htpx: client is nil")

	// ErrCircuitBreakerOpen is returned by CircuitBreaker.Execute when
	// the breaker is in the open state and the call is short-circuited.
	ErrCircuitBreakerOpen = errors.New("htpx: circuit breaker is open")
	// ErrCacheMiss is returned by MemoryCacheStore.Get when no entry
	// exists for the requested key.
	ErrCacheMiss = errors.New("htpx: cache miss")
	// ErrCacheExpired is returned by MemoryCacheStore.Get when an entry
	// exists but its TTL has elapsed.
	ErrCacheExpired = errors.New("htpx: cache expired")

	// ErrReadResponseBody is wrapped via errors.Join when ReadJSON fails
	// to read the response body off the wire.
	ErrReadResponseBody = errors.New("htpx: failed to read response body")
	// ErrUnmarshalResponseBody is wrapped via errors.Join when ReadJSON
	// fails to decode the response body as JSON.
	ErrUnmarshalResponseBody = errors.New("htpx: failed to unmarshal response body")
	// ErrResponseBodyTooLarge is returned when a response body exceeds the
	// client's maxResponseBytes ceiling (see WithMaxResponseBytes). PERF-0024.
	ErrResponseBodyTooLarge = errors.New("htpx: response body exceeds maximum")

	// ErrNoTokenToRefresh is returned by TokenRefresher.Start when the
	// refresher has no current token to refresh.
	ErrNoTokenToRefresh = errors.New("htpx: no token to refresh")

	// ErrAuthTokenMissing is returned by AuthToken when no JWT is
	// present in the request context.
	ErrAuthTokenMissing = errors.New("htpx: failed to get auth token")
	// ErrJWTMissingScopeClaim is returned by OIDC.Authenticate when
	// scopes are required but the token has no "scope" claim.
	ErrJWTMissingScopeClaim = errors.New("htpx: missing scope claim in jwt")
	// ErrJWTScopeClaimNotString is returned by OIDC.Authenticate when
	// the "scope" claim exists but is not a string.
	ErrJWTScopeClaimNotString = errors.New("htpx: jwt scope claim is not a string")
	// ErrJWKEndpointEmpty is returned by OIDC.UnmarshalJSON when the
	// JWK endpoint returns no keys at cold start.
	ErrJWKEndpointEmpty = errors.New("htpx: no keys found in jwk endpoint")
	// ErrUnsupportedJWTAlg is returned by ExtractToken when an inbound
	// token declares a JWS algorithm outside the asymmetric allowlist
	// (e.g. "none" or HS256/384/512). Mirrors api/oidc.go (QG-060) so the
	// JWKS verification path cannot be downgraded via algorithm
	// confusion. QG-081.
	ErrUnsupportedJWTAlg = errors.New("htpx: unsupported jwt algorithm")

	// ErrSignatureConfigRequired is returned by NewSigner when given a
	// nil SignatureConfig.
	ErrSignatureConfigRequired = errors.New("htpx: signature config is required")
	// ErrSignatureKeyIDRequired is returned by NewSigner when the
	// SignatureConfig has an empty KeyID.
	ErrSignatureKeyIDRequired = errors.New("htpx: signature key id is required")
	// ErrSignaturePrivateKeyRequired is returned by NewSigner when the
	// SignatureConfig has a nil PrivateKey.
	ErrSignaturePrivateKeyRequired = errors.New("htpx: signature private key is required")
	// ErrSignatureHMACKeyType is returned by Signer.sign and Verifier.verify
	// when the configured key for HMAC algorithms is not a []byte.
	ErrSignatureHMACKeyType = errors.New("htpx: hmac key must be []byte")
	// ErrSignatureRSAPrivateKey is returned by Signer.sign when the
	// configured private key is not an *rsa.PrivateKey.
	ErrSignatureRSAPrivateKey = errors.New("htpx: rsa private key required")
	// ErrSignatureECDSAPrivateKey is returned by Signer.sign when the
	// configured private key is not an *ecdsa.PrivateKey.
	ErrSignatureECDSAPrivateKey = errors.New("htpx: ecdsa private key required")
	// ErrSignatureED25519PrivateKey is returned by Signer.sign when the
	// configured private key is not an ed25519.PrivateKey.
	ErrSignatureED25519PrivateKey = errors.New("htpx: ed25519 private key required")
	// ErrSignatureMissingAuthHeader is returned by VerifyRequest when
	// the request has no Authorization header.
	ErrSignatureMissingAuthHeader = errors.New("htpx: missing authorization header")
	// ErrSignatureDateHeaderMissing is returned by VerifyRequest when
	// the signed headers include "date" but the request has none.
	ErrSignatureDateHeaderMissing = errors.New("htpx: date header required but not found")
	// ErrSignatureClockSkew is returned by VerifyRequest when the date
	// header is outside the configured clock-skew window. The message
	// intentionally retains the substring "clock skew" for compatibility
	// with existing tests that match on it.
	ErrSignatureClockSkew = errors.New("htpx: date header outside allowed clock skew")
	// ErrSignatureHMACVerify is returned by Verifier.verify when the
	// HMAC signature does not match the expected value.
	ErrSignatureHMACVerify = errors.New("htpx: hmac verification failed")
	// ErrSignatureRSAPublicKey is returned by Verifier.verify when the
	// configured public key is not an *rsa.PublicKey.
	ErrSignatureRSAPublicKey = errors.New("htpx: rsa public key required")
	// ErrSignatureECDSAPublicKey is returned by Verifier.verify when the
	// configured public key is not an *ecdsa.PublicKey.
	ErrSignatureECDSAPublicKey = errors.New("htpx: ecdsa public key required")
	// ErrSignatureECDSAVerify is returned by Verifier.verify when the
	// ECDSA signature does not match.
	ErrSignatureECDSAVerify = errors.New("htpx: ecdsa verification failed")
	// ErrSignatureED25519PublicKey is returned by Verifier.verify when
	// the configured public key is not an ed25519.PublicKey.
	ErrSignatureED25519PublicKey = errors.New("htpx: ed25519 public key required")
	// ErrSignatureED25519Verify is returned by Verifier.verify when the
	// Ed25519 signature does not match.
	ErrSignatureED25519Verify = errors.New("htpx: ed25519 verification failed")
	// ErrSignatureHeaderFormat is returned by parseSignatureHeader when
	// the Authorization header does not start with "Signature ".
	ErrSignatureHeaderFormat = errors.New("htpx: invalid signature header format")
	// ErrSignatureHeaderIncomplete is returned by parseSignatureHeader
	// when the Signature header is missing required fields.
	ErrSignatureHeaderIncomplete = errors.New("htpx: incomplete signature header")
	// ErrSignatureDigestHeaderMissing is returned by verifyDigest when
	// the request is missing the Digest header that was signed.
	ErrSignatureDigestHeaderMissing = errors.New("htpx: digest header required but not found")
	// ErrSignatureDigestUnsupported is returned by verifyDigest when the
	// Digest header uses an algorithm other than SHA-256.
	ErrSignatureDigestUnsupported = errors.New("htpx: only SHA-256 digest supported")
	// ErrSignatureDigestMismatch is returned by verifyDigest when the
	// computed digest does not match the value supplied in the header.
	ErrSignatureDigestMismatch = errors.New("htpx: digest mismatch")
	// ErrSignaturePEMDecode is returned by LoadPrivateKey and
	// LoadPublicKey when the PEM block cannot be decoded.
	ErrSignaturePEMDecode = errors.New("htpx: failed to decode PEM block")
)

Sentinel errors for the htpx package. Kept at package level per repo conventions. Messages use a lowercase, package-prefixed, diagnostic form so log output reads naturally; errors.Is callers should rely on pointer equality rather than substring matching of Error().

Sentinels that have richer documentation (ErrInvalidURL, ErrInvalidScheme, ErrAuthHeaderMissing, ErrMalformedAuthHeader, ErrEmptyAudience, ErrSignatureBodyTooLarge, ErrNotApplicable) are declared at their use sites.

View Source
var ErrAuthHeaderMissing = errors.New("htpx: authorization header missing")
View Source
var ErrEmptyAudience = errors.New("htpx: empty audience")
View Source
var ErrInvalidScheme = errors.New("htpx: invalid scheme")
View Source
var ErrInvalidURL = errors.New("htpx: invalid url")
View Source
var ErrMalformedAuthHeader = errors.New("htpx: malformed authorization header")
View Source
var ErrNotApplicable = errors.New("htpx: rewriter not applicable")
View Source
var ErrSignatureBodyTooLarge = errors.New("htpx: signed-request body exceeds size cap")

ErrSignatureBodyTooLarge is returned by computeDigest (via SignRequest and VerifyRequest) when the request body exceeds MaxSignatureBodyBytes. It is wrapped, so callers should use errors.Is to test it.

Functions

func APIKeyMiddleware

func APIKeyMiddleware(cfg *APIKeyAuthConfig) gin.HandlerFunc

APIKeyMiddleware creates a Gin middleware for API key authentication

func Args

func Args(app string) cli.Args

func AuthToken

func AuthToken(ctx context.Context) (jwt.Token, error)

func CacheMiddleware

func CacheMiddleware(cfg *CacheConfig) gin.HandlerFunc

CacheMiddleware creates a response caching middleware

func CircuitBreakerMiddleware

func CircuitBreakerMiddleware(cb *CircuitBreaker) gin.HandlerFunc

CircuitBreakerMiddleware creates a circuit breaker middleware for Gin

func Clone

func Clone(in *http.Request) *http.Request

func CloneHeader

func CloneHeader(in http.Header) http.Header

func CloneResponse

func CloneResponse(in *http.Response) *http.Response

func CloneURL

func CloneURL(in *url.URL) *url.URL

func CompressionMiddleware

func CompressionMiddleware(cfg *CompressionConfig) gin.HandlerFunc

CompressionMiddleware creates a response compression middleware

func ConstantTimeCompare

func ConstantTimeCompare(a, b string) bool

ConstantTimeCompare performs a constant-time comparison of two strings

func FIDOMiddleware

func FIDOMiddleware(store FIDOStore) gin.HandlerFunc

FIDOMiddleware creates a Gin middleware for FIDO2 authentication

func GenerateKeyPair

func GenerateKeyPair(bits int) (*rsa.PrivateKey, *rsa.PublicKey, error)

GenerateKeyPair generates a new RSA key pair for testing

func GenerateTLSCertificate

func GenerateTLSCertificate(
	l net.Listener, names ...string,
) (tls.Certificate, error)

GenerateTLSCertificate generates a self-signed TLS certificate.

func LoadPrivateKey

func LoadPrivateKey(pemBytes []byte) (any, error)

LoadPrivateKey loads a private key from PEM bytes

func LoadPublicKey

func LoadPublicKey(pemBytes []byte) (any, error)

LoadPublicKey loads a public key from PEM bytes

func Proxy

func Proxy(
	ctx context.Context,
	l net.Listener,
	opts ...ProxyOption,
) error

func RequestLogger

func RequestLogger(logger *slog.Logger) gin.HandlerFunc

RequestLogger returns the Gin request-logging middleware htpx installs on every server. It emits one structured slog record per request via github.com/samber/slog-gin with slog-gin's DefaultConfig: the request method and path are logged under a "request" group (request.method, request.path), the response status under a "response" group (response.status), and a request id under the "id" key (taken from the inbound X-Request-Id header, or generated when absent). Passing a logger backed by a custom slog.Handler lets callers/tests capture and assert that contract; New uses RequestLogger(slog.Default()) so the production wiring and the tested wiring share one construction point (GAP-0057).

func RequireScope

func RequireScope(scope string) gin.HandlerFunc

RequireScope returns a middleware that requires a specific scope

func SignatureMiddleware

func SignatureMiddleware(verifier *Verifier) gin.HandlerFunc

SignatureMiddleware creates a Gin middleware for signature verification

func To

func To[T any](c *Client, resp *http.Response) (T, error)

Types

type APIKey

type APIKey struct {
	ID          string         `json:"id"`
	Name        string         `json:"name"`
	KeyHash     string         `json:"key_hash"`
	Prefix      string         `json:"prefix"`
	Scopes      []string       `json:"scopes"`
	Metadata    map[string]any `json:"metadata"`
	Created     time.Time      `json:"created"`
	LastUsed    time.Time      `json:"last_used"`
	ExpiresAt   *time.Time     `json:"expires_at,omitempty"`
	RateLimitID string         `json:"rate_limit_id"`
	Enabled     bool           `json:"enabled"`
}

APIKey represents an API key

func GetAPIKey

func GetAPIKey(c *gin.Context) (*APIKey, bool)

GetAPIKey retrieves the authenticated API key from the context

func (*APIKey) HasScope

func (k *APIKey) HasScope(scope string) bool

HasScope checks if the API key has a specific scope

func (*APIKey) IsExpired

func (k *APIKey) IsExpired() bool

IsExpired checks if the API key has expired

type APIKeyAuthConfig

type APIKeyAuthConfig struct {
	// Manager is the API key manager
	Manager *APIKeyManager
	// HeaderName is the header to check for the API key (default: X-API-Key)
	HeaderName string
	// RequireScopes are the scopes required for this route
	RequireScopes []string
	// Optional makes the API key optional (for public endpoints with optional auth)
	Optional bool
}

APIKeyAuthConfig configures API key authentication

type APIKeyManager

type APIKeyManager struct {
	// contains filtered or unexported fields
}

APIKeyManager manages API keys

func NewAPIKeyManager

func NewAPIKeyManager(store APIKeyStore, logger *slog.Logger) *APIKeyManager

NewAPIKeyManager creates a new API key manager

func (*APIKeyManager) DeleteKey

func (m *APIKeyManager) DeleteKey(id string) error

DeleteKey deletes an API key

func (*APIKeyManager) GenerateKey

func (m *APIKeyManager) GenerateKey(name string, scopes []string, metadata map[string]any, expiresAt *time.Time) (*APIKey, string, error)

GenerateKey generates a new API key

func (*APIKeyManager) GetKey

func (m *APIKeyManager) GetKey(id string) (*APIKey, error)

GetKey returns a specific API key by ID

func (*APIKeyManager) ListKeys

func (m *APIKeyManager) ListKeys() ([]*APIKey, error)

ListKeys returns all API keys

func (*APIKeyManager) RevokeKey

func (m *APIKeyManager) RevokeKey(id string) error

RevokeKey revokes an API key

func (*APIKeyManager) ValidateKey

func (m *APIKeyManager) ValidateKey(keyStr string) (*APIKey, error)

ValidateKey validates an API key and returns the key object

type APIKeyStats

type APIKeyStats struct {
	// contains filtered or unexported fields
}

APIKeyStats tracks API key usage statistics

func NewAPIKeyStats

func NewAPIKeyStats() *APIKeyStats

NewAPIKeyStats creates a new stats tracker

func (*APIKeyStats) GetAllStats

func (s *APIKeyStats) GetAllStats() map[string]any

GetAllStats returns stats for all API keys

func (*APIKeyStats) GetStats

func (s *APIKeyStats) GetStats(keyID string) map[string]any

GetStats returns stats for a specific API key

func (*APIKeyStats) RecordRequest

func (s *APIKeyStats) RecordRequest(keyID string, err error)

RecordRequest records a request for an API key

type APIKeyStore

type APIKeyStore interface {
	// GetByHash retrieves an API key by its hash
	GetByHash(hash string) (*APIKey, error)
	// GetByID retrieves an API key by its ID
	GetByID(id string) (*APIKey, error)
	// GetByPrefix retrieves an API key by its prefix
	GetByPrefix(prefix string) (*APIKey, error)
	// Save saves or updates an API key
	Save(key *APIKey) error
	// Delete deletes an API key by ID
	Delete(id string) error
	// List returns all API keys
	List() ([]*APIKey, error)
	// UpdateLastUsed updates the last used timestamp
	UpdateLastUsed(id string, t time.Time) error
}

APIKeyStore defines the interface for storing and retrieving API keys

type CacheConfig

type CacheConfig struct {
	// Store is the cache store
	Store CacheStore
	// TTL is the default cache time-to-live
	TTL time.Duration
	// KeyFunc generates cache keys (default: method + URL)
	KeyFunc func(*gin.Context) string
	// ShouldCache determines if a response should be cached
	ShouldCache func(*gin.Context) bool
}

CacheConfig configures response caching

type CacheEntry

type CacheEntry struct {
	StatusCode int
	Headers    http.Header
	Body       []byte
	Timestamp  time.Time
	TTL        time.Duration
}

CacheEntry represents a cached response

func (*CacheEntry) IsExpired

func (e *CacheEntry) IsExpired() bool

IsExpired checks if the cache entry has expired

type CacheStore

type CacheStore interface {
	Get(key string) (*CacheEntry, error)
	Set(key string, entry *CacheEntry) error
	Delete(key string) error
	Clear() error
}

CacheStore defines the interface for caching

type CircuitBreaker

type CircuitBreaker struct {
	// contains filtered or unexported fields
}

CircuitBreaker implements the circuit breaker pattern

func NewCircuitBreaker

func NewCircuitBreaker(cfg *CircuitBreakerConfig) *CircuitBreaker

NewCircuitBreaker creates a new circuit breaker

func (*CircuitBreaker) Execute

func (cb *CircuitBreaker) Execute(fn func() error) error

Execute executes a function with circuit breaker protection

func (*CircuitBreaker) State

func (cb *CircuitBreaker) State() CircuitState

State returns the current circuit state

func (*CircuitBreaker) Stats

func (cb *CircuitBreaker) Stats() map[string]any

Stats returns circuit breaker statistics

type CircuitBreakerConfig

type CircuitBreakerConfig struct {
	// MaxFailures is the number of failures before opening the circuit
	MaxFailures int64
	// Timeout is how long to wait before attempting to close the circuit
	Timeout time.Duration
	// FailureThreshold is the percentage of failures to trigger circuit open (0-100)
	FailureThreshold float64
	// MinRequests is the minimum number of requests before evaluating failure rate
	MinRequests int64
	// OnStateChange is called when the circuit state changes
	OnStateChange func(from, to CircuitState)
}

CircuitBreakerConfig configures a circuit breaker

type CircuitState

type CircuitState int

CircuitState represents the state of a circuit breaker

const (
	CircuitClosed CircuitState = iota
	CircuitOpen
	CircuitHalfOpen
)

func (CircuitState) String

func (s CircuitState) String() string

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(ctx context.Context, opts ...Option) (*Client, error)

func (*Client) Close

func (c *Client) Close() (out error)

func (*Client) Delete

func (c *Client) Delete(
	uri string,
	query url.Values,
	opts ...RequestOption,
) (*http.Response, error)

func (*Client) Do

func (c *Client) Do(req *http.Request) (*http.Response, error)

func (*Client) Get

func (c *Client) Get(
	uri string,
	query url.Values,
	opts ...RequestOption,
) (*http.Response, error)

func (*Client) JSONReader

func (c *Client) JSONReader(in any) (io.ReadCloser, error)

func (*Client) LogValue

func (c *Client) LogValue() slog.Value

func (*Client) Post

func (c *Client) Post(
	uri string,
	query url.Values,
	body io.ReadCloser,
	opts ...RequestOption,
) (*http.Response, error)

func (*Client) Put

func (c *Client) Put(
	uri string,
	query url.Values,
	body io.ReadCloser,
	opts ...RequestOption,
) (*http.Response, error)

func (*Client) RoundTrip

func (c *Client) RoundTrip(req *http.Request) (*http.Response, error)

type ClientWrapper

type ClientWrapper func(*http.Request) (*http.Response, error)

func (ClientWrapper) Do

func (t ClientWrapper) Do(req *http.Request) (*http.Response, error)

func (ClientWrapper) RoundTrip

func (t ClientWrapper) RoundTrip(r *http.Request) (*http.Response, error)

type CompressionConfig

type CompressionConfig struct {
	// Level is the compression level (1-9 for gzip)
	Level int
	// MinSize is the minimum response size to compress (bytes)
	MinSize int
	// Types are the content types to compress
	Types []string
}

type FIDOConfig

type FIDOConfig struct {
	// RPDisplayName is the relying party display name (e.g., "My App")
	RPDisplayName string
	// RPID is the relying party ID (e.g., "example.com")
	RPID string
	// RPOrigins are the allowed origins for WebAuthn requests
	RPOrigins []string
	// Timeout is the timeout for WebAuthn ceremonies (default: 60s)
	Timeout time.Duration
	// Debug enables debug logging
	Debug bool
	// Logger for WebAuthn operations
	Logger *slog.Logger
	// Store is the credential store
	Store FIDOStore
}

FIDOConfig configures WebAuthn/FIDO2 authentication

type FIDOCredential

type FIDOCredential struct {
	ID              []byte                            `json:"id"`
	PublicKey       []byte                            `json:"public_key"`
	AttestationType string                            `json:"attestation_type"`
	Transport       []protocol.AuthenticatorTransport `json:"transport"`
	Flags           webauthn.CredentialFlags          `json:"flags"`
	Authenticator   webauthn.Authenticator            `json:"authenticator"`
	BackupEligible  bool                              `json:"backup_eligible"`
	BackupState     bool                              `json:"backup_state"`
	Created         time.Time                         `json:"created"`
	LastUsed        time.Time                         `json:"last_used"`
}

FIDOCredential represents a WebAuthn credential

func FromWebAuthn

func FromWebAuthn(cred *webauthn.Credential) *FIDOCredential

FromWebAuthn creates a FIDOCredential from webauthn.Credential

func (*FIDOCredential) ToWebAuthn

func (c *FIDOCredential) ToWebAuthn() webauthn.Credential

ToWebAuthn converts FIDOCredential to webauthn.Credential

type FIDOServer

type FIDOServer struct {
	// contains filtered or unexported fields
}

FIDOServer provides WebAuthn/FIDO2 authentication handlers

func NewFIDOServer

func NewFIDOServer(cfg *FIDOConfig) (*FIDOServer, error)

NewFIDOServer creates a new FIDO server

func (*FIDOServer) BeginLogin

func (s *FIDOServer) BeginLogin(c *gin.Context)

BeginLogin starts the FIDO2 authentication ceremony

func (*FIDOServer) BeginRegistration

func (s *FIDOServer) BeginRegistration(c *gin.Context)

BeginRegistration starts the FIDO2 registration ceremony

func (*FIDOServer) FinishLogin

func (s *FIDOServer) FinishLogin(c *gin.Context)

FinishLogin completes the FIDO2 authentication ceremony

func (*FIDOServer) FinishRegistration

func (s *FIDOServer) FinishRegistration(c *gin.Context)

FinishRegistration completes the FIDO2 registration ceremony

func (*FIDOServer) RegisterRoutes

func (s *FIDOServer) RegisterRoutes(r gin.IRouter)

RegisterRoutes registers FIDO2 routes on a Gin router

type FIDOStore

type FIDOStore interface {
	// GetUser retrieves a user by ID
	GetUser(userID []byte) (*FIDOUser, error)
	// GetUserByName retrieves a user by username
	GetUserByName(username string) (*FIDOUser, error)
	// SaveUser saves or updates a user
	SaveUser(user *FIDOUser) error
	// SaveCredential saves a credential for a user
	SaveCredential(userID []byte, credential *FIDOCredential) error
	// GetCredentials retrieves all credentials for a user
	GetCredentials(userID []byte) ([]*FIDOCredential, error)
	// UpdateCredential updates a credential (e.g., sign count)
	UpdateCredential(credentialID []byte, credential *FIDOCredential) error
}

FIDOStore defines the interface for storing and retrieving WebAuthn credentials

type FIDOUser

type FIDOUser struct {
	ID          []byte `json:"id"`
	Name        string `json:"name"`
	DisplayName string `json:"display_name"`
	Credentials []*FIDOCredential
}

FIDOUser represents a WebAuthn user

func GetFIDOUser

func GetFIDOUser(c *gin.Context) (*FIDOUser, bool)

GetFIDOUser retrieves the authenticated FIDO user from the context

func (*FIDOUser) MarshalJSON

func (u *FIDOUser) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler for FIDOUser

func (*FIDOUser) UnmarshalJSON

func (u *FIDOUser) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler for FIDOUser

func (*FIDOUser) WebAuthnCredentials

func (u *FIDOUser) WebAuthnCredentials() []webauthn.Credential

WebAuthnCredentials returns the user's credentials

func (*FIDOUser) WebAuthnDisplayName

func (u *FIDOUser) WebAuthnDisplayName() string

WebAuthnDisplayName returns the display name for WebAuthn

func (*FIDOUser) WebAuthnID

func (u *FIDOUser) WebAuthnID() []byte

WebAuthnID returns the user ID for WebAuthn

func (*FIDOUser) WebAuthnIcon

func (u *FIDOUser) WebAuthnIcon() string

WebAuthnIcon returns the user icon URL (deprecated in WebAuthn spec)

func (*FIDOUser) WebAuthnName

func (u *FIDOUser) WebAuthnName() string

WebAuthnName returns the username for WebAuthn

type HTTPClient

type HTTPClient interface {
	Do(req *http.Request) (*http.Response, error)
}

type HTTPExchange

type HTTPExchange struct {
	Request  *ReplayRequest `json:"request"`
	Response *ReplayResult  `json:"response"`
}

HTTPExchange represents an HTTP request/response pair

type Host

type Host string

type ID

type ID string

type KeyStore

type KeyStore interface {
	// GetPublicKey retrieves a public key by key ID
	GetPublicKey(keyID string) (any, error)
}

KeyStore provides public keys for signature verification

type MemoryAPIKeyStore

type MemoryAPIKeyStore struct {
	// contains filtered or unexported fields
}

MemoryAPIKeyStore is an in-memory implementation of APIKeyStore

func NewMemoryAPIKeyStore

func NewMemoryAPIKeyStore() *MemoryAPIKeyStore

NewMemoryAPIKeyStore creates a new in-memory API key store

func (*MemoryAPIKeyStore) Delete

func (s *MemoryAPIKeyStore) Delete(id string) error

func (*MemoryAPIKeyStore) GetByHash

func (s *MemoryAPIKeyStore) GetByHash(hash string) (*APIKey, error)

func (*MemoryAPIKeyStore) GetByID

func (s *MemoryAPIKeyStore) GetByID(id string) (*APIKey, error)

func (*MemoryAPIKeyStore) GetByPrefix

func (s *MemoryAPIKeyStore) GetByPrefix(prefix string) (*APIKey, error)

func (*MemoryAPIKeyStore) List

func (s *MemoryAPIKeyStore) List() ([]*APIKey, error)

func (*MemoryAPIKeyStore) Save

func (s *MemoryAPIKeyStore) Save(key *APIKey) error

func (*MemoryAPIKeyStore) UpdateLastUsed

func (s *MemoryAPIKeyStore) UpdateLastUsed(id string, t time.Time) error

type MemoryCacheStore

type MemoryCacheStore struct {
	// contains filtered or unexported fields
}

MemoryCacheStore is an in-memory cache store

func NewMemoryCacheStore

func NewMemoryCacheStore() *MemoryCacheStore

NewMemoryCacheStore creates a new in-memory cache store

func (*MemoryCacheStore) Clear

func (s *MemoryCacheStore) Clear() error

func (*MemoryCacheStore) Delete

func (s *MemoryCacheStore) Delete(key string) error

func (*MemoryCacheStore) Get

func (s *MemoryCacheStore) Get(key string) (*CacheEntry, error)

func (*MemoryCacheStore) Set

func (s *MemoryCacheStore) Set(key string, entry *CacheEntry) error

type MemoryFIDOStore

type MemoryFIDOStore struct {
	// contains filtered or unexported fields
}

MemoryFIDOStore is an in-memory implementation of FIDOStore for development/testing

func NewMemoryFIDOStore

func NewMemoryFIDOStore() *MemoryFIDOStore

NewMemoryFIDOStore creates a new in-memory FIDO store

func (*MemoryFIDOStore) GetCredentials

func (s *MemoryFIDOStore) GetCredentials(userID []byte) ([]*FIDOCredential, error)

func (*MemoryFIDOStore) GetUser

func (s *MemoryFIDOStore) GetUser(userID []byte) (*FIDOUser, error)

func (*MemoryFIDOStore) GetUserByName

func (s *MemoryFIDOStore) GetUserByName(username string) (*FIDOUser, error)

func (*MemoryFIDOStore) SaveCredential

func (s *MemoryFIDOStore) SaveCredential(userID []byte, credential *FIDOCredential) error

func (*MemoryFIDOStore) SaveUser

func (s *MemoryFIDOStore) SaveUser(user *FIDOUser) error

func (*MemoryFIDOStore) UpdateCredential

func (s *MemoryFIDOStore) UpdateCredential(credentialID []byte, credential *FIDOCredential) error

type MemoryKeyStore

type MemoryKeyStore struct {
	// contains filtered or unexported fields
}

MemoryKeyStore is an in-memory key store

func NewMemoryKeyStore

func NewMemoryKeyStore() *MemoryKeyStore

NewMemoryKeyStore creates a new in-memory key store

func (*MemoryKeyStore) AddKey

func (s *MemoryKeyStore) AddKey(keyID string, key any)

AddKey adds a key to the store

func (*MemoryKeyStore) GetPublicKey

func (s *MemoryKeyStore) GetPublicKey(keyID string) (any, error)

GetPublicKey retrieves a public key by ID

type MockEndpoint

type MockEndpoint struct {
	Method     string            `json:"method"`
	Path       string            `json:"path"`
	StatusCode int               `json:"status_code"`
	Response   any               `json:"response"`
	Headers    map[string]string `json:"headers,omitempty"`
	Delay      time.Duration     `json:"delay,omitempty"`
	MatchQuery map[string]string `json:"match_query,omitempty"`
	MatchBody  map[string]any    `json:"match_body,omitempty"`
}

MockEndpoint represents a mock API endpoint

func LoadMockEndpointsFromJSON

func LoadMockEndpointsFromJSON(data []byte) ([]*MockEndpoint, error)

LoadMockEndpointsFromJSON loads mock endpoints from JSON

type MockServer

type MockServer struct {
	// contains filtered or unexported fields
}

MockServer provides a configurable mock HTTP server

func NewMockServer

func NewMockServer(logger *slog.Logger) *MockServer

NewMockServer creates a new mock server

func (*MockServer) AddEndpoint

func (ms *MockServer) AddEndpoint(endpoint *MockEndpoint)

AddEndpoint adds a mock endpoint

func (*MockServer) AddEndpoints

func (ms *MockServer) AddEndpoints(endpoints []*MockEndpoint)

AddEndpoints adds multiple endpoints

func (*MockServer) Handler

func (ms *MockServer) Handler() http.Handler

Handler returns the HTTP handler

type OIDC

type OIDC struct {
	Issuer string `json:"issuer" validate:"required,url,max=255"`

	AuthZURL *url.URL
	TokenURL *url.URL
	UserURL  *url.URL

	LogoutURL      *url.URL
	IntrospectURL  *url.URL
	RevocationURL  *url.URL
	DeviceAuthzURL *url.URL

	JWKsURL *url.URL
	// JWKs holds the cold-start key set so legacy callers continue to
	// observe a non-nil reference. The authoritative, auto-refreshing
	// view lives behind jwkCache and is consulted on every JWT
	// validation path so IdP key rotation is picked up without a host
	// process restart (PERF-0043, SEC #150).
	JWKs jwk.Set

	Aud string

	ResponseTypes [][]string
	ResponseModes []string `json:"response_modes_supported"`
	GrantTypes    []string `json:"grant_types_supported"`

	SigningAlgos         []string `json:"id_token_signing_alg_values_supported"`
	SubjectTypes         []string `json:"subject_types_supported"`
	AuthMethods          []string `json:"token_endpoint_auth_methods_supported"`
	AcrValues            []string `json:"acr_values_supported"`
	Scopes               []string `json:"scopes_supported"`
	Claims               []string `json:"claims_supported"`
	CodeChallengeMethods []string `json:"code_challenge_methods_supported"`

	ClaimsParameters bool `json:"claims_parameter_supported"`
	RequestParameter bool `json:"request_parameter_supported"`
	// contains filtered or unexported fields
}

func LoadOIDC

func LoadOIDC(
	ctx context.Context,
	client *Client,
	oidcURL string,
	aud string,
) (*OIDC, error)

LoadOIDC loads the OpenID Connect configuration from the given URL. @client: HTTP client to use for the request. @url: URL of the OpenID Connect configuration endpoint. @aud: Audience of the OpenID Connect configuration endpoint (usually the client ID of the application). @return: OpenID Connect configuration.

func (*OIDC) Authenticate

func (o *OIDC) Authenticate(
	ctx context.Context, input *oapifilter.AuthenticationInput,
) error

ValidateToken middleware verifies a valid Auth0 JWT token being present in the request.

func (*OIDC) ExtractToken

func (o *OIDC) ExtractToken(ctx *gin.Context) (jwt.Token, error)

ExtractToken parses the Authorization HTTP header for valid JWT token and validates it with the JWK keys. Also verifies if the audience present in the token matches with the designated audience as per current configuration.

func (*OIDC) JWT

func (o *OIDC) JWT(ctx *gin.Context)

func (*OIDC) UnmarshalJSON

func (o *OIDC) UnmarshalJSON(data []byte) error

type Option

type Option func(*Client) error

func WithAPIKey

func WithAPIKey(key string) Option

WithAPIKey adds an API key to the client

func WithAutoRefreshToken

func WithAutoRefreshToken(refresher *TokenRefresher) Option

WithAutoRefreshToken creates a client with automatic token refresh

func WithBasicAuth

func WithBasicAuth(username, password string) Option

func WithBearerToken

func WithBearerToken(token string) Option

func WithCircuitBreaker

func WithCircuitBreaker(cb *CircuitBreaker) Option

func WithDebugFile

func WithDebugFile(file string) Option

func WithDoer

func WithDoer(client HTTPClient) Option

func WithHeader

func WithHeader(key, value string) Option

func WithLimiter

func WithLimiter(
	ctx context.Context,
	delay time.Duration,
	retries int,
	concurrency int,
	requestTimeout time.Duration,
) Option

func WithLogger

func WithLogger(log log.Logger) Option

func WithMaxResponseBytes

func WithMaxResponseBytes(n int64) Option

WithMaxResponseBytes sets the ceiling on response-body bytes buffered by ReadJSON. A value <=0 disables the limit (unbounded; use only for trusted upstreams). The default is DefaultMaxResponseBytes (32 MiB) (PERF-0024).

func WithOAuth2

func WithOAuth2(
	clientID, clientSecret, tokenURL string,
	scopes ...string,

) Option

func WithRetry

func WithRetry(cfg *RetryConfig) Option

WithRetry adds retry logic to the client

func WithRoot

func WithRoot(rootURL string) Option

func WithSignature

func WithSignature(signer *Signer) Option

WithSignature adds HTTP signature signing to the client

func WithTransport

func WithTransport(transport http.RoundTripper) Option

type ProxyOption

type ProxyOption func(*proxy) error

func WithCertificate

func WithCertificate(cert, key string) ProxyOption

func WithClient

func WithClient(c *Client) ProxyOption

func WithRewriters

func WithRewriters(r ...Rewriter) ProxyOption

func WithRule

func WithRule(name string, rule Rule) ProxyOption

func WithSubnetFilter

func WithSubnetFilter(subnets ...netip.Prefix) ProxyOption

func WithTLSConfig

func WithTLSConfig(cfg *tls.Config) ProxyOption

type Register

type Register func(*gin.Engine) error

type ReplayRequest

type ReplayRequest struct {
	Method  string            `json:"method"`
	URL     string            `json:"url"`
	Headers map[string]string `json:"headers"`
	Body    []byte            `json:"body,omitempty"`
}

ReplayRequest represents a captured HTTP request for replay

func LoadRequestsFromJSON

func LoadRequestsFromJSON(data []byte) ([]*ReplayRequest, error)

LoadRequestsFromJSON loads replay requests from JSON

type ReplayResult

type ReplayResult struct {
	Request    *ReplayRequest
	StatusCode int
	Headers    http.Header
	Body       []byte
	Duration   time.Duration
	Error      error
	Timestamp  time.Time
}

ReplayResult contains the result of a replayed request

type Replayer

type Replayer struct {
	// contains filtered or unexported fields
}

Replayer replays captured HTTP requests

func NewReplayer

func NewReplayer(client *http.Client, baseURL string, logger *slog.Logger) *Replayer

NewReplayer creates a new request replayer

func (*Replayer) Replay

func (r *Replayer) Replay(ctx context.Context, req *ReplayRequest) *ReplayResult

Replay replays a single request

func (*Replayer) ReplayBatch

func (r *Replayer) ReplayBatch(ctx context.Context, requests []*ReplayRequest, parallel bool) []*ReplayResult

ReplayBatch replays multiple requests

type RequestOption

type RequestOption func(*Client, *http.Request) error

func WithBody

func WithBody(body io.ReadCloser) RequestOption

func WithMethod

func WithMethod(method string) RequestOption

func WithQuery

func WithQuery(query url.Values) RequestOption

func WithRequestAuth

func WithRequestAuth(auth string) RequestOption

func WithRequestHeader

func WithRequestHeader(key, value string) RequestOption

type RetryConfig

type RetryConfig struct {
	// MaxAttempts is the maximum number of retry attempts
	MaxAttempts int
	// InitialDelay is the delay before the first retry
	InitialDelay time.Duration
	// MaxDelay is the maximum delay between retries
	MaxDelay time.Duration
	// Multiplier is the backoff multiplier (default: 2.0)
	Multiplier float64
	// ShouldRetry determines if a request should be retried
	ShouldRetry func(*http.Response, error) bool
}

RetryConfig configures retry behavior

type Rewriter

type Rewriter struct {
	From Host
	To   Host

	Paths   map[string]string
	Headers map[string]http.Header
	Query   map[string]url.Values
}

func (Rewriter) Process

func (r Rewriter) Process(in *http.Request) error

type Rule

type Rule func(w http.ResponseWriter, r *http.Request) error

type Server

type Server interface {
	Serve(context.Context) error
}

func New

func New(reg Register, opts ...ServerOption) (_ Server, err error)

type ServerOption

type ServerOption func(*apiServer) error

func RateLimit

func RateLimit(limit int, duration time.Duration) ServerOption

func WithAPIKeyManager

func WithAPIKeyManager(manager *APIKeyManager, adminAuth gin.HandlerFunc) ServerOption

WithAPIKeyManager adds API key management routes to the server

func WithCORS

func WithCORS(allowedOrigins []string, methods ...string) ServerOption

func WithErrorHandler

func WithErrorHandler(handler func(ctx *gin.Context, err error, i int)) ServerOption

func WithFIDO

func WithFIDO(cfg *FIDOConfig) ServerOption

WithFIDO adds FIDO2 authentication to an API server

func WithHost

func WithHost(host string) ServerOption

func WithImpl

func WithImpl(impl any) ServerOption

func WithMiddleware

func WithMiddleware(middleware ...gin.HandlerFunc) ServerOption

func WithPerIPRateLimit

func WithPerIPRateLimit() ServerOption

WithPerIPRateLimit enables per-remote-IP token buckets instead of global bucket.

func WithPort

func WithPort(port int) ServerOption

func WithServerLogger

func WithServerLogger(logger log.Logger) ServerOption

func WithSwaggerSpec

func WithSwaggerSpec(spec *openapi3.T) ServerOption

func WithTLS

func WithTLS(cert, key string) ServerOption

func WithTrustedProxies

func WithTrustedProxies(proxies []string) ServerOption

func WithValidation

func WithValidation(
	ctx context.Context,
	oidc string,
	audience string,
) ServerOption

type SignatureAlgorithm

type SignatureAlgorithm string

SignatureAlgorithm represents the signature algorithm

const (
	AlgoHMAC_SHA256  SignatureAlgorithm = "hmac-sha256"
	AlgoRSA_SHA256   SignatureAlgorithm = "rsa-sha256"
	AlgoECDSA_SHA256 SignatureAlgorithm = "ecdsa-sha256"
	AlgoED25519      SignatureAlgorithm = "ed25519"
)

type SignatureConfig

type SignatureConfig struct {
	// KeyID identifies the key used for signing
	KeyID string
	// Algorithm is the signature algorithm
	Algorithm SignatureAlgorithm
	// PrivateKey is the private key for signing (crypto.Signer or []byte for HMAC)
	PrivateKey any
	// PublicKey is the public key for verification (crypto.PublicKey or []byte for HMAC)
	PublicKey any
	// Headers are the headers to include in the signature (default: date, digest)
	Headers []string
	// IncludeDigest adds a Digest header with the request body hash
	IncludeDigest bool
	// MaxClockSkew is the maximum allowed clock skew for date validation (default: 5 minutes)
	MaxClockSkew time.Duration
}

SignatureConfig configures HTTP signature signing/verification

type SignatureInfo

type SignatureInfo struct {
	KeyID     string
	Algorithm SignatureAlgorithm
	Headers   []string
	Signature string
}

SignatureInfo contains parsed signature information

type SignatureStats

type SignatureStats struct {
	Total   int64
	Success int64
	Failed  int64
	Errors  map[string]int64
}

SignatureStats tracks signature verification statistics

func NewSignatureStats

func NewSignatureStats() *SignatureStats

NewSignatureStats creates a new stats tracker

func (*SignatureStats) Record

func (s *SignatureStats) Record(err error)

Record records a verification result

func (*SignatureStats) Summary

func (s *SignatureStats) Summary() map[string]any

Summary returns a summary of statistics

type Signer

type Signer struct {
	// contains filtered or unexported fields
}

Signer signs HTTP requests

func NewSigner

func NewSigner(cfg *SignatureConfig) (*Signer, error)

NewSigner creates a new HTTP signature signer

func (*Signer) SignRequest

func (s *Signer) SignRequest(req *http.Request) error

SignRequest signs an HTTP request

type TOKEN

type TOKEN string

type Timeout

type Timeout time.Duration

func (Timeout) D

func (t Timeout) D() time.Duration

type TokenRefresher

type TokenRefresher struct {
	// contains filtered or unexported fields
}

TokenRefresher automatically refreshes OAuth2 tokens

func NewTokenRefresher

func NewTokenRefresher(config *oauth2.Config, token *oauth2.Token, logger *slog.Logger) *TokenRefresher

NewTokenRefresher creates a new token refresher

func (*TokenRefresher) Client

func (tr *TokenRefresher) Client(ctx context.Context) *http.Client

Client returns an HTTP client with automatic token refresh

func (*TokenRefresher) OnRefresh

func (tr *TokenRefresher) OnRefresh(fn func(*oauth2.Token))

OnRefresh sets a callback for when the token is refreshed

func (*TokenRefresher) Start

func (tr *TokenRefresher) Start(ctx context.Context) error

Start starts the automatic token refresh

func (*TokenRefresher) Stop

func (tr *TokenRefresher) Stop()

Stop stops the automatic refresh

func (*TokenRefresher) Token

func (tr *TokenRefresher) Token() *oauth2.Token

Token returns the current token

type TrafficDiff

type TrafficDiff struct {
	Type        string `json:"type"` // "method", "path", "header", "body", "status"
	Field       string `json:"field,omitempty"`
	Expected    any    `json:"expected"`
	Actual      any    `json:"actual"`
	Description string `json:"description"`
}

TrafficDiff represents a difference between two HTTP exchanges

type TrafficDiffer

type TrafficDiffer struct {
	// contains filtered or unexported fields
}

TrafficDiffer compares HTTP traffic

func NewTrafficDiffer

func NewTrafficDiffer() *TrafficDiffer

NewTrafficDiffer creates a new traffic differ

func (*TrafficDiffer) Compare

func (td *TrafficDiffer) Compare(expected, actual *HTTPExchange) []*TrafficDiff

Compare compares two HTTP exchanges

func (*TrafficDiffer) IgnoreFields

func (td *TrafficDiffer) IgnoreFields(fields ...string)

IgnoreFields sets JSON fields to ignore in body comparisons

func (*TrafficDiffer) IgnoreHeaders

func (td *TrafficDiffer) IgnoreHeaders(headers ...string)

IgnoreHeaders sets headers to ignore in comparisons

type Verifier

type Verifier struct {
	// contains filtered or unexported fields
}

Verifier verifies HTTP signatures

func NewVerifier

func NewVerifier(keyStore KeyStore, maxClockSkew time.Duration) *Verifier

NewVerifier creates a new HTTP signature verifier

func (*Verifier) VerifyRequest

func (v *Verifier) VerifyRequest(req *http.Request) error

VerifyRequest verifies an HTTP request signature

Directories

Path Synopsis
cmd
apimap command
internal
ui

Jump to

Keyboard shortcuts

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