staticserver

package
v0.0.0-...-821bc36 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package staticserver is the shared static-site origin proxy (w1/m21): one always-on HTTP server, behind Traefik, that serves every static_site App's built output from the object-store origin. It dispatches by request Host to the App's current revision prefix, fetches objects with signed GETs (the bucket stays private), and applies the App's edge rules — redirects/rewrites (Spec.Routes, Render's /routes) and custom response headers (Spec.Headers, Render's /headers) — plus index.html defaulting and SPA fallback. Objects are cached in memory: a revision prefix is immutable, so a hit is never stale, and caching keeps egress inside the object store's fair-use budget.

The handler is decoupled from Kubernetes and S3 via two seams — Resolver (host → Site) and Origin (key → bytes) — so the edge-rule behavior is unit tested with fakes (staticserver_test.go); the real wiring lives in resolver.go (a controller-runtime cache over static_site Apps) and s3origin.go.

Index

Constants

View Source
const DefaultCacheBytes = 256 << 20 // 256 MiB

DefaultCacheBytes is the default in-memory object cache budget (main.go may override it via BEX_STATIC_CACHE_BYTES).

Variables

View Source
var ErrNotFound = errors.New("staticserver: object not found")

ErrNotFound is what an Origin returns when the requested key does not exist.

View Source
var ErrObjectTooLarge = errors.New("staticserver: object too large")

ErrObjectTooLarge is what an Origin returns when a tenant-controlled object exceeds maxOriginObjectBytes, so a single oversized asset can't be allocated whole and OOM the shared single-replica server (codex-security #10).

View Source
var ErrOverloaded = errors.New("staticserver: origin fetch capacity reached")

ErrOverloaded is returned when the origin-fetch admission gate is at capacity; the handler sheds it as 503 rather than buffering unbounded misses (finding 12).

Functions

This section is empty.

Types

type CachedResolver

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

CachedResolver serves the host→Site map by periodically listing static_site Apps and atomically swapping an immutable snapshot. Polling (rather than an informer) keeps the standalone binary simple; static-site config changes are infrequent and a few seconds of edit propagation is acceptable (Render is not instant either). Only Apps that are static_site, not suspended, and already published (Status.ActiveRevision set) are served.

func NewCachedResolver

func NewCachedResolver(reader client.Reader, namespace, baseDomain string) *CachedResolver

NewCachedResolver builds a resolver reading Apps in namespace (empty = all) from reader; baseDomain expands an App's Expose flag to "<name>.<baseDomain>".

func (*CachedResolver) Refresh

func (c *CachedResolver) Refresh(ctx context.Context) error

Refresh rebuilds the host→Site snapshot from the current static_site Apps.

func (*CachedResolver) Resolve

func (c *CachedResolver) Resolve(host string) (Site, bool)

Resolve returns the Site serving host, or ok=false when none does.

func (*CachedResolver) Run

func (c *CachedResolver) Run(ctx context.Context, interval time.Duration, onErr func(error))

Run refreshes the snapshot every interval until ctx is cancelled, after an immediate first refresh. A refresh error is non-fatal: the previous snapshot keeps serving until the next successful poll.

type Handler

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

Handler is the static-site origin proxy. Construct with New.

func New

func New(resolver Resolver, origin Origin, cacheBytes int64) *Handler

New builds a Handler over a Resolver and Origin. cacheBytes caps the in-memory object cache (0 disables caching).

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Object

type Object struct {
	Body        []byte
	ContentType string // from the origin; may be empty (then inferred from extension)
}

Object is a fetched origin object.

type Origin

type Origin interface {
	// Get returns the object at key, or ErrNotFound if it does not exist.
	Get(ctx context.Context, key string) (Object, error)
}

Origin fetches objects from the static-site object store by key.

type Resolver

type Resolver interface {
	// Resolve returns the Site for host, or ok=false when no static_site App
	// serves that host.
	Resolve(host string) (Site, bool)
}

Resolver maps a request host to its Site config.

type S3Origin

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

S3Origin is an Origin backed by an S3-compatible object store (Wasabi/Hetzner today). It signs each GET, so the bucket stays private — the same credentials the publish Job uses to write (AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY from the environment). Path-style addressing is used because S3-compatibles rarely support virtual-host-style bucket subdomains.

func NewS3Origin

func NewS3Origin(ctx context.Context, endpoint, region, bucket string) (*S3Origin, error)

NewS3Origin builds an S3-backed Origin. Credentials come from the standard AWS environment (the ServiceAccount-mounted Secret in-cluster). endpoint is the S3-compatible base URL (e.g. https://s3.eu-central-2.wasabisys.com); region defaults to us-east-1 when empty (required by the SDK even though path-style + a custom endpoint ignores it for routing).

func (*S3Origin) Check

func (o *S3Origin) Check(ctx context.Context) error

Check verifies the configured identity can list the dedicated origin bucket. It is called once at startup so a missing, wrong, or over-rotated read Secret fails the static-server Deployment immediately instead of leaving a Ready pod that answers every tenant request with an opaque 503.

func (*S3Origin) Get

func (o *S3Origin) Get(ctx context.Context, key string) (Object, error)

Get fetches key from the bucket, mapping a missing object to ErrNotFound.

type Site

type Site struct {
	AppID    string                     // first object-key segment (legacy sites)
	Revision string                     // last key segment (e.g. "rev-7"); immutable per revision
	Prefix   string                     // full object-key prefix including trailing slash when known
	Routes   []appv1alpha1.StaticRoute  // ordered redirect/rewrite rules
	Headers  []appv1alpha1.StaticHeader // custom response headers by path
}

Site is the serving config for one static_site App, keyed by request host.

Jump to

Keyboard shortcuts

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