Blink Kernel
Book a call

LFW Documentation

LFW Documentation

Compiler-owned documentation for the lfw kernel, rendered through the same typed viewmodels the website uses in production. Start with kernel-to-distro because it decides where any fix belongs. Then: adminapi is the operator API surface; alfred is the code generator; html is the runtime renderer for every page you see here.

Runtime provenance

Where this page gets its data

This documentation page renders the same Head object production pages use. The page-specific SEO fields are authored in alfred. Customer identity and logo URLs come from compass after it reads customer.json. The generated viewmodel combines both before _head.html writes tags.

Live Head values

Title
LFW Documentation
Description
Compiler-owned documentation for the lfw kernel: adminapi, alfred, and html explained through the typed viewmodels this website renders.
Customer/site name
Blink Kernel
Canonical URL
https://cink.in/
CSS links
2 registered
JS scripts
1 registered

Logo URLs from Head

Blink Kernel dark logo
https://imagedelivery.net/nFGdEXnBqGfMypCF7IGeUQ/96e7d7e7-6a92-4418-605c-277e685e1300/public
Blink Kernel light logo
https://imagedelivery.net/nFGdEXnBqGfMypCF7IGeUQ/96e7d7e7-6a92-4418-605c-277e685e1300/public

Customer/site name

Source: customer.json, loaded into compass.Config.Customer.Name

Flow: appboot.CustomerData copies it to viewmodels.CustomerData.Name; html/viewmodels.newPageHead assigns Head.OgSiteName.

Owner: Customer configuration owns the value; html owns the mapping.

Light and dark logo URLs

Source: customer.json logo fields: company_logo, company_logo_light, company_logo_dark

Flow: appboot.CustomerData copies the logo fields; CustomerLogoLightURL and CustomerLogoDarkURL prefer those overrides and fall back to LogoPathForSite.

Owner: Customer configuration owns overrides; html owns the typed fallback rule.

Title and description

Source: alfred/website/pages_distro.go homeTitle and homeDescription

Flow: newIndexData returns core.AlfredHead; alfred generates buildIndex; buildIndex calls newPageHead with those values.

Owner: Alfred page data owns page-specific SEO copy.

Canonical URL and Open Graph description

Source: alfred/website/pages_distro.go homeCanonicalURL and homeOGDescription

Flow: newIndexData returns them in core.AlfredHead; generated buildIndex assigns Head.CanonicalURL and Head.OgDescription.

Owner: Alfred page data owns route-specific metadata.

CSS links and font CSS

Source: core.ServiceSiteCSSURL and core.ServiceSiteFontsCSSURL

Flow: generated buildIndex appends default CSS when the page has no CSSLinks override; authored CSSLinks replace that default for special pages.

Owner: core owns URL contracts; alfred owns whether a page uses defaults or overrides.

Page JavaScript

Source: alfred/website/pages_distro.go AlfredHeadAssets.JSScripts

Flow: newIndexData names core.TmplIndex; generated buildIndex turns it into the typed dist URL for this app and page.

Owner: Alfred page data owns the page's required scripts.

JSON-LD

Source: alfred/website/pages_distro.go websiteHomeJSONLD plus customer identity from compass

Flow: generated buildIndex assigns the page graph; MergeWebsiteOrganizationJSONLD adds the Organization node from CustomerData; Head.JSONLDHTML validates and renders it.

Owner: Alfred owns page schema; html owns the customer Organization merge and output boundary.

Rendered HTML head

Source: templates/_head.html

Flow: _head.html reads the final Head struct and emits meta tags, links, scripts, JSON-LD, and critical CSS.

Owner: html templates own presentation only; typed Head owns the contract.

kernel-to-distro

Kernel → Distro: the bug-fix flow

The canonical flow for fixing bugs across the lfw kernel and its distros. One question decides everything: is this the kernel's bug, or this distro's bug? Then follow the matching lane — no shortcuts, no hot-patching a kernel bug inside one client repo.

lfw is a Linux-kernel-style shared framework. Every client repo (starter, strikepointsolutions, mjdcontracting, …) is a distro scaffolded from it with `lfw new`. A distro carries a full copy of the framework source with its module path relocalized, keeps a permanent `upstream` link back to the kernel, and pulls shared fixes down.

Kernel

github.com/offGridSoft/blink-kernel/v2026 — owns auth, the framework, transport, crypto routing (professor), the CLI control plane (cmd/lfw), core/, and everything shared. The single source of truth for framework behavior.

Distro

A client repo — owns its branding, content, templates, CSS/JS, customer bindings, and env. It has an `upstream` remote pointing at the kernel and a `.lfw-version` marker recording the last kernel SHA it has absorbed.

The one question

Ask: if I fix this only here, will every other distro still be broken? Yes → kernel bug → Lane B: the defect is in shared framework code; fix it in the kernel, ship it, then cherry-pick it down. No → distro bug → Lane A: the defect is this distro's own content or branding; fix it here and only here. Edge case: a kernel defect that only manifests through distro content is still a kernel bug — fix the framework in the kernel.

Lane A — distro-only fix

The defect is this distro's and nobody else's (content, branding, this distro's policy).

  1. Branch git checkout -b bug-<slug> in the distro.
  2. bug red Write the failing regression first to prove the bug; commit 'red: add regression for BUG-###'. No skipping straight to the fix.
  3. Fix Change the distro-owned files.
  4. bug green Stage the fix; commit 'green: fix BUG-###'. The tool refuses if the red proof metadata is missing.
  5. Gates lfw check (lint + test + witness) — test excludes cmd/lfw on a distro — plus fieldalignment ./... for any touched Go.
  6. Ship Commit and push to the distro's origin. Nothing goes upstream.

Lane B — kernel bug (stop · switch · fix · gate · ship · cherry)

The defect is in shared framework code. Stop touching the distro the moment you identify it.

  1. Stop & switch Leave the distro working tree clean; cd to the kernel; branch bugfix-<slug> off main.
  2. Red → green bug red (a failing kernel regression), then fix the framework honoring doctrine — crypto via professor, no unsafe, no shims, ≤10 cyclomatic complexity, typed Validate() boundaries — then bug green.
  3. Full gates lfw check over ./... with no exclusions, plus fieldalignment ./... (the gate most often forgotten). Land it as one coherent commit.
  4. Ship the kernel Push and merge to main so the fix lands on upstream/main; record the merged kernel SHA.
  5. Cherry-pick In each affected distro: lfw cherry <sha> — it refuses a dirty tree, fetches upstream, cherry-picks, relocalizes the module path, then runs go mod tidy + go build. Use lfw sync --apply to absorb everything new upstream.
  6. Verify & ship distro lfw check in the distro, verify headlessly, push origin. Repeat for every other affected distro.

Forbidden

  • Never hot-patch a kernel bug only inside a distro — the next distro stays broken and the kernel never learns.
  • Never cherry push distro-owned paths (templates/, static/, customer/, env/) upstream — branding does not belong in the kernel.
  • Never hand-edit generated paths (dist/, witness_testimony/, go.sum, .lfw-version, compiled gen HTML).
  • Never skip bug red before the fix, or fieldalignment before declaring a Go commit done.
  • Never advance .lfw-version by hand — lfw sync owns it.

Framework foundations

adminapi

adminapi

The HTTP endpoints an operator uses to manage user accounts: list, block, unblock, change role, hard-delete, read stats, read geo signals, and download an export. Nine routes, all behind authentication and a permission check. No HTML pages live here — only JSON.

Every request is gated twice on purpose. Middleware decides whether the request reaches the admin surface; then the handler itself re-checks the caller's role with policy.Evaluate. That is defense in depth: even if the middleware were wired wrong, the handler still refuses. Validation runs twice too — the incoming request is validated, and so is the outgoing response — so nothing malformed can enter and nothing malformed can escape.

Vocabulary

HTTP handler
A Go function that receives a request and writes a response. Here they look like func(ctx, request) (response, error); the framework turns that into a real http.Handler.
Route
A method plus a compiler-owned path constant, e.g. GET core.RouteAPIAdminUsersVersioned, bound to one handler.
Middleware
A function that wraps a handler to run something before or after it — check a login cookie, a CSRF token, log. Wrapping is an onion: outer(inner(handler)); the outer layer runs first.
RBAC
Role-Based Access Control: what you may do depends on your role (super_admin, admin, staff, customer, plus an invalid unknown). An action is a thing you might be allowed to do; the policy package owns which roles may do which actions.
core.ULID
A 26-character sortable unique ID used for user IDs — a nicer UUID. The all-zero ULID means 'not set'; id.IsZero() checks for it.
Store
The database behind a Go interface. Handlers never touch SQL; they call methods like st.FindByID(ctx, id).
WriteTx
A database transaction. You hand it a function; everything in it commits or rolls back together. Inside the callback you read and write through tx, not the store.
Ledger
An append-only audit log. Every meaningful change writes a ledger entry, so there is a permanent, tamper-evident record of who did what and when.
Validate()
Almost every type has a Validate() error method that rejects malformed values at a clear boundary instead of passing bad data around silently.
Sentinel error
A named, comparable error like core.ErrForbidden, tested with errors.Is, so the HTTP layer can map it to the right status code.
Fail closed
When something required is missing or unclear, deny or error rather than allow. This package fails closed everywhere.
CSRF
Cross-Site Request Forgery: an attack that makes your browser send a state-changing request with your cookies. The defense is a secret token the attacker can't know; mutation routes require it, read routes don't.

Reads

handleListUsers
Checks ViewAdmin + ReadUser, pages users from the store, projects each into the safe UserSummary view, and reports what the caller may do so the UI can show or hide buttons.
handleUserDetails
Like list but for one user, returning the richer UserExport view.
handleGetStats
Checks ViewAdmin and reads aggregate counts. If no stats reader exists, fails closed with Forbidden rather than fabricating an empty result.
handleGetGeo
Checks the stronger ManageBlock because IP and location data is sensitive. With no geo reader configured it returns a valid 'feature off' response, not an error.
Projection
A user record contains secrets — password hash, token anchors, codes. Handlers never serialize those; they project each user into a UserSummary/UserExport allow-list, guarded by Validate() and field tests.

The life of one request

A request such as POST core.RouteAPIAdminBlockVersioned flows through layers. By the time the handler runs, the request is already parsed and validated; the handler's job is authorization plus business logic, nothing else.

  1. private middleware Is this a logged-in session at all?
  2. admin middleware Does the request reach the admin surface?
  3. CSRF middleware Mutations only — is the anti-forgery token valid?
  4. transport wrapper Parse the JSON body, parse path and query, and Validate() the request. The wrapper is transport.JSONWithParams: bind body, FromHTTPRequest, Validate, call the handler, then write api.OK (which validates the response) or api.Fail (which maps the error to a status).
  5. handler Check the caller's permission with policy.Evaluate, do the work, return a typed response.
  6. response wrapper Validate() the response, then encode it to JSON.
  • Permission is checked twice, in two layers: the admin middleware gate and the handler's own policy check.
  • Validation is checked twice: the incoming request and the outgoing response.

Who is allowed to act

Every handler starts by checking permission through one small set of helpers. The gate decides whether a request reaches the admin surface at all; the handler decides whether this specific caller has the specific permission for this specific action.

  1. Who is the caller? resolveCallerRole reads auth.InfoFrom(ctx). If the caller is anonymous (zero UserID) it returns ErrUnauthorized — before any role lookup, so anonymous is always 401, never 403.
  2. May this role act? requirePermissionsRole resolves the role, then calls policy.Evaluate(role, action) for each required action. Any non-Allow returns ErrForbidden naming the failing action.
  3. Named gates Tiny one-liners — requireAdmin, requireManageBlock, requireDeleteUser, requireUpdateUserRole, requireViewLedger, requireAdminReadUser — so the compiler, not a human, tracks which action each route needs. Adding a gate is one line.
Authorization outcomes
SituationErrorStatus
No authenticated callercore.ErrUnauthorized401
Role checker missingcore.ErrAdminapiRoleCheckerNotConfigured500
Authenticated but lacks permissioncore.ErrForbidden403

Changing state safely

The four mutation handlers — block, unblock, role, hard-delete — share one shape. Learn it once.

Why read the user inside the transaction

Problem: If you read the user before opening the transaction, a concurrent change — say a password change — can land between your read and your write. Your write then saves the stale snapshot and silently erases the other change. That is a lost update.

Fix: Do the FindByID inside WriteTx so the read and the write are one atomic transaction. The database serializes them, so there is no lost update and the ledger snapshot reflects the actual committed state.

  1. Permission Refuse early if the caller may not do this.
  2. Self-action guard An admin cannot block, unblock, demote, or delete themselves — this prevents permanent self-lockout.
  3. Write transaction Everything below is all-or-nothing inside store.WriteTx.
  4. Fresh read + transition check Read the user fresh inside the transaction, then check the state transition is legal.
  5. Mutate, audit, commit Change the user, build a ledger entry from the fresh snapshot, and commit the user change and the ledger entry together.
Mutation handlers
HandlerPermissionRuleEffectExtra
handleBlockUserManageBlocktransition to Locked must be validstatus to Lockedsets LockoutUntil and LockoutReason; bumps AnchorVersion to invalidate refresh tokens
handleUnblockUserManageBlocktransition to Active must be validstatus to Activeclears the lockout fields
handleUpdateUserRoleUpdateUserRolethe actor/current/next role assignment must be allowedrole onlyafter commit, invalidates the cached role decision; fails closed if that fails
handleHardDeleteUserDeleteUseruser must already be soft-deleteddeletes the rowwrites a GDPR-redaction ledger entry; the irreversible final step

The export system

GET core.RouteAPIAdminExportVersioned with the segment query downloads a data bundle. It must handle huge data and a ledger split across hot and cold storage.

everything
the default when ?segment= is empty: stats + users + ledger
users
just users
stats
just aggregate stats
ledger
just the audit log
  1. It can be huge Buffering everything could exhaust memory, so export streams where it can and caps the total at AdminapiMaxExportEntries; when the cap is hit it sets truncated: true so the client knows it is partial.
  2. Ledger lives in two places Recent entries are hot (the main store); old entries may be in cold archive storage. Export merges them and keeps them ordered.
  1. stream ledger segment is ledger and no archive is configured — stream straight from store pages.
  2. stream users segment is users — stream paged store reads.
  3. buffer then encode everything, stats, or ledger-with-archive — assemble in memory (needed to merge and sort archive data) then encode.
  • The merge sorts by CreatedAt, then Seq, then ID, so the same data always exports in the same order.
  • Every path sets Cache-Control: private, no-store and a Content-Disposition attachment filename, logs faults with a request ID, and never caches an export.

ExportSegment — a closed enum done the project's way

ExportSegment is a uint8 enum and shows the standard pattern for a small set of named values that must never be corrupted.

SegmentUnknown
0 — the invalid zero value
SegmentEverything
stats + users + ledger
SegmentUsers
users only
SegmentStats
stats only
SegmentLedger
ledger only
Valid / IsValid
is this one of the four real values?
String
the wire name, or 'unknown'
MarshalJSON
refuses to encode an invalid segment
UnmarshalJSON
refuses to decode an unknown string
parseExportSegment
the single string-to-enum lookup both the query parser and UnmarshalJSON use

Routes

HTTP routes
MethodPathGateHandlerPermission
GET/v1/admin/usersprivate + adminhandleListUsersActionViewAdmin and ActionReadUser
GET/v1/admin/users/{id}/detailsprivate + adminhandleUserDetailsActionViewAdmin + ActionReadUser
GET/v1/admin/statsprivate + adminhandleGetStatsActionViewAdmin
GET/v1/admin/geoprivate + adminhandleGetGeoActionManageBlock — not the weaker ViewAdmin, because IP and location data is sensitive
GET/v1/admin/exportprivate + adminhandleExportActionViewLedger
POST/v1/admin/block/{id}private + admin + CSRFhandleBlockUserActionManageBlock
POST/v1/admin/unblock/{id}private + admin + CSRFhandleUnblockUserActionManageBlock
PATCH/v1/admin/users/{id}/roleprivate + admin + CSRFhandleUpdateUserRoleActionUpdateUserRole
DELETE/v1/admin/users/{id}private + admin + CSRFhandleHardDeleteUserActionDeleteUser

Request contracts

ListUsersRequest
Source: ?limit= and ?after= query
Rejects: limit outside 1..100
ListGeoSignalsRequest
Source: ?limit= query
Rejects: limit outside 1..100
ExportRequest
Source: ?segment= query
Rejects: a Segment that isn't a known enum value
AdminUserDetailsRequest
Source: {id} path
Rejects: zero ULID
BlockRequest
Source: {id} path + reason JSON body
Rejects: zero ULID; empty reason; reason over AdminapiMaxFieldLen
UnblockRequest
Source: {id} path
Rejects: zero ULID
UpdateUserRoleRequest
Source: {id} path + role JSON body
Rejects: zero ULID; a role that isn't a known enum value
HardDeleteUserRequest
Source: {id} path
Rejects: zero ULID

Response contracts

UserSummary
Source: safe projection of a user for list views
Rejects: missing ID; invalid Status or Role enum
UserExport
Source: richer safe projection of one user
Rejects: same as UserSummary
ListUsersResponse
Source: the users list plus the caller's capabilities
Rejects: any invalid user summary or assignable role
LedgerExportEntry
Source: audit-log row in an export
Rejects: missing ID; invalid Type; negative Seq or DurationNs
StatsResponse
Source: aggregate counts
Rejects: negative TotalUsers
GeoSignalResponse
Source: one geo/risk signal
Rejects: invalid EventType; non-finite or out-of-range Latitude/Longitude
OKResponse
Source: success-only acknowledgement
Rejects: OK that is not true

Error-handling philosophy

Typed sentinels, not strings
Code returns named errors and callers test with errors.Is; the HTTP layer maps each to a status code. Never compare error text.
Wrap with context, preserve identity
Handlers wrap errors with a breadcrumb using %w, so errors.Is still finds the original sentinel deep in the chain while logs show where it happened.
Fail closed
Missing middleware is a 500 on every affected route; a missing role checker denies; a missing stats reader is forbidden; an unknown enum is rejected. The safe default is always no.

System fit

  • Constructed at startup with a real store and a policy.Worker as the role checker.
  • Auth, admin, and CSRF middleware come from the auth layer and are wired with the With...MW methods.
  • Register mounts the routes onto the application router through the switchboard package.
  • Ledger writes share the same ledgerwriter strategy the regular user API uses, so audit entries stay consistent.
  • The optional archive reader is wired only when cold ledger storage exists.

Tests

adminapi_test.go
Shared fakes plus handler tests, the anonymous-vs-nil-checker gate matrix, and JSON-field allow-list tests proving no secret user fields leak into exports.
regression_test.go
One test per historical bug — fail-closed wiring, read-inside-transaction, streaming, the enum bouncer — pinning behaviors so they can't regress.
hostile_test.go
Adversarial tables for the pure logic: the segment dispatcher's full matrix, the ledger comparator's precedence, and the merge/cap behavior.
validation_hostile_test.go
Adversarial tables for every Validate(): geo lat/long boundaries including NaN and Inf, the full ledger.Type and user.Role enum domains, int extremes, and a 'which gate fired' check.
contracts_test.go
Error-identity ratchets, format-string verb checks, and the validate-before-wire matrix for every response type.

alfred

alfred

A dev-only code generator: it reads the page definitions you author by hand and writes the Go viewmodels, HTML templates, and CSS/JS stubs the app needs to serve them. It never runs in production — it runs when you type a CLI command, produces files, and exits.

A page in this framework needs several files that must all agree: an HTML template, a typed Go viewmodel holding the data it renders, catalog code mapping the URL to that viewmodel, and CSS/JS entry points. Maintained by hand they drift, and you find out at runtime. alfred makes one thing the source of truth — the page definition you author — and generates everything else from it. Generated files are reproducible: delete them, run alfred, get them back identically.

Vocabulary

Service / lane
The app ships as four services: website (public), webapp (logged-in app), admin (operator console), and api (JSON API). Most of alfred's work is per-service. core.Service is the typed enum.
Site
One service (usually website) can serve several sites: the main bare-domain site plus optional campaign/tenant subdomains. core.SiteOrMain(s) folds an empty site to 'main' everywhere.
Template
An HTML file with {{ }} placeholders (Go's html/template). Identified by a core.Tmpl* constant.
Viewmodel
The typed Go struct a template renders. The template's root dot is a viewmodel value, so {{.Copy.Title}} reads viewmodel.Copy.Title. Lives in html/viewmodels/<service>/.
Page route
A core.PageRouteSpec declaring one stable route identity, service, method, site, path, template, access kind, and page-data requirement.
Page constructor
A function returning a page's <head> data (title, description, scripts, JSON-LD). One per page. You write these.
Page data
Optional authored content for a page, as a typed Go struct that implements core.AlfredPageData. alfred reflects it into the generated viewmodel. This very page's IndexData is an example.
Scaffold vs Validate
alfred's two verbs. Scaffold creates/regenerates files (write). Validate is a read-only check that disk matches what alfred would generate and that nothing is missing.
Finding
alfred never panics for a normal problem; it returns []Finding (a message + a severity Level: Info, Warn, Error). The CLI prints them and exits non-zero if any are Error.
*_gen.go
By convention a file whose name ends in _gen.go is generated ('// Code generated by alfred; DO NOT EDIT'). Never hand-edit one; change the source and regenerate.

Core concepts

WRITE side vs GENERATED side
Files under alfred/ are authored — the source of truth (the page intents, constructors, and page data). Files under html/viewmodels/, html/catalog_*_gen.go, and api/*_gen.go are produced by 'lfw prepare', checked in, compiled into production, and never hand-edited. The template you author reads the generated viewmodel's fields. Trace it: alfred/website/index_distro.go (IndexData) → prepare → html/viewmodels/website/index_gen.go (IndexPage) → templates/website/starter_index.html renders {{.Copy...}}.
Scaffold vs Validate share one generator
Validate is 'scaffold, but compare instead of write': it re-generates everything in memory and diffs it against disk. Because the same code backs both, the on-disk generated files and the authored source cannot silently drift.
The page-data reflector is a strict gate
pageDataFields uses reflection to turn your page-data struct into viewmodel source. Its output must compile, so it accepts only string, bool, the platform int, slices of those, and named UNEXPORTED structs of those. Everything else is rejected: float, non-platform integers (int64/uint/byte), complex, interface, chan, func, array, map, pointer, exported nested type names, and empty structs. Nested type names must be lowercase because the generated package owns those names.
Idempotent and fail-closed
Scaffold creates files only if absent (atomic O_CREATE|O_EXCL), never clobbering your edits, and regenerates the reproducible _gen.go files every run. A page that requires page data but has none fails the build; an unknown site in the generated catalog returns nil rather than silently serving the main site.

Methods

ScaffoldWebsite / ScaffoldWebapp / ScaffoldAdmin
Regenerate all of a service's files for every registered site. The *Site variants (ScaffoldWebsiteSite, etc.) do one site.
ScaffoldAPI
Generates the API route catalog plus request/response structs and their FromHTTPRequest binders.
ValidateWebsite / ValidateWebapp / ValidateAdmin / ValidateAPI
Read-only checks: gen files present and byte-equal to expected, templates and CSS/JS entry points present, no duplicate routes per service/site.
SitesForService / PrintFindings
SitesForService lists the registered sites for a page service. PrintFindings writes findings to stderr with icons and returns true if any are Error so the CLI can set its exit code.

The codegen pipeline

Follow one page from declaration to generated Go.

  1. Declare the page Add a PageRouteSpec in core/routes_<service>_distro.go. RequiresPageData means authored content must exist or preparation fails.
  2. Resolve specs into PageRoutes resolvePageSpecs validates each compiler-owned spec, requires its constructor and any required page data, then builds Alfred's generation record.
  3. Reflect page data into a viewmodel pageDataFields walks your struct and emits the viewmodel's field list and nested type definitions — only for the accepted kinds described above.
  4. Field alignment The generator reorders fields to minimize struct padding so the output passes the fieldalignment linter. Field order in your authored struct does not matter.
  5. Emit gofmt'd Go It builds the viewmodel struct + build function, the per-service Build aggregator, and the catalog (URL→page) with a fail-closed default. Every emitted string runs through format.Source before being written.
  6. Constants, not magic strings When a value is a known core constant (a route, a field name), core_const_expr.go emits the constant expression (core.RouteIndex) instead of a quoted string, keeping generated code coupled to the kernel's contracts.

Level — finding severity

A typed uint8 with the full safety kit, so an unknown severity errors rather than rendering blank.

Info
progress, e.g. 'created X' / 'exists X'
Warn
suspicious but proceed
Error
the build is wrong and must stop
Valid / IsValid
is this one of the three real values?
String
the lowercase label, or unknown(n)
MarshalText / UnmarshalText
reject out-of-range values with ErrAlfredInvalidLevel
MarshalJSON / UnmarshalJSON
the same contract over JSON

RouteKind — page visibility

A typed uint8 that appears in generated catalog code and decides which middleware pipeline wraps a page. It is a security boundary, so the zero value is deliberately invalid — an unclassified page must not default to public.

KindUnknown
0 — the invalid zero value
KindPublic
no auth
KindPrivate
logged-in
KindAdmin
admin-gated
Valid / IsValid
rejects KindUnknown and out-of-range
String
the wire label, or unknown
MarshalJSON / UnmarshalJSON
refuse to encode/decode an invalid kind
parseRouteKind
the single string-to-enum lookup the JSON path uses

Findings instead of exceptions

Findings, not panics
Ordinary problems are returned as []Finding, not raised. The CLI prints them and exits non-zero when any are Error.
Severity decides the build
Error stops the build; Warn is suspicious but proceeds; Info is progress output.
Fail closed
Missing required page data errors the build; an unknown site in a generated catalog returns nil rather than aliasing the main site; the reflector rejects any unsupported field kind.

System fit

  • Invoked by the dev CLI, never by the production binary.
  • Add a page: declare a PageRouteSpec in core, add its constructor, and add a core.AlfredPageData struct when it carries authored content.
  • 'lfw prepare <service>' scaffolds missing assets and regenerates the _gen.go viewmodel/catalog files.
  • 'lfw doctor' (Validate) confirms disk matches generated output and nothing is missing.
  • The generated files are checked into the distro and compiled into the production binary; alfred itself is never imported by production code.

Tests

alfred_test.go
Full Scaffold→Validate round-trips for every service, plus goleak to catch leaked goroutines.
gen_test.go
The codegen engine: the page-data reflector accepting valid kinds and rejecting unsupported ones, field-alignment correctness, and the invariant that emitted Go is always gofmt-able.
hostile_upgrades_test.go
The reflector's full unsupported-kind matrix (float/uint/int64/complex/interface/chan/func/array/bad-slice-elements + recursion), the accept-implies-valid-Go invariant, an exhaustive title()/buildFuncName() table, and clonePageIntentsForSite with mutation-isolation.
boundary_buckets_test.go
The Level and RouteKind enums exhausted across all 256 byte values, with JSON/text round-trips and errors.Is on the invalid sentinels.
registry_test.go
resolvePageSpecs returns the correct typed identity for malformed specs, missing constructors, and missing required page data without leaking partial routes.

html

html

The runtime that serves server-rendered HTML pages. It takes a URL, finds the right page, fills it with data, and writes the HTML. It powers the three page services — website, webapp, and admin. The api service does not use it; that lane speaks JSON.

There is one description of your pages, and html can serve it three ways depending on the environment: in prod from a pre-rendered, pre-compressed byte blob (zero allocations); in dev by re-parsing templates from disk on every request (hot reload, with a rich error page); and as a stub that 404s everything when unwired. The page content is identical across modes — only how the bytes are produced differs. The page data itself comes from the viewmodels alfred generates.

Vocabulary

Service
One of the four services: website, webapp, admin, api. html serves the first three.
Site
One service (usually website) can serve several sites — the main domain plus tenant subdomains. core.SiteOrMain folds an empty site to 'main'.
Template
An HTML file with {{ }} placeholders (Go's html/template).
Viewmodel
The typed Go struct a template renders; the template's root dot is a viewmodel. Viewmodels live in html/viewmodels/ and are generated by alfred.
Head
The part of every viewmodel describing the <head>: title, description, canonical URL, robots directive, scripts, preloads, JSON-LD.
Catalog
The boot-time context (brand, origin URL, site, customer data, service) used to build the page entries served at runtime.
PageEntry
One resolved page: its route, template name, kind, and typed viewmodel data. The list of these is what the Renderer serves.
HTMLPageKind
A page's visibility class: Public, Private, Admin, or the invalid Unclassified. Drives the robots tag and which middleware wraps the route.
The compiled blob
In prod, every page is pre-rendered and pre-compressed offline into one big []byte (compiled_html_gen.go). At runtime the prod handler just writes slices of it — no templates execute.
Middleware
A function wrapping a handler to run auth before it. Private and Admin pages are wrapped with auth middleware; the template never enforces auth, the middleware does.

Core concepts

Three modes, one catalog
The Renderer's mode is implied by which fields are set: CacheControl + a present blob → prod (ProdHandler); CacheControl but no blob → fail-closed 500 on every page (prod must never silently degrade to dev); TemplateDir → dev (devHandler); zero value → a 404 stub.
Where the data comes from
alfred generates the viewmodel structs and a Build() that fills them with the brand's data. The catalog functions here pair each page with its data and derive its <head>. In prod, cmd/compile renders all of it to a byte blob; in dev the same viewmodels render live.
The catalog filters are a security boundary
WebsiteCatalog serves public pages plus the shared 404/offline. WebappCatalog drops any Admin-kind entry so an admin page never appears on the client app. AdminCatalog keeps admin Private pages (profile, change-password) but drops webapp-only Private pages and shopping routes. A bug here is a visibility/privilege bug.
The <head> derivation is SEO + security
robotsFromKind returns index,follow only for Public; every other kind is noindex,nofollow,noarchive (fail-closed). setPageHead clears the canonical and forces noindex for shared fallback pages, sets canonical = origin+path otherwise, and is a clean no-op on malformed input.
Prod is bytes, dev is rendering
In prod there are no templates: ProdHandler does a jump-table metadata lookup, negotiates the smallest encoding the client accepts, honors ETag/304, validates blob slice bounds (corrupt metadata must not panic), and shovels a blob slice with zero allocations. In dev, devHandler clones the viewmodel per request, re-parses templates, and executes into a pooled buffer.
The dev 500 page is a debugger
When a template fails in dev, error500.go parses the Go template error to show the source file/line/column and failing field, prints surrounding source, dumps the request and viewmodel with secrets (password/token) scrubbed, and styles it with the brand. This richness is dev-only; prod can't fail this way and returns a plain no-store 500.

Methods

Renderer
The thing registered into the router. Its mode (blob/dev/stub) is implied by which fields are populated; it also reports its mode and page count to the ceremony diagnostics system.
WebsiteCatalog / WebappCatalog / AdminCatalog
Build the []PageEntry for each service from the generated catalog, applying the per-service visibility filters and appending the shared 404/offline pages.
Page[T].Entry
Resolves one page into a PageEntry with compile-time-checked viewmodel type, a cloned (never-shared) data value, a site-aware template name, and a derived <head>.
ProdHandler / PageRouteCatalog
ProdHandler serves the zero-alloc blob path. PageRouteCatalog is the validated site|route→PageDef lookup used by dev rendering (its backing map is private so callers cannot bypass validation).

Serving one request

The Renderer picks a mode once at registration; here is what each request then does. Same routes, same pages, same viewmodels across modes — only the byte source differs.

  1. Route + middleware The router matches the path; Private and Admin pages are wrapped with their auth middleware before the handler runs.
  2. Mode dispatch The request reaches the ProdHandler (blob), the devHandler (templates), a fail-closed 500 handler (prod with no blob), or the 404 stub.
  3. Prod path Select the site, look up page metadata by (service, site, path), negotiate the smallest acceptable encoding, set validators, answer 304 if If-None-Match matches, otherwise write the precompiled blob slice — zero allocations.
  4. Dev path Select the site, look up the PageDef, reload the dist asset manifest, clone the viewmodel into an independent head, re-parse the template tree from disk, and execute into a pooled buffer.
  5. Dev error path A parse or execution error renders the diagnostic 500 page instead of a blank error.

PageKind — page visibility

A typed uint8 that classifies a page for middleware and SEO. The zero value is deliberately invalid so an unclassified page never defaults to public.

Unclassified
0 — the invalid zero value
Public
indexable, no auth
Private
noindex, auth enforced by middleware
Admin
noindex, auth + admin RBAC enforced by middleware
Valid / IsValid
rejects Unclassified and out-of-range
String
a stable label, or 'unclassified'
MarshalText / UnmarshalText
reject invalid kinds with ErrInvalidPageKind
MarshalJSON / UnmarshalJSON
the same contract over JSON

Fail closed everywhere

No blob, no silent downgrade
If CacheControl is set (prod/stage) but the compiled blob is missing, every page returns 500 rather than quietly falling back to dev rendering.
Real 404s, no-store
A missing route writes a real 404 body and pins no-store, because 404s are otherwise heuristically cacheable.
Corrupt blob never panics
The prod handler validates blob slice bounds before reading; out-of-range metadata becomes a 500, not a crash. An encoding the client cannot read becomes 406, and a variant with no bytes at all becomes 500.
Non-public is non-indexable
Only Public pages get index,follow; everything else — including any unknown kind — is noindex.

System fit

  • Each host binary (website/webapp/admin) builds a Renderer with its []PageEntry, auth middleware, and site selector, then registers it via the switchboard router.
  • Page data originates in alfred-generated viewmodels; the brand's values come from customer.json via CustomerData.
  • cmd/compile produces compiled_html_gen.go (the blob) for prod; dev reads templates from disk.
  • HTMLPageKind drives both the robots tag and which middleware wraps each route.

Tests

prodhandler_test.go / _adversarial / _cache_hostile
The zero-alloc prod path: encoding negotiation, 304 conditionals, 404/406/500 fail-closed behavior, corrupt-blob bounds, and cache headers on every branch.
catalog_test.go / drift_test.go
The catalog filters (webapp drops admin; admin keeps admin-private) and that routes/templates do not drift from the generated catalog.
seo_parse_hostile_test.go
robotsFromKind exhausted over all 256 kinds (only Public is indexable), the setPageHead canonical/robots matrix, malformed-input no-ops, and the template-parse exclusion set.
devhandler / devrender / dev_hotreload
Live rendering, per-request clone isolation, and hot reload.
error500_test.go
The diagnostic page: template-error parsing, secret scrubbing, and source context.

compass

compass

The boot-time configuration loader. It reads secrets and settings from env/*.env files plus business identity from customer.json, validates all of it, and hands back one immutable Config. If anything required is missing or malformed, it errors and the program refuses to start.

One rule shapes everything: only main.go imports compass; every other package receives its configuration through its constructor's Config. Nothing reaches up to read its own env vars or files. So there is exactly one place that loads config, one place to validate it, and one place a misconfiguration can stop the boot. Fail-fast: main.go calls Load and log.Fatals on any error, so a booted process always has a fully valid config.

Vocabulary

Env
dev, stage, or prod (core.Env). Decides which .env file is read and whether cookies are Secure.
Mode / Service
which of the four services is booting: website, webapp, admin, api (core.Service). Decides which config is required — crypto keys are mandatory everywhere except website.
Env file
a key=value text file under env/. The base is env/<env>.env; an optional per-service overlay is env/<service>/<env>.env.
Overlay
the per-service file whose keys win over the base. Today it only carries PORT, so no secret is duplicated across the four services.
customer.json
business identity (name, domain, logos, hours, contacts, socials, site bindings). Marketing/identity data, not secrets.
Config
the single immutable struct compass produces — everything the app needs to run, threaded into every constructor.
CheckResult
one validation finding {Key, Message, OK, Skipped}. Validation returns a slice, one per invariant, passes included.
Site binding
a {Host, Binding} row mapping an incoming hostname to a service+site token, so one binary can serve several sites.

Core concepts

Only main.go imports compass
Config flows one way: compass loads everything once and the typed Config is threaded down into every constructor. No package below cmd/ reads env vars or files itself. One loader, one validator, one failure point.
Layered env files
Load reads env/<env>.env (base: secrets, DB, providers) then merges env/<service>/<env>.env (overlay: PORT). A missing overlay is fine — the typed port contract still fills PORT. A malformed overlay is fatal.
Deploy-identity guard
APP_NAME is the single project identity. GCP_PROJECT_ID, when set, must equal it or the load fails — this stops a build stamped for one distro from shipping to another distro's GCP project.
Prod credential isolation
IsolationCheck proves prod DB credentials differ from dev/stage (paths are filepath.Cleaned first so ./creds.json and creds.json compare equal), and that APP_NAME matches across all env files. It catches 'prod is accidentally pointed at the dev database.'
customer.json is graceful then strict
A missing file yields a zero CustomerConfig (fine for dev). A present file is decoded with DisallowUnknownFields and trailing-content rejection, then fully validated — a typo'd or doubled document fails loudly.

Methods

Load / LoadFile
Load is the normal path (base + overlay). LoadFile reads a single file with no overlay — used by cmd/lfw at build/deploy time to read base-file identity.
Config.Checks / Config.Validate
Checks() returns the full diagnostic table (one CheckResult per invariant, passes included) for the dashboard; Validate() is the boot gate that errors if any check fails.
IsolationCheck
Reads dev/stage/prod env files and proves prod DB credentials differ from the others and APP_NAME is consistent across them.
LoadCustomer / CustomerConfig.Validate
Loads customer.json (missing → zero config) and validates identity, logos, contacts/locations (≤1 primary), socials, hours, and site/tenant bindings.
CanonicalOriginFor / BrowserAPIOrigin
Build the canonical and browser-API origins used for canonical tags, redirects, and CORS, from the customer domain (with dev/stage special-casing).

The load pipeline

main.go calls Load(env, mode); here is what happens before it gets a Config.

  1. Read base + overlay Parse env/<env>.env, then merge env/<service>/<env>.env over it (overlay keys win; missing overlay is fine).
  2. Require keys APP_NAME is required everywhere; the six crypto keys are required in webapp/admin/api and optional in website. Missing required keys are collected and reported together.
  3. Guard identity & port syncProjectIdentity checks APP_NAME == GCP_PROJECT_ID; resolvePort applies platform > overlay-must-match-contract > contract.
  4. Apply externals & defaults Payment, comms, and asset fields are read; Firestore DB id and customer path get defaults.
  5. Validate Config.Validate() runs every check (crypto format/length, port range, pay-split coherence, webhook tolerance, external-secret bounds) and joins all failures into one error.
  6. Load customer LoadCustomer reads and validates customer.json, becoming the source of truth for brand identity and site bindings.

Day — day of the week

A typed uint8 for opening-hours parsing; the zero value is DayInvalid so an uninitialized day is unmistakable.

DayInvalid
0 — the invalid zero value
DaySunday … DaySaturday
the seven real days
ParseDay
maps any English spelling (monday/mon/mo, tues/thur…) to a Day; unknown → DayInvalid
Valid / IsValid
rejects DayInvalid and out-of-range
String / Abbrev / Display
canonical lowercase, two-letter (Mo), and upper three-letter (MON) forms
MarshalJSON / UnmarshalJSON
round-trip through the string form; reject unknown spellings

Fail-fast, fail-closed

Missing required config stops boot
Missing required keys are collected and returned as one error; main.go log.Fatals. The library never log.Fatals itself.
Malformed env is rejected
A line with no '=', an empty key, or a duplicate key is an error — silent last-wins would hide a copy-paste mistake in a secrets file.
Half-configured features fail closed
Pay-split must have percent > 0, minimum > 0, and a platform account when enabled; an out-of-range port or webhook tolerance fails.
Identity and isolation are guarded
APP_NAME must equal GCP_PROJECT_ID; prod DB credentials must differ from dev/stage.

System fit

  • compass is the first package called in every cmd/X/main.go; the returned Config is threaded through every constructor.
  • Nothing below the cmd layer reads env vars or files itself.
  • The CustomerConfig becomes the source of truth for viewmodels.CustomerData (brand name, logos, domain) and site-embedding policy.
  • IsolationCheck results and the Descriptor are surfaced in the ceremony diagnostics system.

Tests

compass_test.go
Load/LoadFile happy paths, port override, website mode skipping crypto, and canonical-origin construction.
validate_test.go / external_secrets_test.go
The crypto/port/pay-split/webhook checks and external-secret length/whitespace bounds.
customer_test.go
CustomerConfig and nested validations, the Day enum, and schedule normalization.
max_hostile_test.go
The previously-untested pure functions: the deploy-identity guard, the prod-isolation comparison normalizer, env-value de-quoting, day-spec and time normalization, base64 checking, and the at-most-one-primary contract.
isolation_test.go / port_test.go
Prod-vs-dev/stage credential isolation, cross-env APP_NAME consistency, and the port-precedence contract.

ceremony

ceremony

The boot compiler. At startup it asks every wired component to describe itself, assembles those descriptions into one Manifest, validates that the wiring is correct, and aborts the boot if it isn't — and renders the boot dashboard along the way.

main.go is the composition root that wires every piece together. Rather than make it understand the whole picture, every component self-describes via one interface, Wirable. ceremony collects those self-descriptions into a Manifest — the single source of truth for the dashboard, the markdown dump, the thread trace, and the Cloud Logging record. Critically, the manifest is validated: a wiring violation stops the boot before the app accepts traffic. The package knows nothing about business logic; it only sees Wirable metadata.

Vocabulary

Wirable
the interface every component implements to describe itself: name, description, callees, live details, debt flags, and lifecycle state.
Component
one node in the manifest — a Wirable's description plus computed fields (who calls it, what is blocking it).
Manifest
the assembled picture of the whole app: every component, the dependency graph, the routes, and integrity findings. Built by Derive, never hand-written.
Derive
the function that walks all wired components and produces the Manifest — the 'compile' step.
Validate
the boot gate: it inspects the manifest for violations and returns an error (which aborts boot) or nil.
State
a component's lifecycle (core.State): NotConfigured, Stubbed, Active, Failed, Blocked. Failed and Blocked abort boot.
Dependency graph
who calls whom. A component declares its callees (downstream); Derive inverts that to compute callers.
Orphan / Duplicate / Empty
a declared-but-never-wired callee; two components under one name; a component with an empty name. All are wiring bugs.
Singleton / category
Server, Store, Auth are singleton slots; everything else lives in category slices (Middleware, Registrars, Workers, Externals, Infra, Telemetry, Framework, EnvHealth).
Compile report / blob
the precompiled HTML/asset bundle plus a report; ceremony checks the blob's BLAKE3 hash and catalog parity at boot.

Core concepts

Components describe themselves
Every component implements Wirable (name, desc, callees, details, debt flags, state) — all compiler-enforced, so you can't add a component without describing it. ceremony aggregates these, it doesn't reach into business logic.
Derive is deterministic
Derive builds the graph, inverts edges to compute callers, derives BlockedBy from callee states, finds orphans/empties/duplicates, and sorts every slice by name — so the manifest, dashboard, and dumps are identical across runs and machines.
Validate is the gate
Empties, duplicates, orphans, an invalid command surface, unclassified API gates or page kinds, incomplete dispatches, compile/blob mismatches, and any Failed/Blocked component all abort boot. A booted process is therefore always correctly wired.
Foreign-blob contamination is caught
HTML parity compares the page catalog against the compiled report: a catalog page missing from the blob, OR a compiled template not in the catalog (another service's pages leaking in), is a violation. The blob's BLAKE3 hash must also match its report (fail-closed).
Every slot and every category participates
A failed or blocked component aborts boot from any slot — the three singletons and all eight category slices, including EnvHealth. empty() counts every violation category, so a new category can't be silently forgotten and let boot proceed broken.

Methods

Derive(opts)
Applies the With* options (WithStore, WithMiddleware, WithPages, WithMode, WithCompileReport, …) and produces the immutable Manifest.
Manifest.Validate
The boot gate. Collects every violation category and returns a *ValidationError (or nil). main.go turns a non-nil result into a fatal.
RunCeremony / ShowStatic
Drive the boot display: live dashboard in a TTY, static render otherwise, and a PII-redacted ceremony_boot slog record in prod.
DumpMarkdown / DumpThread
Write the manifest as a readable markdown artifact and a continuity trace; both propagate write errors.
RegisterErrorHint / ErrorHelp / FreezeRegistry
Register remediation steps for known failures; after FreezeRegistry lookups are lock-free and further writes error rather than mutate the live registry.

The boot lifecycle

Same manifest, four consumers — but Validate is the one that can stop the boot.

  1. Wire main.go wires every piece; each is a Wirable.
  2. Derive ceremony.Derive builds the Manifest: graph, callers, orphans, blocked-by, empties, duplicates — all sorted and deterministic.
  3. Render RunCeremony renders the boot dashboard (live TUI, static, or a structured prod JSON record), with panic recovery.
  4. Validate manifest.Validate() is the gate: any violation returns an error and boot aborts.
  5. Dump appboot persists the markdown artifact (component tables, compile reports, routes, graph, timeline).

core.State — component lifecycle

A component's self-declared state; Failed and Blocked are the boot-aborting ones.

NotConfigured / Stubbed
not wired, or a placeholder — does not abort boot
Active
wired and healthy
Failed / Blocked
broken or blocked by a dependency — aborts boot
Valid / String
range-checked classification and a stable label

PageRouteKind — page visibility (ceremony's copy)

Classifies a page route without importing service packages; the zero value is Unclassified and is rejected so a page can never sit with no middleware pipeline.

PageRouteUnclassified
0 — invalid; a wiring violation
PageRoutePublic / Private / Admin
the three real visibility classes
Valid / IsValid / String
classification and label
MarshalText / UnmarshalText / MarshalJSON / UnmarshalJSON
round-trip; reject unknown kinds

What aborts boot

Structural wiring bugs
empty component names, duplicate names, and orphaned (declared but unwired) dependencies.
Unclassified surfaces
an invalid command surface (Mode must name a real /cmd binary), an unclassified API route gate, or an unclassified page route kind.
Incomplete or contaminated proof
a partial host->service/site dispatch, a catalog/compiled-blob parity mismatch, or a blob whose BLAKE3 hash does not match its report.
Unhealthy components
any component in Failed or Blocked state, in any slot — singletons and every category slice, including EnvHealth.

System fit

  • ceremony sits between main.go and every runtime component.
  • Each cmd/X/main.go calls Derive with all wired pieces as options, then RunCeremony (display), then Validate (the gate), then the dump writer.
  • The package imports no business logic — it only sees Wirable metadata.
  • A wiring violation here aborts the boot before the app accepts traffic.

Tests

manifest_test.go
Derive wiring and the per-category Validate cases (empties, duplicates, orphans, API gates, page kinds, dispatches, blob hash, compile parity, failed components) plus determinism.
validate_hostile_test.go
The boot-gate invariants at the limit: a failed/blocked component in every one of the 11 slots aborts boot; healthy states never do; empty() counts every violation category; HTML parity catches missing and foreign-blob extra templates; each collector is exhausted; blob-hash verification is fail-closed.
boot_test.go
Dashboard extraction, ordered component names, panic recovery and drain-without-leak, state mappings, and the prod JSON record (with PII redaction).
dump_test.go / thread_test.go / errors_test.go
The markdown/thread dumps, and the error-hint registry lifecycle.
integration_test.go
End-to-end: Derive a full manifest, extract the dashboard config, and verify structure and rendering.

api

api

The transport contract for the JSON API: every response is a typed Success or Failure envelope, every internal error maps to a client-safe body that never leaks internals, and every endpoint is a typed Route in a generated catalog. It knows HTTP but depends on no service or infra package.

Three contracts live here. The envelope: every response is Success[T] (data) or Failure[T] (error), with a uniform wire shape, so a response can never be half-success/half-failure. Safe error mapping: MapError turns any internal error into a sanitized ErrorBody; the raw cause is kept for logs but never sent to the client. The route catalog: endpoints are declared as intents in intents.go and alfred generates the Route values plus the request/response structs, so handler wiring, the mux, and the boot proof all reference one source of truth.

Vocabulary

Body
the contract every response payload satisfies (Primitive core.ValidatedJSONMarshaler). It forces handlers to return a validated struct with an explicit JSON projection — no loose ints, strings, maps, or reflective defaults ride out as data.
Success[T] / Failure[T]
the two typed response branches; the Go side makes you pick one.
Envelope
the uniform wire JSON: data, error, request_id.
APIRequestID
Kernel's typed request identifier copied into every envelope for correlation.
Code
a typed machine-readable error classification. The zero value CodeUnknown is invalid by design.
ErrorBody
the client-visible error payload: message, optional tip, code, and an unexported HTTP status.
Fault
an internal error pairing the raw cause (for logs) with a client-safe ErrorBody (for the wire).
MapError
converts a domain error into a Fault, mapping known sentinels and overlaying explicitly-safe messages.
Route / Intent
a Route is one endpoint (method, path, gate, budget, models); an APIRouteIntent is the authored declaration alfred generates the Route from.
Versioned
prefixes a path into the /v1 API namespace, idempotently.

Core concepts

The envelope is a forced choice
OK builds Success[T] (data + null error); Fail builds Failure[T] (error + null data). request_id is always present. WriteTo validates before writing — an invalid body or empty request id returns an error and writes nothing, so a malformed response never reaches a client.
MapError never leaks the cause
err.Error() is never sent to the client. MapError maps known sentinels to canned safe bodies, optionally overlays a domain ClientMessage/Tip, and falls back to a safe internal body if anything is malformed. The raw cause stays on Fault for logs only — so a DSN, token, or PII in an error can't reach the wire.
Routes are declared, then generated
You author intents in intents.go; alfred generates routes_gen.go (the Route vars + Catalog), api/requests (request structs + path/query binders), and api/viewmodels (response structs). One generated Route is the single source of truth for the handler, the mux, and the ceremony route proof.
The generated binders are a trust boundary
Each request's generated FromHTTPRequest pulls {id} off the URL and parses it into a typed ULID, rejecting malformed or missing values with ErrInvalidInput and never half-populating the request.

Methods

OK / Fail
Construct the Success[T] / Failure[T] response branches from a request id and a body.
MapError
The error-to-Fault mapper; the security boundary that keeps raw internals off the wire.
RequestID
Extracts the request id from Server-Trace-Id then X-Request-Id, with a missing-marker fallback; never returns empty.
Versioned / Catalog
Versioned normalizes a path into /v1; Catalog() (generated) lists every Route for the mux and the boot proof.
Monitor
Probes dependency health and gates readiness behind /health, /live, /ready, managing its recovery loop without leaking goroutines.

The response model

How a handler's result becomes a safe wire response.

  1. Bind The transport wrapper binds the body and runs the request's FromHTTPRequest (path/query) and Validate.
  2. Handle The handler returns either a typed Body (success) or an error.
  3. Map errors An error goes through MapError → a Fault with a client-safe ErrorBody; the raw cause is retained only for logs.
  4. Choose a branch OK(id, body) for success or Fail(id, fault) for failure — request_id is stamped on both.
  5. Validate then write WriteTo validates the chosen envelope and refuses to emit anything malformed; otherwise it streams the uniform JSON.

Code — error classification

A typed uint8 carried in every error body. The zero value is invalid so a forgotten Code is caught rather than defaulting to a real status.

CodeUnknown
0 — invalid; a forgotten code surfaces here
CodeNotFound / InvalidInput / Conflict / Unauthorized / Forbidden / PayloadTooLarge / ServiceUnavailable / Internal
the real client-facing classifications
Valid / IsValid / String
classification and a stable label
MarshalJSON / UnmarshalJSON
round-trip; reject unknown codes (fail closed)

How errors reach the client

Sanitized, never raw
Only the canned safe message (or an explicitly-safe domain ClientMessage) reaches the wire; err.Error() never does.
Fail closed on unknown
An unmapped error becomes CodeInternal / 500 / the generic internal message — no detail, no leak.
Refuse malformed responses
WriteTo validates first; an invalid body or empty request id is refused and nothing is written.
Status comes from the body
HTTPStatus derives from the mapped ErrorBody; an uninitialised body defaults to 500, not a misleading 404.

System fit

  • api sits in the transport layer; transport, userapi, adminapi, auth/starter, payapi and every package that exposes HTTP routes import it.
  • It depends on no service or infra package — only HTTP and the typed contracts.
  • The generated route catalog drives both the switchboard mux and the ceremony route proof.
  • api registers itself through ceremony like every component: its Descriptor is a Wirable wired into the boot manifest, which validates the wiring and aborts boot on any violation.

Tests

api_test.go
The MapError matrix (sentinels, wrap depths, joins, mixed chains, fallback, ClientMessage/Tip overlays), the Code enum, RequestID precedence, Versioned, and Fault unwrap behavior.
wire_hostile_test.go
MapError never leaks the cause to the wire (planted secrets never appear in the serialized envelope, yet remain recoverable for logs), and the envelope shape contract (request_id always present; success=data+null error; failure=error+null data; WriteTo refuses invalid bodies and empty ids).
api/requests/binder_hostile_test.go
The alfred-generated FromHTTPRequest path-ULID binders reject malformed/missing/oversized {id} with ErrInvalidInput and never half-populate the request.
api/viewmodels + api/requests boundary tests
Viewmodel Validate (role/status enum exhaustion, field precedence, omitempty wire) and request Validate boundary buckets plus forged-body rejection.
monitor_test.go / intents_ssot_test.go
Readiness gating and recovery-loop lifecycle; and that authored intents stay the single source of truth for the generated catalog.

bridge

bridge

The package that talks to external services — Stripe and PayPal (payments), Twilio (SMS), Plunk (email) — through bounded HTTP clients, and verifies the webhooks those providers send back. A flaky or hostile third party can never hang the app or forge an event past this layer.

Calling someone else's server is the riskiest thing the app does, so every outbound call is bounded — a hard timeout, capped retries with backoff, and a maximum response size — and gated by a circuit breaker so a provider outage degrades gracefully. Every inbound webhook is verified: a payment event is trusted only if it carries a valid provider signature. Outbound is one shared Client wrapped by per-provider Senders; inbound is webhook verification (Stripe HMAC, PayPal verify-endpoint), which is the package's security boundary.

Vocabulary

Client
the shared bounded HTTP client: applies the timeout, retries transient failures with backoff, caps the response body, and injects a trace header. Client.Do makes the call.
Config
a Client's settings (BaseURL, Timeout, MaxRetries, BaseBackoff/MaxBackoff). Validate rejects a non-positive timeout, negative retries, a bad base URL, or inverted backoff bounds.
Sender
a per-provider wrapper (StripeSender, PayPalSender, TwilioSender, PlunkSender) adding that provider's auth, payload encoding, and response parsing on top of Client.
Breaker
a circuit breaker: after repeated failures it opens and fast-fails instead of hammering a dead provider; ErrBreakerOpen surfaces as a retryable 'service unavailable'.
Webhook
an HTTP callback a provider sends to us (e.g. 'this checkout was paid'). It must be authenticated before it is trusted.
Signature verification
proving a webhook really came from the provider: Stripe signs an HMAC over body + timestamp; PayPal is verified by calling its verify-signature endpoint.
Externals
a ceremony reporter (not an HTTP client) listing which third-party integrations have credentials configured.

Core concepts

Bounded outbound calls
Client.Do enforces the timeout and context cancellation, retries transient failures up to MaxRetries with jittered exponential backoff, replays the request body safely on each attempt, and caps the response to BridgeMaxResponseBytes so a broken or malicious provider can't exhaust memory.
Providers stay behind neutral types
Senders translate to and from provider JSON but expose only framework-neutral types (pay.*, relay.*). The Stripe SDK is confined to a single function; no provider SDK leaks beyond bridge.
Webhooks are verified before they are trusted
Stripe: VerifyStripeWebhook checks the HMAC over the raw payload within a positive Tolerance window (which also defeats replay), then parses a neutral event. PayPal: VerifyWebhook calls PayPal's verify endpoint, guarded by the cert-URL trust check and the status classifier.
The cert-URL trust check
isPayPalCertURL accepts only HTTPS URLs anchored at a known PayPal host. The check is a prefix match against constants that END IN '/' — that trailing slash is the defense against the look-alike attack (https://api.paypal.com.evil.com/...), which must be rejected.
Transient vs terminal classification
verifyHTTPStatus maps the verify response: 2xx accepted; 5xx/408/429 transient (ErrBridgeVerificationUnavailable, retryable, signature never judged); other 4xx terminal (ErrBridgeInvalidSignature, a forged/bad request — don't retry a forgery); 1xx/3xx unavailable. A provider blip must not become a permanent payment failure, and a forgery must not be retried forever.

Methods

NewStripe / NewPayPal / NewTwilio / NewPlunk
Construct the per-provider senders at boot, each validating its own config.
CreateCheckout (Stripe/PayPal)
Create a checkout session and return a provider-neutral pay.CheckoutResult.
TwilioSender.Send / PlunkSender.Send
Send an SMS / email for a neutral relay input.
VerifyStripeWebhook
The only function that imports the Stripe SDK; verifies the signature within tolerance and returns a neutral WebhookEvent.
PayPalSender.VerifyWebhook
Calls PayPal's verify endpoint behind the cert-URL trust check and the status classifier.

An outbound call and an inbound webhook

The two directions bridge mediates.

  1. Outbound: a Sender builds the request Adds provider auth and encodes the neutral input (pay.Request, relay.*) into provider JSON.
  2. Outbound: Client.Do bounds it Timeout, retries with backoff, body cap, trace header, breaker gating — then the Sender parses the response back into a neutral result or a typed error.
  3. Inbound: authenticate the webhook Stripe verifies the HMAC within tolerance; PayPal validates the headers and cert URL, then calls the verify endpoint.
  4. Inbound: classify the result Verified → a neutral WebhookEvent the caller can fulfill; transient failure → retryable unavailable; bad signature → terminal invalid — never fulfilled.

VerificationStatus — PayPal verify result

Only Success clears verification; the zero value is invalid.

VerificationStatusUnknown
0 — invalid
Success / Failure
the provider verdict; only Success is trusted
Valid / IsValid / String
classification and label
MarshalJSON / UnmarshalJSON
round-trip; reject unknown values

EventType — Stripe webhook event

Unhandled types map to Unknown so the handler can still ack with 200; only known types carry structured Data.

EventTypeUnknown
0 — unhandled; acknowledged but not actionable
CheckoutCompleted / CheckoutAsyncPaymentSucceeded
the handled checkout events
Valid / IsValid / String
classification and label
MarshalJSON / UnmarshalJSON
round-trip; reject unknown values

Failure classification

Transient is retryable
5xx, 408, 429, network errors, and an open breaker map to ErrBridgeVerificationUnavailable / ErrBreakerOpen — the caller may retry.
Terminal is final
A bad/forged request (other 4xx) or a non-Success verification status maps to ErrBridgeInvalidSignature — never retried, never fulfilled.
Malformed headers fail closed
Missing transmission headers or an untrusted CertURL fail with ErrBridgeMalformedHeaders before any verification call.
Responses are bounded
Every provider response is read through a capped LimitReader so a hostile body can't exhaust memory.

System fit

  • bridge is in the Infra layer; it is called by service-layer registrars and pay.Service.
  • It imports core (sentinels, header constants), pay (checkout types), and the breaker; no transport or display package imports bridge.
  • No provider SDK leaks beyond bridge — payments, SMS, and email all flow through neutral types.
  • bridge registers through ceremony like every component: Externals is a Wirable/SubItemer reporting configured integrations, so a misconfigured external surfaces at boot, not at the first live call.

Tests

client_test.go / client_inject_test.go
The bounded client: timeout, retry/backoff, body cap, context cancellation, trace injection.
stripe/paypal/twilio/plunk _test.go
Each sender's request building, response parsing, error mapping, and SMS cost accounting.
stripe_webhook_test.go / webhook_test.go
Stripe signature verification (valid/invalid/expired-replay/multi-candidate/malformed) and the PayPal verify flow (headers, body replay across retries, 401 refresh, transient vs terminal status).
webhook_security_hostile_test.go
The two pure payment-security functions exhaustively: verifyHTTPStatus classifies every status into accepted / transient / terminal (mutually exclusive), and isPayPalCertURL accepts only PayPal-anchored HTTPS URLs while rejecting look-alike/embedded/wrong-scheme/empty hosts.
validation_hostile_test.go / boundary_buckets_test.go / externals_test.go
Config and input validation boundaries, the enums, and the Externals ceremony reporter.

alert

alert

Defines the vocabulary for operational alerts — the severity levels, the typed payload, and the callback a component uses to say 'something operationally notable happened' — and nothing else. It is pure: no I/O, no provider SDKs. The siren telemetry component does the actual sending.

Many components need to raise an alert (a breaker tripped, a webhook failed, disk is filling). If each imported the alert sender, the whole codebase would couple to a notification SDK. Instead alert owns only the language — Severity, Payload, Notifier, SignalFunc — importing just core and ceremony. A component depends on the tiny SignalFunc callback, not the full sender; siren implements Notifier at the edge. The dependency arrow points into this small pure package, and the heavy sending machinery stays out.

Vocabulary

Severity
how urgent an alert is: Info, Warning, Critical (plus the invalid zero value Unknown). A typed uint8 enum, ascending by urgency.
Payload
the data carried by one alert: a Severity, a Message, and optional metadata (TraceID, UserULID, Tip).
Notifier
the port that actually dispatches an alert (Notify(ctx, Payload) error). Implemented by siren.
SignalFunc
a minimal callback func(ctx, Payload) a component accepts instead of the full Notifier. No error return.
FromNotifier
adapts a Notifier into a SignalFunc, swallowing the Notify error so alert delivery can't fail the caller's primary operation.

Core concepts

Pure vocabulary, sending at the edge
alert defines the language and contracts only; it imports no I/O. siren (telemetry) implements Notifier. So almost anything can depend on alert without dragging a notification SDK along.
Zero severity is invalid on purpose
SeverityUnknown is the zero value, so a forgotten field fails validation rather than silently routing as the lowest severity — a critical alert can never be quietly demoted to info.
Alert delivery never fails the caller
SignalFunc has no error return and FromNotifier swallows the Notify error. Alerting is a side channel; if it fails, the primary operation (serving the request, finishing the job) must still succeed. The sender logs its own failures.
The wire boundary fails closed
Marshalling an invalid severity errors (never a silent empty); unmarshalling rejects unknown labels with ErrAlertInvalidSeverity and non-string JSON with a decode-format error — never panicking and never clobbering an already-set value.

Methods

FromNotifier
Wraps a Notifier into the minimal SignalFunc dependency, swallowing the Notify error (it does not recover a panic — a Notifier is expected not to panic).
Payload.Validate
Requires a valid Severity and a non-empty Message; the rest is optional metadata.
Severity Marshal/Unmarshal
Text and JSON round-trips that reject invalid severities and non-string JSON, fail-closed and without mutation.

Raising one alert

How a component signals an operator without coupling to the sender.

  1. Depend on SignalFunc A component accepts a SignalFunc callback as a dependency — the smallest possible surface.
  2. Build a Payload Set the Severity and Message (plus optional TraceID/UserULID/Tip); Validate enforces a real severity and non-empty message.
  3. Fire and forget Call the SignalFunc. FromNotifier forwards to the real Notifier and swallows any error so the caller's primary work is unaffected.
  4. Send at the edge siren (the Notifier implementation) performs the actual delivery and logs its own failures.

Severity — alert urgency

A typed uint8, ascending by urgency; the zero value is invalid so a forgotten field is caught.

SeverityUnknown
0 — invalid; a forgotten field surfaces here
Info / Warning / Critical
informational, degraded-but-functional, and immediate-attention
Valid / IsValid / String
classification and the lowercase label (or 'unknown')
MarshalText / UnmarshalText / MarshalJSON / UnmarshalJSON
round-trip; reject invalid severities and non-string JSON

Fail-closed vocabulary

Invalid severity is caught
A zero/out-of-range Severity fails Payload.Validate and fails marshalling with ErrAlertInvalidSeverity — never a silent demotion.
Empty message is rejected
An alert with no message text is operationally useless and fails Validate.
Decode is fail-closed and total
Unknown labels and non-string JSON are rejected without panic and without mutating the target.
Delivery errors are swallowed
FromNotifier drops the Notify error so the caller's primary operation never fails because an alert couldn't be sent.

System fit

  • alert sits low and pure; middleware and service components accept a SignalFunc and call it when something notable happens.
  • siren (telemetry) implements Notifier and is adapted via FromNotifier at composition time.
  • It imports only core and ceremony — no I/O — so it can be a dependency of almost anything.
  • alert registers through ceremony like every component: its Descriptor is a Wirable in the boot manifest (no callees — it's pure vocabulary).

Tests

alert_test.go
The full FromNotifier contract: dispatches the payload, propagates context/cancellation, swallows every error, race-free under concurrency, delivers multiple payloads, plus the documented panic behavior and a benchmark.
boundary_buckets_test.go
The Severity wire boundary: accepted text/JSON round-trips, rejection of case-folded/padded/control-char/unicode-lookalike/near-miss labels (no mutation), and the full uint8 outlier band.
severity_json_drift_hostile_test.go
UnmarshalJSON type drift: a non-string JSON token is the decode-format error (not ErrAlertInvalidSeverity), never panics, never clobbers an already-set value; plus full round-trip fidelity for every valid severity.
hostile_test.go
Iota-ascends-by-urgency, round-trip stability, Payload.Validate idempotency/purity/race-freedom across every severity×message, and adversarial UnmarshalText inputs that must not panic.
contracts_test.go
Every public type has a Validate, and the Notifier/SignalFunc contracts are compiler-owned.

appboot

appboot

The composition-root toolkit every cmd/X/main.go calls to turn a validated configuration into a running, ceremony-described service: load config and bind the port, derive the per-deployment policies (host resolver, browser origins, cookie scope, host→site dispatch), assemble the ceremony wiring, and start/stop the server cleanly.

Four binaries — website, webapp, admin, api — must agree on which hosts they admit, which API origin the browser calls, how auth cookies are scoped, and which host maps to which tenant site. If each main.go computed those by hand they would drift, and a drift in cookie scope or host dispatch is a security bug. appboot derives all of it deterministically from the single compass.Config, so every binary gets interoperable behavior by construction. main.go stays a thin wiring script; the derivation logic lives and is tested here.

Vocabulary

Config
the immutable configuration compass loaded and validated (compass.Config). appboot consumes it; it never reads env/files itself.
Service / mode
which binary is booting: website, webapp, admin, api (core.Service).
Resolver
the host gate — which incoming hostnames this deployment admits (customer domain, App Engine host, dev hosts).
Site dispatch / selection
mapping an admitted request host to a service+site (core.SiteSelection). An unknown host that looks like a tenant must miss, not fall through.
Cookie domain policy
how auth cookies are scoped: shared across the app/admin/api subdomains of one deployment, host-scoped for anything else.
Browser API origin
the API origin browser JavaScript should call; it must match the CSP connect-src the response advertises.
Ceremony Wirable
a self-describing component for the boot manifest; appboot builds several from runtime state.
Shutdown stack
an ordered teardown list so resources close in the reverse order they opened.

Core concepts

Derive everything from one Config, once
Every per-deployment policy (resolver, site dispatch, cookie scope, browser origin) is a pure function of compass.Config, so two binaries on the same deployment compute the same answers and can't drift out of sync.
Host dispatch is exact-match, fail-closed
SiteSelectorForService builds the host→selection table once at boot. Dispatch is exact-host mapping from config, not subdomain heuristics: configured hosts resolve to their declared site, and a host that looks like a tenant subdomain but isn't in the table misses rather than defaulting to a site.
Cross-service tenant isolation
siteFromExplicitBinding only serves a customer.json binding when its service equals the serving binary AND its site is an active non-main member; otherwise it folds to main. A binding meant for admin can never make website serve that tenant.
Cookies interoperate but don't over-scope
NewCookieDomainPolicy shares the auth cookie across the app/admin/api subdomains (same namespace) but keeps any unrecognized subdomain host-scoped, so a stray host can't receive a deployment-wide cookie.
Browser origin convergence
BrowserAPI returns the JS origin and the CSP connect-src from the same source call, so the origin the browser is told to use and the origin the CSP permits can never silently disagree.

Methods

LoadAndBind / CheckDevDeployGuard
The entry point: load+validate config and bind the port; and the guard that refuses dev config in prod.
SiteSelectorForService / SiteForHost / ConfiguredSitesForService
Build the host→site dispatcher and resolve a request host to its service-local site.
NewCookieDomainPolicy / AuthNamespace
Derive the shared, correctly-scoped auth cookie policy from the deployment domain.
BrowserAPI / ResolverForEnv / CustomerData / CustomerHealth
Project the Config into the browser API contract, the host resolver, and the customer viewmodel/health values.
StartServer / Primitive shutdown / Hostfacts memory limit
Run the server with typed bounded teardown and set GOMEMLIMIT from Primitive Hostfacts' effective workload ceiling.

The boot flow

What cmd/X/main.go does through appboot, in order.

  1. Guard CheckDevDeployGuard refuses a dev configuration in a deployed environment (fail-closed against shipping dev secrets/hosts).
  2. Load and bind LoadAndBind runs compass.Load and binds the listener, returning the Config, listener, and address.
  3. Derive policy From the Config: the host resolver, the per-request site selector, the cookie domain policy, the browser API contract, and the customer data/health projections.
  4. Assemble ceremony Build the telemetry/env-health/isolation/cache-mode Wirables, run ceremony.Derive → Validate (the boot gate), then WriteCeremonyDump.
  5. Serve and shut down StartServer runs the server (returning errors, not crashing); Primitive Shutdown tears resources down in reverse order and continues after failures.

Fail-closed wiring

Dev config can't deploy
CheckDevDeployGuard rejects a dev configuration in a deployed environment.
Unknown tenant hosts miss
A tenant-looking host not in the dispatch table fails closed (miss) rather than defaulting to a site.
Cross-service bindings fold to main
A binding whose service doesn't match the serving binary, or whose site isn't active, never serves that tenant.
Non-auth services have no cookie
NewCookieDomainPolicy rejects a service that doesn't carry an auth cookie.

System fit

  • appboot sits directly under cmd/X/main.go and above compass (config), ceremony (manifest), auth (cookie policy), and normalize (host resolver).
  • It turns a validated Config into a live, observable service and owns no business logic — only deterministic wiring.
  • Every component it assembles is registered through ceremony, which validates the wiring before traffic is accepted.
  • The customer.json it received via compass becomes the source of truth for CustomerData and site dispatch.

Tests

site_test.go / site_dispatch_hostile_test.go
The host dispatcher: the attack table, localhost-suffix fail-closed boundary, dev-browser-host routing, and alloc-free per-request selection.
site_binding_isolation_hostile_test.go
Cross-service tenant isolation (siteFromExplicitBinding folds a foreign-service or inactive-site binding to main), the parseSiteBinding parser, and normalizeSites (main-first, dedupe, empty→main).
cookie_domain_test.go
Cookies shared across the auth subdomains, the shared namespace, rejection of non-auth services, and the dev shared-parent case.
config_test.go / service_browser_origin_test.go
The browser API convergence contract, customer data/health projection, hex decode, and the resolver per env.
telemetry_test.go / envcheck_test.go / server_test.go
The ceremony Wirables (telemetry, isolation, cache-mode, env health) and StartServer error return + ordered shutdown.

ballast

ballast

Serves static assets — CSS, JS, fonts, images — under /dist/. In production it writes pre-rendered, pre-compressed bytes straight from one in-memory blob with zero allocations; in dev it serves from disk; unwired, it 404s. It is the asset cousin of html (which serves pages).

Like html, there is one description of the assets and three ways to serve them, chosen by which AssetServer fields are set: prod (blob), dev (disk), or stub (zero value). Two properties recur: fail-closed (prod with no valid blob must never silently fall back to disk) and the caching split (a content-hashed asset is cached for a year, but an error response must never be). The compiled blob holds every prod asset pre-compressed in several encodings with per-asset metadata; the prod handler picks the smallest encoding the client accepts and shovels a slice of the blob.

Vocabulary

AssetServer
the registrar; its mode is implied by its fields — CacheControl set → prod (blob), AssetDir set → dev (disk), neither → stub.
Compiled blob
every prod asset pre-rendered and pre-compressed into one []byte, with per-asset metadata (offsets, ETag, Content-Length, encoding variants). The prod handler writes slices of it.
Compiled asset state
the atomically-published snapshot pairing the blob bytes with its metadata; one snapshot serves a whole request so a hot-swap can't pair old offsets with new bytes.
Forge prefix
AppName + '_', the brand-scoped filename prefix for generated assets.
Service-scoped blob
the blob is keyed by service, so a binary only serves its own service's compiled assets.
Encoding variant
the same asset pre-compressed several ways (identity, gzip, br, zstd); the handler serves the smallest the client accepts.
Immutable caching
hashed asset filenames let the response say 'cache for a year'; only error responses opt out (no-store).

Core concepts

Three modes, fail-closed
Register picks prod (blob), dev (disk), or stub by the AssetServer fields. Production posture (CacheControl set) with no usable blob mounts a 404 'not built yet' handler — it never silently degrades to disk serving.
Routes publish only from a validated snapshot
Before mounting blob routes, Register takes the current compiled-asset snapshot and validates it; an invalid/incomplete snapshot fails closed to the stub handler rather than publishing broken exact-asset routes into the mux.
Zero-alloc prod hot path
ProdHandler.ServeHTTP takes one atomic snapshot, does a jump-table path lookup, negotiates the smallest accepted encoding via a precomputed pick table, validates blob slice bounds, and shovels a zero-copy slice. All response headers are indexed out of preloaded immutable lookup tables (Vary, Content-Encoding, service-worker cache, error cache) — nothing is fetched or copied per request.
The caching split (P0-5)
A served asset's filename is content-hashed, so success carries a ~1-year immutable Cache-Control. But a 404/406/500 is transient (bad path, no acceptable encoding, corrupt metadata) — every error exit happens before the asset cache/validator headers are set and uses no-store, so a shared cache can't pin a failure for a year.
Content-Type is a security boundary
contentTypeByExt derives the type from the final path segment only (a dot in a directory like /assets/x.css/evil does not make the file serve as text/css), and unknown extensions return nil rather than a guessed type.

Methods

AssetServer.Register
Picks the mode, validates the compiled snapshot, and mounts exact blob routes + a disk fallback (prod), the dev handler (disk), or a fail-closed 404 (stub / prod-without-blob).
ProdHandler.ServeHTTP
The zero-alloc blob hot path: snapshot, lookup, smallest-variant negotiation, bounds-check, indexed immutable headers, 304, zero-copy write.
newDevHandler
Serves from disk in dev, stripping .min so unminified source can be debugged, reloading each request.
contentTypeByExt
Resolves the Content-Type from the final-segment extension; unknown extensions return nil (no guessed type).

Serving one prod asset

The zero-allocation hot path, after the routes were published from a validated snapshot.

  1. Snapshot Take one atomic compiled-asset snapshot so metadata and blob bytes come from the same publish.
  2. Lookup Jump-table path → asset metadata; a miss is a no-store 404.
  3. Negotiate encoding Pick the provably smallest variant the client accepts from the precomputed pick table; none acceptable → 406, every variant empty → 500.
  4. Bounds-check Validate the blob slice offset/length before access so corrupt metadata can't panic the hot path.
  5. Headers then bytes Set validators + immutable cache (indexed from preloaded tables), honor If-None-Match → 304, then shovel a zero-copy blob slice.

Fail-closed serving

Prod without a valid blob 404s
Production posture never falls back to disk; a missing or invalid compiled snapshot mounts the stub handler.
Errors are never cached long
404/406/500 set no-store before any asset cache headers, so a shared cache can't pin a transient failure.
Corrupt metadata can't panic
The handler validates blob slice bounds before reading; an out-of-range offset/length becomes a 500, not a crash.
No guessed content types
An unknown or directory-embedded extension yields no Content-Type rather than a sniffable guess.

System fit

  • ballast sits in the transport layer alongside html; the host binary registers an AssetServer via the switchboard router under /dist/.
  • cmd/compile produces the compiled, pre-compressed asset blob for prod; dev reads from disk.
  • It depends on encoding (variant negotiation), core, and switchboard, and on no service/business package.
  • It registers through ceremony as a Wirable, so a prod-without-blob misconfiguration is visible at boot.

Tests

prodhandler_adversarial_test.go / prodhandler_cache_hostile_test.go
The zero-alloc blob path: per-asset Content-Type, encoding negotiation, smallest-variant choice, 304 conditionals, the P0-5 error-cache contract, and corrupt-blob bounds.
mime_lastext_hostile_test.go
The MIME resolver directly: lastExt directory-segment confusion (a directory dot never becomes the file's extension) plus edges, and contentTypeForExt length-bucket dispatch (known per bucket routes; every unsupported length and near-miss resolves to nil).
ballast_test.go / compiled_state_test.go
contentTypeByExt, the AssetServer mode/Register matrix, the fail-closed prod-without-blob path, and the atomic compiled-state snapshot/validation.
smallest_variant_hostile_test.go / sw_header_hostile_test.go / hostile_test.go
Variant selection, service-worker header handling, encoding enum exhaustion, and the register matrix across mount modes.
blob_alignment_ratchet_test.go / pick_table_ratchet_test.go / bench_test.go
Blob layout alignment, the precomputed variant pick table, and allocation benchmarks proving the hot path stays alloc-free.

auth

auth

The security boundary between anonymous and authenticated traffic: it mints and verifies signed session tokens, stores them across a small set of cookies, and provides the Gate middleware that turns a valid request into a known identity — with a valid-access path that does zero database calls. It does authentication (who are you?), not authorization (are you allowed? — the handler's job).

Four ideas explain almost everything. (1) Signed token: the server issues an HS256 JWT and verifies its signature instead of a DB lookup, so a valid request costs no DB call. (2) Split proof: an HMAC tag is cut in half — half in the JWT, half in an HttpOnly cookie — so stealing one piece can't forge a session. (3) Token families & refresh: short-lived access tokens are refreshed by a family-chained refresh token; replaying an old one forks the family and revokes it (theft detection). (4) Fail-closed: any ambiguity — bad signature, wrong algorithm, missing half, expiry, tenant mismatch — denies access.

Vocabulary

JWT / HS256
a signed JSON Web Token; HS256 is HMAC-SHA256 signing where the same server-side key signs and verifies. The verifier is pinned to HS256 to block algorithm-confusion attacks.
Split proof
an HMAC tag cut in two (SplitProof): Part A travels in the JWT, Part B in an HttpOnly cookie. Verification needs both halves; neither alone is sufficient.
The five cookies
all derived from CookieConfig.AppName: access (the JWT), refresh (family-chained), identity (UI hints), device (UA-bound proof), and server (the split-proof B half).
Authority
the token engine that issues and verifies JWTs and proofs; it is the ceremony.Wirable representing this package at boot.
Gate
the middleware that reads the cookies, verifies, and puts an AuthInfo into the request context. Comes in Optional and Required flavors.
AuthInfo
the verified identity carried through context: user id, tenant id, level/scope.
TenantGuard
middleware that blocks a token for one tenant from being used on another tenant's subdomain.
DomainPolicy
how the auth cookies are scoped across a deployment's subdomains.

Core concepts

Verify, don't look up
Authority verifies the HS256 signature with a pinned method list, checks expiry, and recomputes the split-proof HMAC from the JWT half plus the cookie half. It is a fact-check, not error-chain inference, so the valid access path makes zero DB calls.
Split proof defeats token theft
Because Part A (JWT) and Part B (HttpOnly cookie) must be recombined to verify, capturing one — a logged URL, an XSS-readable value — is not enough to forge a session.
Transparent, family-aware refresh
On an expired access token the Gate refreshes via singleflight (concurrent requests for one user refresh once). A replayed/old refresh token forks the family, which revokes it and emits an audit event.
Cross-tenant isolation is fail-closed
TenantGuard compares the token's tenant claim to the host's tenant (incl. a platform token with empty TID hitting a tenant subdomain, BUG-162); a mismatch is a 403 with a canonical JSON no-store body and the protected handler is never reached.
Privacy-bounded audit
Client IPs are never logged raw: IPPrefix masks IPv4 to /24 and IPv6 to /56, returns a sentinel for loopback/unparseable, and the strict decimal parseIPv4 won't let a malformed address masquerade as valid.

Methods

New / Authority
Build the token engine; issue and verify HS256 JWTs with split proofs.
NewGate / Optional / Required
Build the gate; Optional degrades to anonymous, Required (and RequiredRedirect/RequiredWebsiteSignIn) enforces a 401 or login redirect.
TenantGuard
Reject cross-tenant token use with a fail-closed 403, never reaching the protected handler.
NewDomainPolicy / IPPrefix
Scope the auth cookies across subdomains, and mask client IPs for privacy-bounded audit and device binding.

One authenticated request

What the Gate does on the hot path.

  1. Read cookies Pull the access JWT and the split-proof B half from the request cookies.
  2. Verify Check the HS256 signature (pinned method), expiry, and recombine the split proof — any failure is unauthenticated.
  3. Refresh if needed If the access token expired, refresh via singleflight; a forked refresh family triggers revocation + audit.
  4. Set identity Put the verified AuthInfo into the request context for the handler and TenantGuard.
  5. Enforce mode Optional degrades to anonymous; Required returns 401 (or redirects to login) when no valid identity is present.

Fail-closed everywhere

Crypto ambiguity denies
Bad signature, a non-HS256 algorithm, a missing split-proof half, or an expired token all resolve to unauthenticated.
Replay revokes the family
A forked refresh token revokes the whole family and records an audit event rather than minting a token.
Tenant mismatch is 403
A token used on the wrong tenant subdomain is rejected with a no-store JSON 403; the handler never runs.
IPs never leak
Audit and device binding use a masked prefix; a raw client IP is never logged.

System fit

  • auth sits below the service handlers and above professor (MAC/crypto) and store (refresh-family persistence).
  • Binaries mount the Gate and TenantGuard via the switchboard router; appboot builds the DomainPolicy per deployment.
  • No display or business package reaches inside auth — they consume the AuthInfo it leaves in the context.
  • It registers through ceremony as the Authority Wirable (callees: store, professor), so a misconfiguration surfaces at boot.

Tests

token_test.go / token_adversarial_test.go
Issuance/verification: signature, algorithm-confusion rejection, expiry, split-proof reassembly, and forged/tampered tokens.
gate_test.go / gate_adversarial_test.go / gate_fork_revoke_hostile_test.go
The Optional/Required boundary, transparent refresh, family fork detection, and revocation.
tenant_guard_hostile_test.go
Cross-tenant isolation: the rejection's full response contract (403, JSON, no-store, canonical body), exact-match tenant comparison, and proof that a rejected request never reaches the protected handler.
ip_parse_hostile_test.go
The strict decimal parseIPv4 (no octal, strict octet bounds) and the IPPrefix privacy invariant that a raw IP is never returned.
proofs / cookies / domain / claims / device / audit suites
Split-proof 'neither half alone verifies', the five-cookie set/clear + injection resistance, cookie domain scoping, scopes, device binding, and audit-event validation.

auth-storage

auth/storage

The account-storage map behind registration and sign-in: one append-only ledger, fast projections for reads, transactional uniqueness guards, refresh-token families, and a durable outbox for email side effects.

When you look at Firestore during auth testing, the collections are not random tables. They are the ledger-first account model. Mutations append to the ledger first, then maintain the projections and guards needed for fast login, profile, admin, and email workflows. The same logical collections can be backed by Firestore, SQL, or a future store because the store interfaces and core collection constants own the contract.

Vocabulary

events
The canonical append-only ledger. User signup, signin, password changes, recovery, role changes, and other state changes are recorded here as typed ledger entries.
event_seqs
The sequence allocator/checkpoint lane for ledger ordering. Firestore does not provide one global append sequence, so ledgerwriter owns sequence allocation explicitly.
users
The current user projection. Login/profile/admin reads use this shape because reading the append-only ledger for every screen would be slow and awkward.
families
The refresh-token family projection. It tracks token lineage, fork/replay detection, revocation, and session continuity.
user_uniques
Transactional uniqueness guards for email, username, and phone. Firestore has no ordinary unique index, so registration reserves these docs before creating the user.
outbox
Durable side-effect intents. Email, magic links, reset links, login alerts, and similar notifications are written with the account transaction and delivered later by a worker.

Core concepts

Ledger first
The ledger is the truth. Projections are allowed to be rebuilt from it. If a future database changes, replaying the ledger is the migration path instead of trusting one vendor's current document shape.
Projection second
users and families are optimized read models. They exist so the hot path can answer 'who is this user?' and 'is this refresh family valid?' without scanning event history.
Guards are not profiles
user_uniques does not store the user profile. It stores normalized uniqueness reservations so signup cannot race two accounts into the same email, username, or phone.
Outbox prevents split brain
A reset-link email should not send unless the DB transaction also committed, and a committed transaction should not lose its email. The outbox turns that into a durable worker problem.
Devices and places are projections
Device and place collections should be derived from login/signup ledger events. user_devices and geo signals are security projections; device/place rollups are marketing projections. Auth should not write loose analytics tables directly.
Security data is not marketing data
Per-user device/location evidence is for account protection and suspicious-login alerts. Marketing rollups should aggregate coarse device type, browser, city, region, and country without needing names, emails, or raw identifiers.

One auth mutation

Registration, sign-in, recovery, and password changes follow the same storage discipline: validate the request, write the ledger, update projections/guards, enqueue side effects, then let workers drain durable intents.

  1. Validate ingress Transport parses typed request structs, auth/starter validates identity/password/device context, and core/user owns the user aggregate invariants.
  2. Reserve uniqueness Registration reserves email/username/phone in user_uniques inside the same write transaction as the user creation.
  3. Append event ledgerwriter allocates the next sequence, writes the ledger entry to events, and updates event_seqs so ordering is explicit.
  4. Update projections The transaction writes users and families for fast reads and session continuity. These are derived operational state, not the root truth.
  5. Enqueue side effects If an email or alert is required, the transaction writes an outbox intent instead of sending inline.
  6. Workers deliver Relay/notify workers later read outbox, send the message, and mark the intent delivered or failed without changing the ledger's historical fact.
  7. Project devices/places A projection worker can turn the same ledger evidence into user_devices, user location history, and aggregate device/place rollups without changing the auth hot path.
  • Collection existence by itself is not bad data; dirty dev documents are disposable, but the collection roles are the production architecture.
  • Deleting practice data should preserve the same logical collections once the next signup/signin flow runs.

System fit

  • auth/starter writes this model through store interfaces; handlers do not talk directly to Firestore.
  • ledgerwriter owns event sequence and chain behavior; store/firestore maps core collection constants to physical Firestore collections.
  • service/projection and service/counter can replay the ledger into read models and aggregates.
  • Login-alert and device-blocking work should extend this model with typed ledger/outbox/policy contracts, not one-off distro tables.

webauthn

webauthn

Runs the passkey (FIDO2/WebAuthn) ceremonies — registering a new passkey and logging in with one — by wrapping the go-webauthn library and adapting it to this codebase's user/credential types, with the ceremony state carried in an encrypted, self-contained token so the server stores nothing between the two halves of a ceremony.

A passkey is a key pair on the user's device; the private key never leaves it and the server stores only the public key. Logging in means the device signs a fresh random challenge that the server verifies — no password to phish. Each operation is a two-round-trip ceremony: registration (Begin issues a challenge, Finish verifies and stores the credential) and login (Begin issues a challenge naming known credentials, Finish verifies the signature). The challenge issued in Begin must be the exact one verified in Finish; where that challenge lives between the calls — an encrypted, stateless token — is the heart of the package.

Vocabulary

Relying Party (RPID/RPOrigins)
us, the verifier. RPID is our domain and binds every credential to this deployment; RPOrigins is the allow-list of browser origins a ceremony may come from.
Attestation / assertion
the signed object from the device during registration / login.
Challenge
a fresh random value the device must sign; prevents replay.
Authenticator
the package engine (NewAuthenticator) running all four ceremony steps; the ceremony.Wirable for the package.
SessionToken
the opaque, encrypted token holding the ceremony SessionData (including the challenge) between Begin and Finish.
AEAD
authenticated encryption (professor.AEAD, AES-GCM) that both hides and tamper-protects the session token.
SignCount
a per-use counter the caller persists to detect a cloned credential.

Core concepts

Two ceremonies, four steps
BeginRegistration / FinishRegistration store a new public-key credential; BeginLogin / FinishLogin verify an assertion and return which credential was used plus its updated sign count.
Stateless sealed sessions
SealSession AES-GCM-encrypts the SessionData (with the challenge) plus an expiry into the opaque SessionToken; OpenSession decrypts, authenticates, and checks expiry. No server-side session store — the client round-trips an encrypted, unforgeable token.
Tamper-proof challenge
AES-GCM's authentication tag means a flipped byte, truncation, or swapped envelope field fails to decrypt, so the client cannot alter the challenge it was handed; a token sealed with one deployment's key cannot be opened with another's.
Time-boxed with skew grace
The session expires after a short package-owned TTL; OpenSession rejects an expired token with ErrWebAuthnSessionExpired while retaining a bounded package-owned grace for clock corrections.
Fail-closed gates
An empty RPID/RPOrigins config is rejected at boot; the per-user credential cap (10, BUG-119) bounds DoS; revoked credentials are skipped and a revoked-only user cannot begin a login.

Methods

NewAuthenticator
Build the engine, validating RP config and the AEAD/NewID/Clock dependencies at boot.
BeginRegistration / FinishRegistration
Issue a registration challenge (excluding existing credentials, capped per user) and verify + store the new credential.
BeginLogin / FinishLogin
Issue an assertion challenge (usable credentials only) and verify the signature, returning the used credential id and sign count.
SealSession / OpenSession
AES-GCM seal the ceremony session into an opaque token and open it with tamper + expiry checks.

One login ceremony

Two round-trips, no server-side session state.

  1. BeginLogin Validate the user has usable credentials, ask the library for a challenge/assertion, and SealSession the challenge into a token returned to the browser.
  2. Device signs The browser signs the challenge with the passkey private key and posts the assertion back with the session token.
  3. OpenSession Decrypt + authenticate the token and check expiry; a tampered or stale token is rejected here.
  4. FinishLogin Validate the assertion signature against the stored public key and return the credential id + updated sign count for clone detection.

Fail-closed ceremonies

Tampered/expired sessions reject
OpenSession fails on any modified ciphertext, a wrong key, or an expired token (past TTL + grace).
Misconfigured RP can't boot
An empty RPID, RPDisplayName, or RPOrigins fails NewAuthenticator before the first ceremony.
Registration is capped
Once a user holds MaxCredentialsPerUser credentials, BeginRegistration returns ErrForbidden (DoS guard).
Revoked credentials are inert
Revoked credentials are skipped when building the library user, and a revoked-only user cannot begin a login.

System fit

  • webauthn sits below the auth-flow handlers and above professor (the AEAD that seals sessions) and the core/user + core/credential domain types.
  • A handler calls Begin to hand the browser a challenge, then Finish to verify; auth then issues the session token for the authenticated user.
  • No transport or display package reaches inside it, and no go-webauthn library type leaks past the adapter.
  • It registers through ceremony as the Authenticator Wirable (callees: auth, professor), so a misconfigured RP surfaces at boot.

Tests

adversarial_test.go
The session crypto boundary: bit-flip/truncation tampering, decrypt-with-wrong-key, expiry offsets with the clock-skew grace, envelope-field tampering, garbage bodies, and config rejection.
gates_hostile_test.go
The behavioral gates: usableCredentialCount (revoked don't count), BeginLogin rejecting a revoked-only user, and the BeginRegistration cap boundary (limit-1 allowed, limit and above ErrForbidden).
session_test.go / regression_test.go
Nanosecond expiry, pooled/base64 buffer encoding, the clock-skew grace, the BUG-119 cap, and revoked-credential skipping.
verification_test.go / webauthn_test.go
The full Begin/Finish registration and login ceremonies and the round-trip seal/open.
contracts_test.go
Every public type's Validate, the NewAuthenticator dependency checks, and the limit contracts.

switchboard

switchboard

The only router: it composes one immutable http.ServeMux from a list of registrars (the domain packages that have routes), and in the same pass produces a proof of exactly which routes went live — refusing to boot if any mutating route is left without CSRF protection.

Many packages have routes; if each reached into the mux directly nobody could say what the server actually serves. So switchboard splits the job: registrars declare routes on a narrow Router they are handed (they never see the real mux), and switchboard owns the real ServeMux, attributes every mount to its registrar, and emits a Snapshot that is the single source of truth for what is live. Route intent (a catalog) and route proof (what was actually mounted) are reconciled in one place, and security ratchets run over the proof before traffic is accepted.

Vocabulary

Registrar
a domain package that registers routes; implements Register(Router).
Router
the minimal surface a registrar may use: Mount (API route), MountPage (page), MountAsset (static asset). Nothing else.
Mount types
RouteMount (an api.Route + handler + applied layers), PageMount (page routes + handler), AssetMount (path + handler + mode).
Assemble / Runtime
Assemble runs every registrar once and returns Runtime{Handler, Snapshot} — the serving handler plus the route proof.
Snapshot / Descriptor
the typed projection of live API/page/asset routes + sites used for serving and ceremony; Descriptor is the ceremony Wirable carrying the live counts.
Gate / boundary
a route's protection class (core.RouteGate, e.g. GatePublic, GateAuthenticatedCSRF), mapped to a boundary label.
Route budget
an optional per-route deadline and max body size enforced at the mount edge.

Core concepts

Declare vs own
Registrars declare routes on a Router they are handed; switchboard owns the real mux, attributes every mount to its registrar (owner), and is the single source of truth for what is live.
One assembly pass
Assemble filters nil/typed-nil registrars, calls each Register once, reconciles mounts with catalogs, runs the CSRF ratchet, and builds the Runtime — so the served mux and the reported snapshot can never drift (BUG-565).
Intent vs proof
A registrar's catalog is only intent; a route is reported live only if the registrar actually mounted the same pattern under the same owner. The ceremony proof can't be faked by a catalog. Mounting is upsert — same pattern collapses to one entry.
CSRF ratchet fails closed at boot
Assemble runs ratchetUnsafeRoutes over every live API route; an unsafe-method route (POST/PUT/PATCH/DELETE) without a request-authenticity gate (CSRF, origin guard, webhook signature, step-up) or non-browser-facing/exempt status returns ErrUnsafeRouteUnprotected and fails the whole boot — never a silent live CSRF hole.
Per-route budgets
Switchboard applies the route's read/write deadline at the mount edge. Primitive Exchange is the single request-body owner and projects the same route's MaxBody into strict JSON, aggregate, or streaming ingress without a competing wrapper.

Methods

Assemble
Run the registration pass and return the live Runtime, or an error (incl. the CSRF ratchet failure).
Router.Mount / MountPage / MountAsset
The narrow surface registrars use to declare API, page, and asset routes.
CeremonyOptions
Project one Snapshot into the ceremony manifest's page/API/asset route surfaces.
MissingMiddleware
A fail-closed handler for a declared route boundary that wasn't wired at boot.

Assembling the router

One pass from registrars to a served mux + proof.

  1. Filter Drop nil and typed-nil registrars so a misconfigured slot can't panic the boot.
  2. Register Hand each registrar an ownedRouter and call Register once; every mount is tagged with the owner.
  3. Reconcile Report only the routes the registrar both declared and actually mounted under the same owner.
  4. Ratchet Run the CSRF ratchet over the live API routes; an unprotected unsafe route fails the assembly.
  5. Build Produce Runtime{Handler, Snapshot} and validate it; the snapshot also feeds the ceremony manifest.

Fail-closed wiring

Unprotected mutations can't boot
An unsafe-method route without request-authenticity (or an unparseable method label) fails Assemble with ErrUnsafeRouteUnprotected.
Invalid mounts are rejected
A nil handler, empty asset path, or invalid route fails the mount's Validate (fail-closed) rather than mounting a broken route.
Unwired routes aren't claimed
A declared-but-not-mounted or misattributed route never appears in the live snapshot.
Nil registrars are skipped
nil and typed-nil registrars are filtered before Register, so boot never dereferences them.

System fit

  • switchboard sits at the transport edge; each binary builds its registrar slice and calls Assemble.
  • The returned Handler is what the server serves and the Snapshot is what ceremony validates.
  • It imports api, ceremony, core, and transport, and depends on no service/business logic — registrars own request meaning, switchboard owns wiring and proof.
  • It registers through ceremony as the Descriptor Wirable, and projects its snapshot into the manifest via CeremonyOptions.

Tests

csrf_ratchet_hostile_test.go
The boot CSRF ratchet across the full method×gate matrix (Assemble enforces exactly core.CheckUnsafeRouteProtected), hard anchors, typed-nil registrar skipping, and the MissingMiddleware fail-closed handler.
routes_test.go
Live registrar reporting, dropping unmounted/misattributed reports, the per-route budget at the mount edge, asset-route carriage, and nil registrar skipping.
route_entries_hostile_test.go
Upsert (same pattern → one entry), state-clone independence, findAPI/findPage miss handling, and route-mux validation.
switchboard_test.go
Single/multiple/no-registrar assembly and the single-pass snapshot invariant.
contracts_test.go / contract_test.go
Every type's Validate fail-closed behavior, boundary/layer classification, unclassified-gate rejection, and that registrars don't bypass switchboard.

userapi

userapi

Serves authenticated user read, update, and account-deletion flows where every request is gated by login and a caller may only touch their own record unless they are an admin. GET/PUT use /api/v1/users/{id}; body-carrying deletion is the explicit POST action /api/v1/users/{id}/delete, where {id} may be 'me'.

Five ideas explain it. Self-or-admin authorization: every handler proves the caller may act on the target (same id, or admin/super_admin) — the IDOR boundary. Fail-closed wiring: GET declares an auth gate and PUT/POST mutations declare a CSRF gate; missing middleware mounts a 500 rather than downgrading to public. Read-inside-WriteTx: updates and deletes re-read inside the transaction. Step-up: changing phone/username or deleting requires the current password, and deletion also requires an emailed code. Every mutation writes a typed ledger entry and deletion enqueues its outbox intent in the same transaction.

Vocabulary

App
the registrar; holds the store, write strategy, password hasher, clock, and the route middleware closures.
Store backend
the user repository; a request may carry a scoped backend in context, else the App's default is used.
Write strategy
how a mutation and its ledger entry are persisted together inside one transaction (ledgerwriter.WriteStrategy).
Ledger / outbox
the append-only audit log every mutation writes to, and the transactional email/work queue a delete enqueues to.
Hasher / pepper
the password hasher + secret pepper used to verify the caller's current password for step-up actions.
me alias
{id} = 'me' resolves to the authenticated caller's id.
Step-up / delete challenge
re-proving identity (current password) for a sensitive change; delete also needs a time-limited emailed code.

Core concepts

Self-or-admin authorization (IDOR)
authorize denies anonymous callers, allows the self shortcut (caller id == target), and otherwise grants only admin/super_admin loaded from the store — any other role, an unloadable caller, or a nil store fails closed with ErrForbidden.
Fail-closed route wiring
GET is wrapped with private auth and PUT/POST mutations with CSRF; a missing boundary mounts a 500 handler rather than serving unprotected (BUG-532/528).
Read inside the write transaction
update and delete re-read the user fresh inside WriteTx so a concurrent ChangePassword can't be overwritten by a stale earlier read (BUG-555/556).
Step-up for sensitive changes
changing phone/username or deleting requires the caller's current password when acting on self (admins acting on others skip it); delete also requires a non-empty, unexpired emailed code compared in constant time.
Audited, recoverable mutations
every change writes a typed ledger entry with the actor and target; a delete is a soft-delete (StatusDeleted) that also enqueues an account-deleted outbox intent — all in one transaction. A banned user cannot self-delete (BUG-100).

Methods

Register
Mount GET/PUT and the POST delete action through switchboard with declared auth/CSRF boundaries, failing closed when middleware or the App is unconfigured.
handleGetUser / handleUpdateUser / handleDeleteUser
The three handlers: authorize, then read or transactionally mutate, returning a safe viewmodel.
authorize
The self-or-admin access check (the IDOR boundary).
verifySensitiveSelfAction / verifyDeleteChallenge
Step-up password verification and the password+code delete challenge.

Updating your profile

The guarded, transactional update path.

  1. Authorize Validate the request and prove the caller may act on the target (self or admin).
  2. Pre-check For phone/username, verify the current password (self) and that the new value is well-formed and not taken.
  3. Re-read in WriteTx Open the transaction and read the user fresh so concurrent changes aren't clobbered.
  4. Apply + record Apply changed fields, stamp UpdatedAt, and write the user mutation + a ledger entry together; if nothing changed, write nothing.
  5. Respond safely Return a public projection that never includes the password hash.

Fail-closed access control

Cross-user is admin-only
A non-admin touching another user is ErrForbidden; an anonymous caller is ErrUnauthorized.
Sensitive changes need the password
A self phone/username change or delete without the correct current password is rejected; a missing verifier fails closed.
Delete is challenged and bounded
Self-delete needs an unexpired emailed code; a banned user cannot self-delete; the status must legally transition.
Uniqueness conflicts are surfaced
A phone/username already owned by another user is ErrConflict, and invalid formats are rejected before the write.

System fit

  • userapi sits at the API layer; the host binary builds an App, wires the private/CSRF middleware and the password verifier, and registers it via switchboard.
  • It depends on auth (caller identity), store (persistence), ledger/ledgerwriter (audit), outbox (async work), professor (password verify), and the typed api request/viewmodel contracts.
  • Responses are safe projections (PublicProfileView) — no password hash or secret leaves the boundary.
  • It registers through ceremony as the App Wirable (callees: auth, store), so a missing boundary or verifier surfaces at boot.

Tests

authorize_idor_hostile_test.go
The role-elevation IDOR matrix: cross-user access granted only for admin/super_admin (loaded from the store), fail-closed for staff/customer, an unloadable caller, and anonymous.
username_validation_hostile_test.go
isValidUsername length boundaries and the ASCII-only charset (whitespace, punctuation, separators, control bytes, non-ASCII letters, emoji, and homographs all rejected).
userapi_test.go
The ownership boundary, fail-closed missing-store handlers, the safe toProfile projection, the me alias, phone normalization buckets, and the update path.
regression_test.go
Read-inside-WriteTx (BUG-555/556), banned-self-delete and admin-cross-delete (BUG-100), the delete code/password challenge (BUG-332/334/335), phone-verified resets (BUG-099/573), duplicate-identity rejection (BUG-080), the account-deleted outbox intent (BUG-329), and fail-closed middleware wiring (BUG-532).
contracts_test.go
Error-identity distinctness, format/string contracts, ceremony details derivation, and that every input type's Validate gates production inputs.

transport

transport

The HTTP↔service adapter: it wraps a pure service method func(ctx, Req) (Resp, error) into an http.Handler that safely decodes the request, calls the method, and writes a typed JSON envelope back — bounding body size, decoding strictly, never caching, and never leaking internals.

Primitive Exchange owns bounded strict JSON, explicit no-body framing, projection ordering, operation policy, and the final wire write. Kernel transport retains its typed API envelope and adds route policy, request IDs, middleware, domain error mapping, no-store caching, and typed path/query projection. Service methods remain HTTP-free func(ctx, Req) (Resp, error) values.

Vocabulary

Response
a narrow Validate + EncodeHTTP contract; api.Success and api.Failure preserve Kernel's concrete envelope while Primitive Exchange owns the HTTP effect.
HandlerFunc / MidFunc / Chain
a handler that returns a typed Response; a middleware constructor; and the composer that applies middleware outermost-first.
JSON / JSONWithParams
the generic wrappers that turn a service method into an http.Handler (the latter also binds path/query via api.FromRequest at compile time).
ReceiveJSON / ReceiveProjectedJSON / ReceiveNoBody
typed adapters over Primitive Exchange's strict JSON and explicit no-body server boundaries.
Route body budget
a compiler-owned api.Route budget may narrow Primitive's default strict-JSON maximum; no loose context override exists.
BindKey / BindInput / Bind* helpers
typed path/query binders (BindPathULID, BindUserPathULID with the 'me' alias, BindQueryString/Int/ULID).
Respond / logFault
write the typed response through Primitive Exchange (always no-store), and log a fault without leaking internals to the client.

Core concepts

Handlers return values, not bytes
Service methods return (Resp, error) and never touch HTTP; transport reads the request id, decodes/validates, calls the method, and writes an api.Success or a client-safe api.Failure envelope through one Respond path.
Safe JSON decoding is the input boundary
Primitive Exchange rejects over-limit, malformed, duplicated, unknown, truncated, and trailing JSON; closes the stream exactly once; then validates only after typed path/query/header projection completes.
Body limits fail safe
Primitive owns the default strict-JSON cap. A compiler-owned route budget may narrow it at both the switchboard edge and Exchange reader; no loose override can widen it.
API responses are never cached
Respond always sets Cache-Control: private, no-store (BUG-425) so an intermediary can never cache an API response; a nil Response is 204 No Content.
Compile-time param dispatch
JSONWithParams uses a pointer generic constraint to move FromRequest dispatch to compile time — no per-request interface assertion (BUG-554).

Methods

JSON / JSONWithParams
Wrap a body-only or param-bearing service method into an http.Handler with optional middleware.
ReceiveJSON / ReceiveProjectedJSON / ReceiveNoBody
Use the route's typed Primitive Exchange semantics for body, projection, and no-body boundaries.
BindPathULID / BindUserPathULID / BindQuery*
Typed path/query binders, including the 'me' alias that resolves to the caller or fails Unauthorized.
Respond / Chain
Write a typed Response through Primitive Exchange with no-store caching; compose middleware outermost-first.

One request through JSONWithParams

HTTP in, typed envelope out.

  1. Request id Extract the correlation id from headers.
  2. Receive body contract Primitive Exchange receives strict bounded JSON or validates an explicit no-body route before application dispatch.
  3. Bind + validate params FromHTTPRequest binds path/query/headers, then the request's Validate runs.
  4. Call the service Invoke fn(ctx, req); on error, MapError → a client-safe Failure envelope; logFault records the internal detail.
  5. Respond Write the Success/Failure envelope with no-store caching.

Fail-closed at the boundary

Oversize is rejected
An over-limit ContentLength or an over-cap stream is ErrPayloadTooLarge, with a write deadline set on the connection.
Malformed/strict/trailing is a 400
Unknown fields, malformed JSON, trailing data, and decoder panics all map to a client InputError (4xx), never a 500.
No internal leak
The client receives only the mapped fault body; the internal error (incl. any panic text) is logged, not returned.
Anonymous 'me' is Unauthorized
BindUserPathULID resolving 'me' with no authenticated caller returns ErrUnauthorized.

System fit

  • transport sits between the switchboard router and the service packages (userapi, auth flows).
  • A registrar wraps its service method with JSON/JSONWithParams and mounts the resulting handler.
  • It imports api (envelopes/contracts), core, and telemetry for logging, and depends on no business logic — request meaning lives in the service method.
  • It registers through ceremony as the Descriptor Wirable, reporting the configured max body size.

Tests

bind_strict_hostile_test.go
The decode boundary: DisallowUnknownFields, malformed JSON → InputError, the decoder-panic→safe-400 mapping that never leaks the panic text, and the bodyLimit override with a fail-safe default.
hostile_test.go
Middleware LIFO ordering, bind→FromRequest→Validate order, short-circuit on bind error, trailing-data rejection, the one-byte-over / at-limit boundary, body close, and logFault secret redaction.
requestbind_test.go
Every path/query binder, the 'me' alias resolution and anonymous→ErrUnauthorized, and nil-request guards.
transport_test.go / regression_test.go / contracts_test.go
The full handler paths and error→status mapping, the BUG-425 no-store guarantee on every Respond path, and the typed contracts/Validate methods.

ua

ua

Classifies the browser's User-Agent string — answering 'is this a WebKit browser?' (so the server knows whether it may send a zstd-compressed response) and 'what OS/browser/device is this?' (for marketing) — using only zero-allocation string matching safe for the request hot path.

Two unrelated needs share one tiny package. The zstd safety gate: WebKit has a broken zstd streaming decoder, so before choosing zstd the server must know whether the client is WebKit — that is IsSafari, and a wrong answer corrupts the page for a real user, which is why it is the load-bearing function. Marketing segmentation: ParseMarketing reports OS/browser/device for analytics, where a wrong answer is cosmetic. The governing doctrine is 'detect the engine, not the brand': Apple mandates WebKit for every iOS browser, so Chrome/Firefox/Edge on iOS are all WebKit and all have the zstd problem despite their brand.

Vocabulary

Browser engine
the code that renders and fetches: WebKit (Apple), Blink (Chrome/Chromium), Gecko (Firefox). The brand is not the engine.
Token
a substring matched in the UA (Chrome, CriOS, Safari, EdgA, …); the literals live in core.UAToken* constants.
The Safari-token trap
almost every UA contains the literal 'Safari' token for historical reasons, so its presence alone does NOT mean WebKit — order and exclusions matter.
UAInput
the typed wrapper around the raw User-Agent string; an empty/invalid input yields the safe default.
IsSafari
the WebKit (zstd) gate — true for any browser using Apple's WebKit engine.
ParseMarketing / UAMarketingResult
OS/browser/device-type classification for marketing segmentation.
Detector
the stateless ceremony.Wirable representing this package.

Core concepts

Detect the engine, not the brand
iOS browsers rebrand WebKit (CriOS/FxiOS/EdgiOS); they are all WebKit and must all be treated as Safari for zstd, and a novel iOS brand with the WebKit signature must still be detected.
IsSafari ordering is the correctness argument
Check Chrome first (Blink → not Safari, also the majority fast path), then the iOS rebrands (CriOS/FxiOS/EdgiOS → Safari), then 'no Safari token at all' → not WebKit, then EdgA/Chromium → Blink, and only what remains is real Safari.
Cross-function engine invariant
Anything ParseMarketing calls iOS must be IsSafari==true; a disagreement would let the server stream zstd to a WebKit client and corrupt the response.
Marketing browser order is load-bearing
Opera and Edge UAs contain the Chrome token and Chrome contains the Safari token, so the browser check runs Opera→Edge→Firefox→Chrome→Safari — otherwise Opera/Edge read as Chrome and Chrome as Safari.
Zero allocation on the hot path
Pure strings.Contains matching against compiled token constants — no parsing, no regex, no allocations — because IsSafari runs on every response's compression decision; allocation budgets keep it that way.

Methods

IsSafari
The WebKit/zstd gate; zero-allocation ordered token matching.
ParseMarketing
OS/browser/device-type classification returning a validated UAMarketingResult (or the safe default).
Describe
Return the stateless Detector for ceremony wiring.

Choosing a compression for a response

How the zstd gate is consulted.

  1. Take the UA Wrap the request's User-Agent header in core.UAInput.
  2. Ask IsSafari Run the ordered token checks: Chrome→Blink, iOS rebrands→WebKit, no Safari token→not WebKit, EdgA/Chromium→Blink, else→WebKit.
  3. Gate zstd If WebKit, do not send a zstd streaming response (its decoder is broken); choose a safe encoding instead.
  4. Segment (separately) Marketing/analytics call ParseMarketing for OS/browser/device, independent of the zstd decision.

Safe defaults

Empty/invalid UA is not Safari
An invalid UAInput makes IsSafari return false and ParseMarketing return the all-unknown/desktop default.
Unknown OS/browser folds to unknown
An unrecognized OS or browser is reported as unknown rather than guessed.
Out-of-range result folds to default
A marketing result that fails validation folds back to the safe default triple.
Engine truth is never violated
An iOS UA is always Safari, so a WebKit client is never offered broken zstd.

System fit

  • ua is a leaf utility importing only core (tokens + result types) and ceremony.
  • The response/compression layer calls IsSafari to decide whether zstd is safe; marketing/analytics call ParseMarketing.
  • Nothing depends on ua for business-logic correctness — only for the engine-safety and segmentation signals.
  • It registers through ceremony as the Detector Wirable (a stateless null object).

Tests

engine_consistency_hostile_test.go
The production invariant: IsSafari agrees with the iOS-engine truth (iOS ⟹ Safari, so a WebKit client is never sent zstd), and 'detect the engine, not the brand' (a novel iOS browser brand is still Safari).
ua_test.go
The big real-UA tables for IsSafari (macOS/iOS Safari and CriOS/FxiOS/EdgiOS true; desktop/Android Chrome/Edge/Samsung/Firefox false, incl. Chrome-beats-CriOS precedence) and ParseMarketing, plus allocation budgets.
hostile_test.go
Browser/OS/device priority ordering (Opera>Edge>Firefox>Chrome>Safari; iPhone>Macintosh; iPad>FxiOS), token uniqueness/non-emptiness, enum fold-to-unknown, empty-UA defaults, concurrency determinism, and result validation.
contracts_test.go / ua_ceremony_test.go / external_regression_test.go
The typed contracts, the Detector ceremony wiring, and external regression cases.

server

server

Owns the outer HTTP server boundary: typed lane configs, mandatory timeouts, bounded graceful shutdown, no implicit DefaultServeMux, and ceremony-visible runtime details.

server is the final process boundary after switchboard and middleware have built a handler. It does not decide routes and it does not know business logic; it commits a validated net/http.Server to one lane (website, webapp fast, webapp upload, admin, or api), then serves until cancellation and drains within ShutdownTimeout.

Vocabulary

Config
the typed server contract: address, lane, five request/shutdown durations, and MaxHeaderBytes.
ServerLane
core-owned enum naming the serving profile: website, webapp-fast, webapp-upload, admin, or api.
Descriptor
the ceremony.Wirable projection of a Config; it reports the exact lane, address, timeout matrix, and header cap at boot.
ServeListener
serve using a pre-bound listener so cmd packages can fail fast on port conflicts before exposing traffic.
ShutdownTimeout
the maximum graceful-drain window; shutdown detaches from caller cancellation but remains bounded by this duration.

Core concepts

No hidden default mux
New rejects a nil handler. The Go standard library would otherwise use http.DefaultServeMux, a global route table outside switchboard, ceremony, and tests.
Timeouts are boot contracts
Addr, Lane, all durations, MaxHeaderBytes, and ReadHeaderTimeout<=ReadTimeout are validated before serving, so unbounded or dead timeout config fails at boot.
Graceful shutdown survives cancellation
ServeListener uses context.WithoutCancel plus WithTimeout(ShutdownTimeout), preserving context values while giving shutdown a real deadline even after the parent is cancelled.
Idempotent shutdown result
sync.Once prevents duplicate shutdown work, and the first shutdown error is stored so every concurrent caller sees the same result.
Core-owned details
All lane names, timeout defaults, field names, network protocol, and error formats live in core; server consumes them without copied strings.

Methods

WebsiteConfig / WebappFastConfig / WebappUploadConfig / AdminConfig / APIConfig
Typed lane constructors using core-owned timeout defaults and DefaultMaxHeaderBytes.
New
Validate Config and handler, build net/http.Server, then re-validate the constructed Server.
Serve / ServeListener
Validate server and context, bind or accept a listener, serve, and gracefully drain on cancellation.
Shutdown
Validate server and context, then run idempotent graceful shutdown.
Describe
Validate Config and return the ceremony Descriptor.

Serving one binary

The path from command boot to graceful drain.

  1. Choose lane config cmd selects WebsiteConfig, WebappFastConfig, AdminConfig, or APIConfig with its bound address.
  2. Build handler switchboard returns the mux and global middleware wraps it; this handler must be explicit and non-nil.
  3. Describe for ceremony cmd calls server.Describe(serverCfg), and the boot manifest records lane/address/timeouts/header cap.
  4. Serve Serve binds core.NetTCP or ServeListener accepts the pre-bound listener, then starts http.Server.Serve.
  5. Drain On cancellation, shutdown detaches from the cancelled context, applies ShutdownTimeout, waits for the serve goroutine, and returns the typed result.

Fail-closed server boundaries

Invalid config
Empty address, invalid lane, non-positive duration/header cap, or header timeout greater than read timeout wraps ErrServerInvalidConfig.
Nil handler/context/listener
Nil runtime inputs return ErrInvalidInput before net/http or context can fall back or panic.
Listen failure
A bind failure wraps ErrServerListenFailed and preserves the underlying error identity.
Serve failure
Unexpected http.Server.Serve errors are wrapped; http.ErrServerClosed is normalized to nil for clean shutdown.

System fit

  • Every service binary registers server.Describe(serverCfg) with ceremony.WithServer.
  • server sits outside global middleware and switchboard; it owns serving, not routing.
  • appboot.StartServer is the cmd-facing wrapper that creates/starts this package and handles process shutdown plumbing.
  • It imports only core and ceremony plus standard library networking primitives.

Tests

shutdown_concurrency_hostile_test.go
The BUG-128 contract under concurrency: many simultaneous Shutdown calls run the work once, share one result, and are race-free under -race; plus the fail-closed nil-handler rejection at construction.
server_test.go
Config-to-http.Server wiring, lane defaults + Validate-cleanliness, ceremony details, listen/serve/shutdown behavior, nil/zero-value guards, AST ratchets, and shutdown bug regressions.
contracts_test.go
Error identity, format wrap verbs, field/string contract uniqueness, nil handler/context/listener gates, and Validate ownership.
regression_test.go / fuzz_test.go
Live listener regressions and fuzz coverage for config validation.

wall

wall

The security perimeter: three independent middlewares every request passes through before the app — Armor (security response headers), Bastion (IP blocking + rate limiting), and Gate/Admission (global capacity + per-tenant fairness).

wall runs early in the global pipeline (cyclops → horizon → Armor → Bastion → Admission → normalize → telescope): it stamps protective headers, turns away blocked or flooding clients, and sheds load before a request touches routing or handlers. Each middleware is independent — a binary can wire one without the others — and each makes only perimeter decisions, passing everything else through.

Vocabulary

Armor
middleware that sets security response headers (CSP, nosniff, frame, referrer, permissions, HSTS) and re-applies them so inner handlers can't strip them.
CSP
Content-Security-Policy: controls what a page may load/embed/connect to and who may embed the page.
Bastion
middleware that blocks banned IPs (403) and rate-limits floods (429), emitting alert signals.
RateLimiter
a per-IP token bucket with a bounded number of tracked IPs (a DDoS memory cap).
Gate / Admission
middleware that caps concurrent in-flight requests globally and keeps one tenant from starving the others.
Mode
the operational posture (Peace / War), a typed enum with the full kit.
Descriptor
the ceremony.Wirable reporting the live wall posture (which layers are wired).

Core concepts

Security headers can't be stripped
Armor wraps the ResponseWriter and re-applies its headers at WriteHeader/Write time, so an inner handler that deletes or overwrites them loses (BUG-132); it also strips HTTP/2-reserved pseudo-headers before sealing (BUG-708) and preserves Flush/Hijack for SSE/WebSocket.
CSP can't inject headers
ArmorConfig.Validate bounds the custom CSP length and rejects any CR/LF — a newline in a CSP value would split the response header and inject arbitrary headers — and a raw custom CSP is mutually exclusive with the typed source lists so framing/connect/script policy can't vanish into an untyped string.
Explicit frame policy
With no FrameAncestors, Armor sends X-Frame-Options: DENY; with an allow-list it omits that header and relies on CSP frame-ancestors (multi-origin). The typed CSP is built once at boot from frame-ancestors/embed/connect/script-hash inputs.
Rate limiting is DDoS- and clock-safe
The per-IP token bucket caps the number of tracked IPs (a new IP beyond the cap is denied, not allocated) with a race-safe post-store eviction (BUG-165), and clamps elapsed time to >=0 so a backward clock jump can't drain a bucket (BUG-470).
Capacity with fairness
Gate caps concurrent requests at MaxGlobal via lock-free atomic CAS and enforces per-tenant fairness so one tenant can't consume the whole budget; once a tenant resolver + threshold are set, MaxPerTenant must be > 0 or the guard is bypassed (BUG-123).

Methods

Armor
Build the security-header middleware from a validated ArmorConfig (CSP injection-guarded, frame policy explicit).
Bastion
Build the block+rate-limit middleware; nil limiter folds to block-only, nil signal to no-alerts.
NewGate / NewRateLimiter
Build the admission gate (capacity + fairness) and the per-IP token-bucket limiter.
Describe
Derive the ceremony Descriptor from the live wall runtime objects.

One request through the perimeter

Armor, then Bastion, then Admission.

  1. Armor wraps the writer Security headers are scheduled to stamp at write time and survive inner-handler tampering.
  2. Bastion checks the IP Blocked IP → 403; an empty rate-limit bucket → 429 + Retry-After; both emit a signal.
  3. Admission reserves capacity Reserve a global slot (and a per-tenant slot); over-capacity is shed, not queued.
  4. Pass through The request continues to normalize/routing; on the way out Armor seals the security headers.

Fail-closed perimeter

Blocked/flooding clients are turned away
A banned IP gets 403 and a flood gets 429 + Retry-After before reaching the app.
CSP injection is rejected
A CR/LF or over-length custom CSP, or a custom CSP coexisting with typed source lists, fails ArmorConfig.Validate.
Memory is bounded under attack
The rate limiter denies new IPs past the tracked-IP cap rather than growing its map without bound.
Fairness can't be bypassed
A tenant-aware Gate with a zero MaxPerTenant fails Validate so one tenant can't silently saturate global capacity.

System fit

  • wall is global middleware mounted by each binary ahead of routing.
  • It imports core (header/CSP/IP/mode contracts) and depends on no service or business logic.
  • appboot/compass supply the CSP allow-lists from customer config; the blocker and limiter are injected.
  • It registers through ceremony as the Descriptor Wirable, reflecting which perimeter layers are wired.

Tests

armor_csp_injection_hostile_test.go
The CSP header-injection guard (CR/LF in a custom CSP rejected) and the complete CSP-exclusivity matrix (custom CSP can't coexist with script-hashes; invalid hash elements rejected).
armor_frame_hostile_test.go
Strip-resistance: Deny mode overwrites a hostile inner X-Frame-Options, and CSP-ancestors mode strips it on both the WriteHeader and Write paths.
wall_test.go
RateLimiter behavior (burst, refill, max-tracked-IPs cap, per-IP isolation, burst boundary, concurrency), Bastion (block/limit/passthrough/signal, IP wrapper handling), Armor CSP building, and the boundary-bucket Validate sweeps.
contracts_test.go / regression_test.go
Error-identity ratchets, enum JSON round-trips, Validate boundary buckets, and the bug regressions (BUG-133 rate/burst, BUG-470 clock skew, BUG-166 reconfigure, refresh exemptions).
boundary_buckets_test.go / bastion_test.go / armor_test.go
The Mode enum sweep, the per-tenant gate (BUG-123), and Armor error-on-invalid-config.

squeeze

squeeze

Compresses bytes at the absolute maximum quality of three codecs — Zstandard (level 22), Brotli (quality 11), and Gzip (level 9) — for use offline at build time, so the assets it produces are as small as possible before they're ever served.

Compression trades speed for size; the highest settings are too slow per request but produce the smallest output. squeeze runs in cmd/compile during the build, not on the request hot path, so it can afford the slowest, best settings. The compressed blobs it makes are then served straight from memory by ballast/html with zero per-request compression — compress once slowly at build, serve instantly forever. It is a pure utility library (like ua): not a ceremony component, no runtime state.

Vocabulary

Codec
a compression algorithm; the browser picks one via Accept-Encoding, so we precompress in all three.
Level / quality
the effort dial; higher means smaller and slower. squeeze pins each codec to its maximum.
Encoder
a validating batch handle for zstd with an explicit open/close lifecycle (the compressor itself is stateless per call).
AlignTo64
rounds a byte offset up to the next 64-byte cache-line boundary, used when laying out the compiled asset blob.

Core concepts

Offline, maximum quality
Zstd level 22, Brotli quality 11, Gzip BestCompression — too slow for a request, ideal for a build that runs once and yields the smallest assets.
Empty is nil, not an error
Each compressor returns (nil, nil) for empty input; codec failures wrap core.ErrSqueezeCompression joined with the library error so callers match the class with errors.Is.
Round-trip fidelity at scale
Anything compressed must decompress byte-for-byte; for incompressible input the compressed form can exceed the capacity hint and force the output buffer to grow — a path that must stay correct on large inputs (the BUG-144/438 buffer-sizing territory).
Explicit Encoder lifecycle
NewEncoder marks the handle ready (atomic), CompressZstd validates readiness so use-after-close is a typed error not a panic, and Close uses compare-and-swap so a double Close is a typed error.
Overflow-safe alignment
AlignTo64 returns the offset unchanged when rounding up would overflow near math.MaxInt, never wrapping to a negative (BUG-145).

Methods

Zstd / Brotli / Gzip
Compress []byte at each codec's maximum quality; empty → nil; errors wrap ErrSqueezeCompression.
NewEncoder / CompressZstd / Close
The batch zstd handle: ready on construction, validates readiness on use, CAS-guarded close.
AlignTo64
Round an offset up to the next 64-byte boundary, overflow-safe near MaxInt.

Compress once at build

How an asset becomes a small served blob.

  1. Build invokes squeeze cmd/compile passes each HTML page / static asset to Zstd, Brotli, and Gzip.
  2. Maximum-quality compress Each codec runs at its top setting; empty inputs yield nil; failures wrap ErrSqueezeCompression.
  3. Lay out the blob AlignTo64 aligns each entry to a cache line as the compiled blob is assembled.
  4. Serve instantly ballast/html serve the precompressed bytes per the client's Accept-Encoding with zero per-request compression.

Typed, class-matchable failures

Compression failure
A codec error wraps core.ErrSqueezeCompression joined with the underlying library error.
Use-after-close
CompressZstd on a closed Encoder returns a typed error before any dereference.
Double close
A second Close finds the handle already not-ready and returns a typed error.
Alignment overflow
AlignTo64 passes the offset through unchanged rather than overflowing to a negative.

System fit

  • squeeze is a build-time leaf: cmd/compile calls it to pre-compress HTML and static assets into the blobs ballast and html serve.
  • It imports only core (levels/sizes/error constants) plus the zstd/brotli libraries.
  • Nothing depends on it at request time — the request path serves the precompressed output.
  • It is a pure utility with no ceremony.Wirable entry (like ua), holding no runtime state.

Tests

squeeze_test.go
Per-codec round-trips, empty → nil, single byte, null-byte blocks, incompressible data, already-compressed input, repeated sizes; AlignTo64 (never-decreases, multiple-of-64, formula); the Encoder (ultra level, concurrent); and Zstd/Brotli fuzz targets.
roundtrip_large_hostile_test.go
At-scale round-trip fidelity: ~1 MB compressible, 256 KB incompressible (forces the output buffer to grow past its hint — the BUG-144/438 territory), a mixed buffer, and a full byte-range buffer, each recovered byte-identical for all three codecs.
hostile_test.go
Encoder validation (nil/zero/use-after-close typed errors before dereference), every empty shape returns nil, AlignTo64 boundary buckets + idempotence, error-format identity wrapping, and concurrent compress race-freedom with integrity.
regression_test.go / contracts_test.go
BUG-144 (brotli grow), BUG-145 (AlignTo64 near MaxInt), BUG-438 (single output-buffer alloc), and the error-identity/format/limit contracts.

seedstarter

seedstarter

Reads the account-seed file (the JSON describing the bootstrap admin account) and mints the user.User it describes — purely, with no I/O, clock, or randomness of its own.

A fresh deployment has no users, so no one can log in to create the first admin — a chicken-and-egg problem solved by a seed: an operator-authored JSON file imported once by cmd/seedStarterAccount. This package does only the pure part — decode the JSON into typed values and assemble the user.User. The impure things (clock, random IDs/anchors, password hashing, database writes) are observed by the command shell and injected in, so the same minted identity can be written to several databases without drift.

Vocabulary

Seed file (SeedFile)
the whole decoded document: schema version, execution posture, redirects, and the accounts to create.
Execution (SeedExecution)
the run posture: which mode/store and which side effects (write a ledger entry? send email?).
Bootstrap / Plain password
the password directives; SeedPlainPassword is the operator-filled plaintext, hashed under each target's pepper and never persisted.
Mint (MintUser)
assemble the final user.User from the seed plus the injected identity/time/hash.
Anchor
a UUID tying a user's tokens to their identity (TokenAnchor and the two birth-token values).
Pepper
a per-deployment secret mixed into the password hash.

Core concepts

Pure decode + mint
The package reads no clock, generates no randomness, and writes nothing; the shell injects the minted ID, now, password hash, and anchors, so MintUser is a pure function and the identity is minted once and reused across every target.
Strict ingress
DecodeSeed uses DisallowUnknownFields (a drifted/renamed directive fails loudly, not silently dropped), rejects trailing documents and a nil reader, and validates — everything wrapping ErrSeedInvalidSchema.
Audit + silence invariants
Creating the most privileged account has two hard rules: WriteLedgerEntry must be true (the creation must be audited) and SendEmail must be false (a seed import is silent); only create_only over firestore is supported.
Typed, bounded, never-persisted password
SeedPlainPassword's zero value is the explicit 'not filled yet' state, non-empty values are length-bounded (256) so a corrupted seed can't stream megabytes into the hasher, and the plaintext is hashed under each pepper and never stored.
Mint mirrors a real sign-up
MintUser builds the same user.User a normal registration would, stamps the injected now onto every birth/created/updated instant, applies last-login telemetry as a unit (device type ⇒ last-login time, or none at all), and returns a user that satisfies user.User.Validate.

Methods

DecodeSeed
Strictly decode + validate the seed JSON into a typed SeedFile (unknown fields, trailing data, and nil reader rejected).
SeedFile / SeedExecution / SeedUser Validate
Enforce the schema version, the audit/email posture, and the user record's identifiers, enums, and ranges.
SeedBootstrap.NeedsPlainPassword
Report when the write path must have an operator-filled password (derive set, slot empty); the dry path doesn't.
MintUser / MintInput.Validate
Assemble the user.User from the seed + injected identity, requiring a non-zero ID, now, password hash, and three UUID anchors.

Seeding the bootstrap account

From JSON file to a minted user, once.

  1. Decode The shell hands the file reader to DecodeSeed, which strictly decodes and validates the SeedFile.
  2. Observe identity The shell mints the ID + three UUID anchors, reads now, and hashes the operator-filled plaintext under each target's pepper.
  3. Mint once MintUser assembles the user.User from the seed plus the injected identity, validating the result.
  4. Write to each target The shell writes the same minted user to each configured store with the required ledger entry, and never persists the plaintext.

Fail-loud at the seam

Schema drift rejected
Unknown fields, a wrong schema version, trailing documents, or a nil reader fail with ErrSeedInvalidSchema.
Unsafe posture rejected
A seed without a ledger entry, with email enabled, or in any mode/store other than create_only/firestore is rejected.
Oversized password rejected
A plaintext over the 256-char bound fails at the decode boundary, never reaching the hasher.
Incomplete identity rejected
MintUser refuses a zero ID, empty now, missing hash, or non-UUID anchor before assembling the user.

System fit

  • seedstarter is the pure core of the cmd/seedStarterAccount tool.
  • The command shell reads the file, observes clock/entropy, hashes under each pepper, calls MintUser once, and writes to each store with the required ledger entry.
  • It imports core and core/user for typed contracts and nothing else; the DTO lives here (not core) because Role/Status are typed against the user package.
  • It is a pure utility with no ceremony.Wirable entry — no runtime state, runs in a one-shot command.

Tests

seedstarter_test.go
DecodeSeed happy path + unknown-field rejection + a hostile-case table (bad schema/mode/store, ledger disabled, email requested, derivation disabled, missing email, marketing opt-out, bad redirect, unknown role, empty accounts, trailing doc); NeedsPlainPassword; and MintUser (typed-field assembly, login-telemetry gating, determinism) + MintInput.Validate hostile cases.
seedstarter_security_hostile_test.go
The SeedExecution.Validate audit/email-invariant matrix directly (ledger required, email disabled, mode/store valid), and the SeedPlainPassword contract (zero = unfilled, the 256-char bound at/over the limit, non-string JSON rejected, marshal round-trip).
example_test.go
That the shipped example seed template decodes.

professor

professor

Kernel's symmetric crypto and hashing boundary: password hashes, MACs, encryption, hashes, and constant-time comparisons flow through one audited, OWASP-compliant door.

Scattered crypto is how subtle, catastrophic bugs creep in — a math/rand where crypto/rand was needed, a == where a constant-time compare was needed, an Argon2 cost too low. Professor keeps Kernel's server-held symmetric mechanisms small, reviewable, and uniformly hardened. Domain-separated Ed25519 structural signatures belong to Primitive attest. Stateful operations are interfaces whose constructors validate key material once at creation and re-check at the execution boundary; stateless operations are package functions.

Vocabulary

Primitive attest
the reusable bounded, domain-separated Ed25519 signing and proof-carrying verification contract used for structural artifacts.
Argon2id
a slow, memory-hard password hashing function, deliberately expensive to resist brute force.
Pepper
a server-wide secret mixed into the password before hashing, on top of the per-hash salt.
PHC string
the self-describing hash format $argon2id$v=19$m=...,t=...,p=...$<salt>$<hash> storing the params alongside the digest.
HMAC / AEAD
a keyed message authentication code; and authenticated encryption (AES-256-GCM) that hides and tamper-protects data with a prepended random nonce.
Constant-time compare
a comparison whose duration doesn't depend on where two secrets differ, so timing can't leak the secret.
CSPRNG
a cryptographically secure random source (crypto/rand), used exclusively — never math/rand.

Core concepts

One audited symmetric door
Kernel's symmetric crypto and hashing live behind professor; Primitive attest owns asymmetric structural signatures, keeping each security-critical surface small and explicit.
Validate once, re-check at the edge
Constructors enforce key sizes at creation (32-byte AEAD key and ≥32-byte HMAC key) and copy the material so callers can't mutate it; methods re-validate before use.
Password hashing is DoS-bounded both ways
Verify caps the password at 128 bytes, parses the PHC defensively (malformed = error, never panic), rejects params below the OWASP minimum AND above a ceiling (an attacker-supplied hash can't make Verify grind gigabytes), and compares in constant time with no short-circuit.
Encryption fails closed
Seal prepends a random nonce; Open extracts it and rejects a too-short ciphertext, and any tamper, swapped nonce, or wrong key fails the GCM tag — never partial plaintext. This seals the WebAuthn session token.
Correct, not just present
Hashes are checked against known-answer vectors (BLAKE3) and the stdlib (SHA-256), and the streaming SHA-256 hasher is proven equal to one-shot Sum256 across chunkings.

Methods

NewPasswordHasher (Argon2id)
Hash/Verify with pepper, PHC format, the 128-byte cap, and DoS-bounded params, constant-time.
NewMAC / NewAEAD
HMAC-SHA256 compute/verify (≥32-byte key) and AES-256-GCM seal/open (32-byte key, nonce-prefixed).
Equal / Sum256 / SumBLAKE3 / NewSHA256
Constant-time comparison and one-shot/streaming hashes; Primitive Keygen owns constructed secret material.

Using a primitive

Construct once, use safely.

  1. Construct NewMAC/NewAEAD/NewPasswordHasher validate key material once and copy it.
  2. Operate MAC/Seal/Hash re-validate, then run the primitive at OWASP parameters.
  3. Verify in constant time Verify/Open/Equal compare secrets with crypto/subtle or GCM authentication, leaking nothing via timing.
  4. Fail closed Any invalid key, malformed PHC, out-of-bounds Argon param, or tampered ciphertext is a typed error, never a panic or partial result.

Fail-closed crypto

Bad keys rejected at construction
Wrong-size HMAC/AES keys fail NewX with ErrProfessorInvalidKey/ShortKey before any operation.
Malformed hashes can't panic or DoS
A malformed PHC errors; params below the minimum or above the ceiling are rejected so Verify can't be made to grind.
Tampered ciphertext is rejected
Open returns an error (never partial plaintext) on a flipped byte, swapped nonce, wrong key, or short input.
Secrets compared in constant time
Equal/EqualString and HMAC/GCM verification never branch on secret content.

System fit

  • professor is a foundational leaf importing Primitive core, Kernel core, and vetted crypto libraries.
  • Kernel uses it for server-held symmetric mechanisms: auth (split proofs, password verify), webauthn (AEAD session sealing), seedstarter (password hashing), and ledger/sentinel (hashing).
  • Asymmetric structural attestations compose Primitive attest instead.
  • It registers through ceremony via the Vault presence marker; the primitives carry their own contracts.

Tests

professor_test.go
Argon2 PHC format + verify, HMAC, AEAD seal/open + unique/tampered/short, Sum256 (deterministic, matches stdlib), and concurrency.
adversarial_test.go / failclosed_test.go
AEAD cross-key, key isolation, tamper-every-byte, nonce cut-and-paste/re-prefix; PHC adversarial decode; wrong-pepper rejection.
hostile_test.go
Argon param validation (zeroed fields, below-OWASP, above-ceiling memory/iterations, thread bounds), Equal/EqualString buckets, Sum256/SumBLAKE3 distinctness, AES-GCM short-ciphertext + unique-nonce, and key-copy isolation.
hashers_hostile_test.go
The BLAKE3 known-answer vector (proves the real BLAKE3 digest, not just distinct-from-SHA-256) plus a library cross-check; streaming SHA-256 rides the Primitive digest door.
professor_fuzz_test.go / structural_security_test.go / contracts_test.go / totp_test.go
Fuzzing, the no-direct-crypto-import rule, the error/format contracts, and TOTP.

pay

pay

Orchestrates hosted checkout sessions (Stripe or PayPal) and computes the platform commission to split off each sale using exact Primitive Currency amounts.

A payment bug is a revenue leak (too little fee) or an overcharge (too much), so pay composes Primitive Currency amounts with Kernel product policy: amount and ISO currency are inseparable, arithmetic is exact minor-unit math, and no floating point enters the money path. The fee is min(max(amount × percent / 10000, minimum), amount); percent is in basis points (10000 = 100%); the fee is 0 when splitting is disabled or the amount is non-positive; and the fee never exceeds the amount, never overcharges on overflow, and rounds down — all in the customer's favor. The Orchestrator wraps providers through a structural CheckoutCreator interface, so pay depends on no provider SDK.

Vocabulary

Primitive amount
an exact signed minor-unit value inseparably bound to a validated ISO currency.
Basis points
percent × 100; 250 basis points = 2.5%, 10000 = 100%.
Commission split
the platform's cut of a sale, routed to the platform while the rest goes to the connected seller account.
Connected account / Application fee
the seller's provider account that receives the payment minus the exact platform fee.
Checkout session
a provider-hosted payment page; the result is a redirect URL.
Orchestrator
the engine that wraps the providers and applies the split; the ceremony.Wirable for the package.

Core concepts

Primitive Currency money math
No floats or parallel currency fields; the fee formula is min(max(amount × percent / 10000, minimum), amount) over exact currency-bound minor units.
Fee never overcharges
ComputeFee returns 0 when disabled/non-positive, returns 0 on a 128-bit multiply overflow (never a garbage fee), caps the fee at the amount (micro-transactions safe, BUG-172), and rounds down via integer division.
Fee is on the total, account only when split-on
Checkout computes the fee on amount × quantity (overflow-guarded), and sets the connected account ONLY when the split is enabled — routing with a 0 fee while split is off is a silent revenue leak (BUG-168).
Validated at every edge
Request.Validate requires positive amount/quantity, non-empty product/currency/URLs, and no amount×quantity overflow; the provider's returned URL is validated so a blank result is rejected before redirecting the user.
Provider-SDK-free
Providers satisfy the structural CheckoutCreator interface (adapters wrap the bridge senders in ignition); an unconfigured provider returns a typed ErrPay…NotConfigured.

Methods

New / Config.Validate
Build the Orchestrator; Stripe/PayPal optional, a valid SplitConfig required.
StripeCheckout / CreatePaypal
Create a hosted session with the fee on the total and the connected account gated on split-enabled.
ComputeFee
Exact minor-unit fee policy: disabled→0, overflow→0, cap-at-amount, rounds down.
EnabledProviders / Configured / CeremonyState
Report the live providers (sorted) and the configured state, degrading safely when unconfigured.

Creating a checkout

From a request to a redirect URL with the right fee.

  1. Provider check Return ErrPay…NotConfigured if the requested provider isn't wired.
  2. Total + fee Compose Primitive's exact amount with Kernel quantity policy, reject overflow, and compute the application fee on the total.
  3. Route the split Set the connected account only when the split is enabled.
  4. Create + validate Call the provider, then validate the returned CheckoutResult so a blank URL is rejected, not redirected to.

Money-safe failure

Invalid request rejected
Non-positive amount/quantity, missing fields, or an amount×quantity overflow fail Request.Validate / totalAmount with ErrPayInvalidRequest.
Overflow never overcharges
A fee multiply that overflows returns 0 rather than a wrapped/huge value.
Unconfigured provider is typed
Calling a non-wired provider returns ErrPayStripeNotConfigured / ErrPayPalNotConfigured.
Blank provider URL rejected
A provider returning an empty/whitespace checkout URL fails CheckoutResult.Validate at the orchestrator boundary.

System fit

  • pay is the checkout use-case layer; the CheckoutCreator interface is satisfied by adapters (wired in ignition) wrapping the bridge Stripe/PayPal senders.
  • It composes Primitive Currency with core product policy and ceremony wiring — no provider SDK.
  • Callers hand it a Request and get a redirect URL or a typed error.
  • It registers through ceremony as the Orchestrator Wirable, reporting the live providers and split posture.

Tests

split_test.go
The ComputeFee table (disabled→0, pct/floor/exact-tie, rounds-down, sub-cent→0, one-basis-point, overflow guard at MaxInt64 and MaxInt64/2→0), fee-never-exceeds-amount, fee-always-non-negative, and SplitConfig.Validate boundaries.
pay_test.go
Stripe/PayPal checkout tables (success, floor/pct wins, quantity>1 fee-on-total, idempotency passthrough, account propagation, provider-not-configured, empty provider URL, MaxUint64 quantity overflow), BUG-168 (both providers), fee-never-exceeds-total, totalAmount tables, and concurrent isolation.
hostile_test.go
Request.Validate every-axis (incl. amount×quantity overflow), SplitConfig.Validate every-axis + boundaries, CheckoutResult whitespace rejection, EnabledProviders sort, Configured, bridge error-identity passthrough, and concurrent fee-field isolation.
reporting_failsafe_hostile_test.go / contracts_test.go
The nil/no-provider reporting fail-safety (NotConfigured/empty/off without panic), and the error/format identities including the two overflow formats.

payapi

payapi

The payment HTTP surface: it creates hosted checkout sessions through pay, verifies Stripe and PayPal webhooks through bridge, and fulfills each confirmed payment exactly once through ledger + outbox inside atom.

payapi has two jobs. Checkout validates a typed api/requests.CheckoutRequest, mints an idempotency key, and asks pay.Orchestrator for a hosted checkout URL. Webhooks are the harder boundary: attacker-reachable provider callbacks are size-limited, signature-verified, parsed into typed events, and either ACK'd as unhandled or fulfilled exactly once. Fulfillment records a payment_completed ledger entry and enqueues a confirmation email in one atom transaction, so provider retries become idempotent no-ops instead of duplicate money events.

Vocabulary

Checkout session
a provider-hosted payment page; payapi returns its redirect URL to the caller.
Webhook
a signed provider callback saying a payment event happened.
Idempotency
replaying the same provider event has the same effect as processing it once.
Fulfillment
recording the payment_completed ledger entry and enqueueing the confirmation outbox message.
webhookReply
the typed status/body reply code; 4xx means do not retry, 5xx means retry.

Core concepts

Primitive owns currency
CheckoutRequest carries a Primitive Currency amount directly while core.PayProvider remains Kernel product policy. Boundaries validate both before execution.
Verification before fulfillment
Stripe HMAC verification and PayPal verify-endpoint checks happen before any ledger/outbox write. Unverified data is never fulfilled.
Typed ACK/retry semantics
webhookReply owns each rejection status/body pair. Forged or malformed requests are 4xx; transient verification, configuration, or fulfillment failures are 5xx so the provider retries.
Atomic idempotent writes
fulfillPayment runs ledger record and outbox enqueue inside atom.Do. ErrDuplicateOperation is treated as success so provider retries do not duplicate effects.
Bounded body handling
Primitive Exchange receives and closes each raw webhook body under the compiler-owned core.PayapiMaxWebhookBodyBytes route cap; provider SDKs receive only the bounded aggregate.

Methods

handleCheckout / createCheckout
Validate the API request, mint an idempotency key, and dispatch to the appropriate pay method.
handleStripeWebhook / handlePayPalWebhook
The inbound provider boundaries: body cap, signature verification, typed parsing, fulfillment.
writeWebhookError
Writes a typed webhookReply with prebuilt bodies and no http.Error allocation path.
fulfillPayment
Validates dependencies and params, then records ledger + outbox inside atom.Do.
Provider amount projection
Parses provider decimal strings directly into Primitive Currency amounts without float64; currency exponent policy, including JPY, remains Primitive-owned.

A verified webhook

How a provider callback becomes exactly one internal payment event.

  1. Read bounded body Primitive Exchange enforces the payapi route cap against both declared and streaming length, closes the body, and maps one byte over to webhookReplyPayloadTooLarge.
  2. Verify signature Stripe verifies HMAC locally; PayPal validates headers and calls the bridge verifier under timeout.
  3. Parse typed event PayPal uses encoding/json with trailing-data checks; Stripe bridge emits a core.StripeCheckoutSession DTO.
  4. Classify Unhandled verified events are ACK'd with 200. Completed payment events continue to fulfillment.
  5. Fulfill once atom.WithOperationID binds the provider event ID, then fulfillPayment records ledger + outbox atomically.

Webhook failure semantics

4xx stops retries
Malformed requests, oversized bodies, and forged signatures are client/provider faults and should not be retried.
5xx requests retry
Verifier outage, missing fulfillment dependencies, or storage failure returns 5xx so a real payment is retried.
Unknown reply fails closed
An undeclared webhookReply fails Validate and maps to a non-2xx response, never an accidental ACK.
Errors keep identity
Stable payapi wrapping formats live in core, so tests and callers use errors.Is/As rather than string matching.

System fit

  • payapi is mounted only in the api binary; website/webapp/admin call the API instead of talking to payment services directly.
  • It calls pay for provider-neutral checkout orchestration and bridge for webhook verification; provider SDKs do not leak into payapi.
  • It writes financial effects only through store ports inside atom, preserving idempotency and rollback behavior.
  • It registers through ceremony as CompPayapi with callees CompPay, CompBridge, and CompStore.

Tests

payapi_test.go
Checkout routing, idempotency keys, webhook fulfillment, retry/ACK paths, rollback, source ratchets, and direct handler boundaries.
event_test.go / boundary_buckets_test.go
Amount parsing, JPY zero-decimal handling, PayPal event/capture validation, hostile bodies, and fuzz corpus coverage.
webhook_reply_hostile_test.go
Every webhookReply status/body mapping plus the 4xx no-retry vs 5xx retry invariant and zero-value fail-closed behavior.
security_invariants_test.go / contracts_test.go
Verify-before-fulfill, error identity distinctness, and stable core-owned format contracts.

normalize

normalize

The request-canonicalization perimeter: middleware that cleans and validates every request's Host, Path, and Query into one canonical form — rejecting the malformed and the malicious — before any routing, auth, or tenant decision is made.

Downstream code decides which tenant a request belongs to (Host), which route it hits (Path), and what it asks for (Query). If two byte sequences could mean the same thing — or one could sneak past a check and mean something else at the origin — those decisions are unsafe. normalize collapses each field to a single canonical form and rejects anything that can't be canonicalized, stopping host spoofing/homographs, path traversal (incl. encoded %2E%2E), and CR/LF response splitting.

Vocabulary

Host normalization
Host() turns a raw Host header into a HostResult (HostOnly label + full Normalized host[:port]).
Tenant / face
the subdomain identity extracted from the host (admin.lfw.com → face admin); a Resolver knows the base domain and declared faces/tenants.
Path normalization
Path() resolves ./.. and percent-encoding into a safe absolute PathResult.
Query normalization
Query() sorts and canonicalizes the query string.
Reject code
a typed reason (BAD_HOST, PATH_TRAVERSAL, BAD_ENCODING, BAD_QUERY) emitted when a request is refused.
HostInfo
the parsed host/tenant/face the middleware puts in the request context for downstream consumers.

Core concepts

Canonicalize before anyone trusts it
Host/Path/Query are collapsed to one canonical form so downstream auth, routing, and tenant decisions reason about clean, unambiguous input; anything that can't be canonicalized is rejected.
Host blocks spoofing
Non-ASCII (homograph), control/invisible, leading/consecutive dots, stray brackets, and out-of-range ports are rejected; standard ports are stripped; the resolver extracts the tenant/face and aliases App Engine versioned hosts to their canonical tenant host.
Path defeats traversal
Percent-encoding is validated and uppercased; only unreserved chars are decoded so %2E becomes . before dot resolution (catching encoded %2E%2E) while reserved %2F stays encoded (no segment injection); .. that escapes root is rejected, not clamped; double-encoded %252E stays literal (single decode pass).
Query is canonical and deterministic
Percent uppercased, ; normalized to &, pairs sorted by key then value, empty pairs dropped, the = distinction preserved, + left literal, length capped — a fixed point under re-normalization.
Reject is typed and CRLF-safe
On failure Wrap returns a 400 whose body carries the typed reject code and whose X-Reject-Reason header is that code with CR/LF stripped (no response splitting); the inner handler is never reached. All three normalizers are idempotent.

Methods

Host / Path / Query
The three canonicalizers, each returning a typed result or a typed rejection; all idempotent.
Resolver.Wrap
The middleware that runs all three, rejects with a typed code, and lands HostInfo in context.
NewResolver
Build the resolver from the base domain and the declared faces/tenants.
InfoFrom / WithHostInfo / TenantFrom
Read and write the parsed HostInfo on the request context.

A request through Wrap

Canonicalize, reject or pass with HostInfo.

  1. Host Normalize + validate the host; extract tenant/face (resolver), aliasing App Engine versioned hosts.
  2. Path Validate percent-encoding, decode unreserved, resolve dots, reject traversal/over-length.
  3. Query Uppercase percent, normalize separators, sort pairs, cap length.
  4. Reject or pass On any failure, 400 + typed reject code + CRLF-safe reason, inner handler skipped; on success, put HostInfo in context for TenantGuard and site dispatch.

Typed, CRLF-safe rejection

Bad host rejected
Empty, non-ASCII, control, leading/consecutive dots, bad port → BAD_HOST.
Traversal rejected
A .. (or encoded %2E%2E) escaping root → PATH_TRAVERSAL; malformed percent → BAD_ENCODING.
Bad query rejected
Over-length or control-bearing query → BAD_QUERY.
Reason is CRLF-stripped
The X-Reject-Reason header has CR/LF removed so an attacker-influenced code can't split the response.

System fit

  • normalize sits early in the global pipeline (Armor → Bastion → Admission → normalize → telescope), so routing sees canonical input + a HostInfo in context.
  • auth.TenantGuard and appboot site dispatch read the HostInfo tenant/face to make isolation and routing decisions.
  • It imports only core and the standard library; it owns no routing or business logic.
  • It registers through ceremony as the Resolver Wirable, reporting the base domain and faces/tenants.

Tests

normalize_test.go
The exhaustive Host table (ports, dots, control, ZWJ, non-ASCII, IPv4/IPv6, leading/consecutive dots), Host/Path/Query idempotency, toLowerASCII vs stdlib + zero-alloc, the Query canonicalization table, and the end-to-end Wrap reject matrix with CRLF-sanitized reason (BUG-646).
path_test.go
Traversal attacks, percent-encoded %2E%2E traversal, %2F-stays-encoded, double-encoding single-pass, malformed-percent rejection, control-byte rejection, dot-segment semantics, and the length-cap boundary.
wrap_reject_class_hostile_test.go
That the Wrap reject code matches the failure class (bad host vs traversal vs bad query), the inner handler doesn't run, and the body carries the matching code.
host_test.go / appengine_test.go / hostile_test.go / contracts_test.go
Bracket/IPv6 host handling, App Engine versioned-host aliasing + tenant resolution, the resolver/face/reject-code JSON contracts, nil-handler/invalid-resolver guards, CRLF-strip, and concurrent race-freedom.

nonce

nonce

The CSRF defense: middleware that stops a malicious site from making a user's browser perform state-changing requests against this app, using signed double-submit tokens for logged-in sessions and origin checks for anonymous ones.

CSRF: you're logged into the app, you visit evil.com, which silently makes your browser POST with your cookies attached — the request looks authentic. The defense is to require proof the request originated from our own pages. nonce provides three layers, one per traffic shape: a session-bound double-submit token (logged-in), an Origin/Referer check (anonymous, no token), and a non-session double-submit where the browser fetches then echoes a token (anonymous forms).

Vocabulary

Token
a fixed-length value the server HMAC-signs and binds to the session and a time window.
Double-submit
the token is sent both as a cookie and as a header/field; they must match. A cross-site page can't read the cookie to copy into the header, so it can't forge the pair.
Origin check
comparing the request's Origin (or Referer) to our allowed origin; a cross-site request carries a different one.
MAC
the HMAC signature (via professor) proving the token is ours.
Signal
a typed abuse-telemetry event emitted on each rejection, feeding alerting/rate-limiting.

Core concepts

Three CSRF layers by traffic shape
Session Middleware (logged-in double-submit), OriginGuard (anonymous no-session origin check, e.g. the public lead POST), and PublicFormGuard (anonymous fetch-then-echo double-submit).
Tokens verify in constant time
verifyToken checks the double-submit (cookie==header) and signature/expiry; the comparison is constant-time over a fixed-width buffer so a wrong-length input doesn't short-circuit (BUG-709, no timing oracle), and BOTH cookie and header must be present.
Origin checking is the single source of truth + fails closed
checkOrigin prefers Origin, falls back to Referer, compares port-normalized/IPv6-safe (BUG-468/149/600), and when BOTH are absent fails closed (BUG-698) so a header-less or non-browser POST can't ride through.
Anonymous requests still get CSRF
Without a session there's no token to double-submit, so unsafe anonymous requests fall back to an Origin check; a missing Origin (BUG-367) or no configured domain (BUG-904) fails closed.
Every reject is classified
Each rejection emits a distinct typed signal (OriginMismatch / MissingCookie / MissingHeader / InvalidToken / TokenGenerationFailed) so abuse detection can tell an origin-spoofing wave from a missing-token wave.

Methods

Middleware
Session-bound CSRF: cookie on safe methods, origin + double-submit verify on unsafe, Origin-only fallback for anonymous.
OriginGuard
No-session origin check for anonymous state-changing endpoints; no HMAC secret needed.
PublicFormGuard.Issue / Middleware
Issue a host-scoped cookie + token, then verify the origin → cookie → header → token ladder, signaling each reject.
verifyToken / checkOrigin
The constant-time double-submit verifier and the port-normalized, fail-closed origin matcher.

An unsafe request, checked

Session vs anonymous CSRF enforcement.

  1. Classify Safe methods pass (and a session GET refreshes the cookie); unsafe methods are enforced.
  2. Origin Check Origin/Referer against the allowed origin, failing closed when both are absent.
  3. Double-submit (session/public-form) Require matching cookie + header and a valid HMAC/expiry; a single value or a mismatch is rejected.
  4. Reject or pass On failure, emit the branch-specific signal and return 403; otherwise continue to the handler.

Fail-closed CSRF

Cross-site/origin-less rejected
An unsafe request whose Origin/Referer doesn't match (or is absent when checks are configured) is a 403.
Token must be a matching pair
A missing cookie, missing header, mismatched values, or bad signature/expiry is rejected; a single value never suffices.
Anonymous fails closed
Anonymous unsafe requests with a missing Origin or no configured domain are rejected.
Each reject is signaled
A distinct typed signal per branch keeps abuse detection from going blind.

System fit

  • nonce composes after auth in the pipeline (the session middleware needs the session id).
  • It depends on professor (HMAC), core (headers, error/signal identities, token shape), and alert (signal payloads).
  • It owns no routing or business logic — only the CSRF decision and its cookie/token plumbing; appboot/handlers wire the allowed origins and secret.
  • It registers through ceremony.

Tests

nonce_test.go
The session Middleware ladder, origin/Referer validation incl. port normalization and IPv6 (BUG-600), the BUG-698 absent-header fail-closed, verifyToken hostile table + both-required (BUG-709), and the missing-cookie signal.
origin_guard_hostile_test.go
OriginGuard rejects cross-site and origin-less unsafe requests, allows matching/safe and a trusted origin set, and no-ops when unconfigured.
public_form_test.go / public_form_signal_hostile_test.go
The Issue + reject ladder (missing/mismatched cookie/header/origin) with Secure/SameSite cookie attributes, plus that each reject branch emits exactly one Warning signal with the branch-specific reason.
hex_macinput_test.go / regression / contracts / structural_security / csrf_session_flow
MAC input encoding, the bug ratchets (BUG-367/584/904), typed contracts, the no-secret-capture structural rule, and the end-to-end session CSRF flow.

mid

mid

The transaction-boundary middleware: it runs each HTTP handler inside a database transaction, committing when the handler succeeds and rolling back when it fails — and it buffers the response so a rollback never leaks a success the database never committed.

A handler that writes rows AND returns 200 has two things that can disagree: the commit and the HTTP response. If the commit fails after the 200 is sent, the client thinks it worked but nothing was saved. mid runs the handler inside an atom.Doer transaction, buffers the response instead of sending it immediately, commits on 2xx/3xx and rolls back on 4xx/5xx, and flushes the buffered response only if the commit succeeded — so the client never sees an uncommitted success.

Vocabulary

Transaction
a group of database writes that all commit or all roll back together.
atom.Doer
the transaction runner: Do(ctx, fn) begins a tx, runs fn, commits if it returns nil, rolls back if it errors.
Transactor / Wrap
the middleware; Wrap(next) returns a handler that runs next inside a transaction.
Buffered writer
captures the handler's status/headers/body in memory so they can be suppressed on failure.
Passthrough
when no Doer is configured, Wrap returns the handler unchanged (no transaction).

Core concepts

Response tied to commit outcome
Commit on 2xx/3xx (a redirect reaches the client), roll back on 4xx/5xx (ErrMidRollback), 1xx is not an error (BUG-127); a handler 2xx whose commit then fails is suppressed into a 500.
Buffer so nothing leaks
The response is buffered and flushed only on success (BUG-060), so a commit failure never ships the handler's success body; the buffer is capped (BUG-164) to prevent OOM, with over-cap writes dropped but the full count reported (BUG-421).
No flush bypass
bufWriter does not implement Unwrap/Flush (BUG-682), so a handler calling http.NewResponseController(w).Flush() gets ErrNotSupported and cannot push bytes before commit; deadline setters are forwarded since they don't bypass buffering.
Panic rolls back, never re-panics
A handler panic is recovered inside the Do closure (so the tx rolls back), then a 500 is written; the payload travels as a typed panicError (BUG-665) unwrapping to ErrMidPanicked, and the buffered body + Set-Cookie are discarded.
Commit-failure response hygiene
On commit failure the 500 strips body-describing headers (BUG-459), suppresses Set-Cookie so a rolled-back session can't leak (BUG-685), flushes the remaining safe headers (BUG-171), and returns a no-store JSON body (BUG-199).

Methods

New / Wrap
Build the Transactor; Wrap runs next inside a transaction, or returns it unchanged (passthrough) when no Doer is wired.
runTx
Run the handler in the Doer closure, mapping status to commit/rollback and recovering panics.
bufWriter
The buffered ResponseWriter: capped body, no Unwrap/Flush, deadline forwarding.
writeCommitFailure / writePanicFailure
Emit the clean 500 on commit failure or panic, with header/cookie hygiene.

One request inside a transaction

Buffer, run, decide, flush-or-suppress.

  1. Begin + buffer Open the transaction and give the handler a buffered writer instead of the real connection.
  2. Run Execute the handler, recovering any panic into a typed error so the tx rolls back.
  3. Decide 2xx/3xx → commit; 4xx/5xx → roll back; 1xx → not an error.
  4. Flush or suppress On a successful commit, flush the buffered response; on rollback/commit-failure/panic, write a clean 500 with no leaked success or cookie.

Fail-closed, leak-free

Rollback on error status
A 4xx/5xx handler response rolls the transaction back; the handler's status is forwarded verbatim.
No uncommitted success
A commit failure suppresses the buffered 2xx and returns a 500 commit-failed body.
No leaked cookie
Set-Cookie is suppressed on commit failure and discarded on panic, so rolled-back session state can't reach the client.
Panic becomes a 500
A handler panic rolls back and returns a typed 500, never re-panicking past the HTTP boundary.

System fit

  • mid is global middleware mounted around mutating handlers.
  • It depends on store/atom (the transaction runner), core (error/body identities, limits), and ceremony.
  • It owns no business logic — handlers do the work; mid ties that work's persistence to the HTTP response.
  • It registers through ceremony as the Transactor Wirable (callee store), reporting Active when a Doer is wired and NotConfigured (passthrough) when not.

Tests

mid_test.go
Commit/rollback by status (incl. BUG-127 1xx/4xx), BUG-060 commit-failure-never-leaks-200, panic recovery (string/non-string/empty/nil), body-cap truncation, BUG-171/199/459 header hygiene, BUG-685 Set-Cookie suppression, BUG-665 panic-text + typed sentinel, BUG-682 deadline forwarding, and BufWriter_NoUnwrap.
flush_bypass_hostile_test.go
The behavioural half of BUG-682: an inner ResponseController.Flush() returns ErrNotSupported, and a handler that tries to flush its success can't leak it past a commit failure (still a 500 commit-failed body).
hardening_test.go
Panic discards buffered body + Set-Cookie, success-body-must-not-flush-when-Do-errors, redirect reaches client, and raw-header-insertion edges.
regression_test.go / contracts_test.go
The bug ratchets, the txResult/panicError/bufWriter Validate contracts, the ceremony state/details/callees, and the panicError→ErrMidPanicked unwrap.

local

local

The per-route middleware presets: four named stacks (Bare, Public, Optional, Required) that compose the auth, encoding, and CSRF-nonce middleware in the correct order for each kind of route — so a route picks a preset instead of hand-assembling (and mis-ordering) its own middleware.

Different routes need different middleware in a specific order: infra needs only encoding; a public page needs encoding + a CSRF nonce; a mixed-access page needs optional auth first then encoding + nonce; a protected endpoint needs required auth first. Hand-wiring this per route is how you get a protected route with auth after encoding, or a nonce stamped before the session is known. local names the four correct stacks once. It is a transport-layer package: it does NOT import auth or nonce — the composition root constructs each middleware and passes the transport.MidFunc values in, so local owns the order and the categories, not the implementations.

Vocabulary

MidFunc
a middleware, func(http.Handler) http.Handler.
Preset
a named, ordered middleware stack (Bare/Public/Optional/Required).
Slot
one of the four wired middlewares: AuthOptional, AuthRequired, Encoding, Nonce.
Config → Pipeline
Config holds the four MidFuncs; New(Config) validates them and returns a Pipeline whose methods produce the presets.
transport.Chain
composes a slice of MidFuncs around a handler outermost-first — the first in the slice runs first.

Core concepts

Presets encode the correct order
Bare=encoding; Public=encoding→nonce; Optional=authOptional→encoding→nonce; Required=authRequired→encoding→nonce. Auth runs outermost (decides identity first), then encoding, then the session-aware nonce.
Optional vs Required use different gates
Optional uses the optional auth gate and Required uses the required gate; swapping them would let an unauthenticated request reach a protected route, so the distinction is pinned by tests.
Dependency inversion
local imports neither auth nor nonce; the god layer builds those middlewares (with their secrets) and passes MidFunc values into New, keeping the transport layer free of auth/CSRF dependencies.
Fail-closed wiring
Every Config slot is required; a nil slot fails New with ErrLocalNilMiddleware (named) at boot, not at request time, and a preset on an unvalidated zero-value Pipeline panics rather than serving an unprotected route.
State-driven ceremony reporting
A fully-wired Pipeline reports Active + callees [auth, encoding, nonce, transport] + four Active sub-items; the zero value reports NotConfigured + nil callees + four NotConfigured sub-items, so the boot manifest shows whether the presets are live.

Methods

New / Config.Validate
Build the Pipeline, requiring all four MidFunc slots non-nil (fail at boot).
Bare / Public / Optional / Required
The four presets, each returning a MidFunc that Chains the inner handler in the correct order.
Pipeline.Validate
Pre-flight that every slot is wired before applying a preset.
Describe / CeremonySubItems
The ceremony Wirable/SubItemer reporting, state-driven per wiring.

Applying a preset to a route

Construct once, choose per route.

  1. Construct middlewares The composition root builds auth/encoding/nonce middleware with their config.
  2. New(Config) Pass the four MidFuncs to New, which validates every slot is non-nil and returns a Pipeline.
  3. Pick a preset A route registrar applies Bare/Public/Optional/Required as its middleware.
  4. Serve in order The preset Chains the handler so the stack runs auth → encoding → nonce → handler (per preset).

Fail-closed at boot

Nil slot rejected
A nil AuthOptional/AuthRequired/Encoding/Nonce fails New with ErrLocalNilMiddleware naming the slot.
Unvalidated pipeline panics
Calling a preset on a zero-value/unwired Pipeline panics before mutating the handler, rather than serving it unprotected.
Order is fixed
Presets always compose auth-outermost → encoding → nonce; the Optional/Required gate is never swapped.
Unwired reports not-configured
The ceremony surface reports NotConfigured + nil callees when not fully wired, never a phantom Active.

System fit

  • local sits in the transport layer between switchboard (route mounting) and the middleware implementations.
  • The composition root constructs auth/encoding/nonce and hands them to local.New; registrars apply the presets per route.
  • It imports only core, ceremony, and transport — not auth or nonce.
  • It registers through ceremony as the Pipeline Wirable (callees auth/encoding/nonce/transport when active).

Tests

local_test.go
TestPreset_Order (exact execution order of all four presets, distinguishing authOptional vs authRequired), determinism across invocations, concurrent requests not sharing order, nil-config rejection, full config, and zero-value-is-unsafe.
hostile_test.go
Every nil slot rejected with typed identity, invalid pipeline panics before handler mutation, config slots copied by value, concurrent preset selection race-freedom, enum/label contracts, Bare-wraps-exactly-one, auth-before-handler, encoding-before-nonce, and the Required hot-path alloc budget.
ceremony_reporting_hostile_test.go
The ceremony surface in both states: fully-wired reports Active + four callees + four Active sub-items; zero value / Describe() reports NotConfigured + nil callees + four NotConfigured sub-items.
contracts_test.go
Error-identity distinctness, format-verb preservation, and the enum contracts.

global

global

The outer middleware pipeline: the seven middlewares that wrap every route in one frozen order — cyclops → horizon → armor → bastion → gate → normalize → telescope — so tracing, metrics, security headers, abuse blocking, capacity, tenant resolution, and access logging apply uniformly and in the right sequence to all traffic.

Unlike per-route middleware (local), global is applied once around the whole router, so it runs for every request. The order is the contract and it's frozen in code — an explicit chain, not a loop over a reorderable slice — built outermost-first. Each position is load-bearing: cyclops is outermost so its panic recovery + trace ID cover everything; armor sets headers before bastion spends effort; bastion (IP block + rate limit) runs before gate so abuse is dropped before it consumes a capacity slot; normalize resolves the tenant before telescope logs it.

Vocabulary

MidFunc
a middleware, func(http.Handler) http.Handler.
Slot
one of the seven named middlewares: Cyclops, Horizon, Armor, Bastion, Gate, Normalize, Telescope.
Config → Pipeline
Config holds the seven MidFuncs; New(Config) validates them and returns a Pipeline whose Wrap applies them.
Outermost-first
the chain is built so the first slot runs first and is the outermost wrapper.

Core concepts

One pipeline, every route, frozen order
The seven middlewares wrap the whole router. Wrap builds the chain with explicit lines (no loop, no slice), so the canonical order cyclops→horizon→armor→bastion→gate→normalize→telescope is visible and frozen in source.
Order is load-bearing
cyclops outermost (covers all panics/traces), armor before bastion, bastion before gate (drop abuse before spending a capacity slot), normalize before telescope (log the resolved tenant). A wrong order loses the tenant in the log or wastes admission on a flood.
Encoding is deliberately not a slot
Content-encoding negotiation isn't one of the seven — only the compiled-blob handlers consume it and exactly one handler runs per request, so they negotiate inline rather than paying a per-request marker on every route. The pipeline has exactly seven slots (GlobalMiddlewareCount).
Fail-closed construction
Every Config slot is required; New rejects a nil slot with ErrGlobalNilMiddleware (named) at boot, and Wrap panics on a zero-value Pipeline rather than serving with a gap in the chain.
Dependency inversion
global imports none of the telemetry/security packages; the composition root (main.go) builds each middleware and passes the MidFunc values in, so global owns the order, not the implementations.

Methods

New / Config.Validate
Build the Pipeline, requiring all seven MidFunc slots non-nil (fail at boot).
Wrap
Apply the seven middlewares in frozen canonical order; panics on an unwired pipeline.
Pipeline.Validate
Pre-flight that every slot is wired.
CeremonyCallees / CeremonyDetails
Report the dependency edges and the canonical-order string for the boot manifest.

Wrapping the router

Construct once, applied to all traffic.

  1. Construct middlewares main.go builds cyclops/horizon/armor/bastion/gate/normalize/telescope with their config.
  2. New(Config) Pass the seven MidFuncs to New, which validates every slot is non-nil.
  3. Wrap the router Pipeline.Wrap chains the seven around the top-level router, outermost-first.
  4. Serve every request Each request flows cyclops → … → telescope → router → handler, then unwinds.

Fail-closed at boot

Nil slot rejected
Any nil of the seven slots fails New with ErrGlobalNilMiddleware naming the slot.
Unwired Wrap panics
Calling Wrap on a zero-value Pipeline panics (wrapping the sentinel) before mutating the handler, rather than serving with a gap.
Order is frozen
Wrap always executes cyclops→…→telescope; the order is in code, not caller-supplied data.
Manifest matches runtime
The published CeremonyDetails order string equals the observed Wrap execution order.

System fit

  • global.Wrap wraps the top-level router — the outermost layer every request passes through before route middleware (local) and handlers.
  • The composition root constructs the seven middlewares and hands them to global.New.
  • It imports only core, ceremony, and transport — not the telemetry/security packages.
  • It registers through ceremony as the Pipeline Wirable (callees wall/normalize/cyclops/telescope/horizon), always Active.

Tests

global_test.go
TestWrap_Order (the full seven-step execution order cyclops→…→telescope→handler), nil-middleware rejection, all-valid construction, ceremony details, and handler-called.
hostile_test.go
Every nil slot rejected with typed identity, zero-value Wrap panic before handler mutation (wrapping the sentinel), config copied by value, concurrent Wrap race-freedom, cyclops-outermost/telescope-innermost, slots-match-execution-order + defensive copy, count-matches-enum, exact callees membership + fresh-slice, the auxiliary flags (state Active even for zero value), details-encodes-canonical-order, empty-on-invalid, and next-handler-identity preservation.
details_matches_execution_hostile_test.go
Derives the order from the published CeremonyDetails string and asserts it equals the observed Wrap execution, so the manifest can't claim an order the server doesn't run.
alloc_test.go / contracts_test.go
The sampled/unsampled per-request alloc budget, and the error-identity / format-verb / slot / ceremony contracts.

encoding

encoding

The content-encoding negotiator: given a request's Accept-Encoding, it works out which compressed variants (gzip / brotli / zstd) the client can read, then picks the smallest precompiled variant of an asset the client will accept — so every response is the smallest representation that client can decode.

Assets are compressed offline into several variants (raw, gzip, brotli, zstd). At request time encoding parses what the client accepts (honoring q=0 rejection and the Safari zstd exclusion), then serves the variant with the fewest bytes among the ones the client accepts AND that have content. Because compression is offline, the smallest-per-accept-set answer is known at compile time, so selection is a precomputed table lookup, not a per-request scan.

Vocabulary

AcceptSet
a 3-bit set (gzip/br/zstd flags) of what a request accepts; the zero value means identity-only.
BlobEnc
an index into a compiled asset's variants (Raw/Gzip/Br/Zstd) plus the None sentinel (no acceptable variant has content).
Negotiation
turning Accept-Encoding + User-Agent into an AcceptSet.
PickTable
a precomputed array: for each AcceptSet, the smallest variant that set can read.
ETag / If-None-Match
cache revalidation — if the client's stored tag matches, the server replies 304.

Core concepts

q=0 is rejection, not preference
gzip;q=0 means 'not acceptable' and does not set the flag. Parsed carefully: q=0/0.0/0.000, case-insensitive Q=0, and proxy suffixes (q=0;maxage=10) all reject; q=0.1/q=1 and lookalikes (freq=0/req=0) do not. Token boundaries stop 'br' matching inside 'zebra'.
Safari zstd exclusion
Safari advertises zstd but has a known streaming-decoder bug, so zstd is dropped for Safari (gzip/br unaffected); the User-Agent is only scanned when zstd is actually offered.
Smallest-accepted selection
SmallestAccepted returns the fewest-byte variant among the identity and every accepted variant with content; ties prefer the earlier entry (identity first, then the cheaper decoder). PickTable precomputes this per accept-set and ValidateFor rejects a drifted hand-edited table.
406-vs-500 discrimination (BUG-710)
A None result means no acceptable variant has bytes — two failures: AnyVariant true → the asset has bytes the client refuses → 406; AnyVariant false → the asset is empty in every variant (broken build) → 500. Getting it wrong masks a broken asset as a client problem or vice-versa.
Safe caching + slicing
ETagMatch parses tokens (BUG-794) so a longer/prefixed tag can't false-match by overlap, handling W/ weak prefix and lists with zero allocations; ValidBlobSlice uses subtraction (BUG-132) so an off+ln that overflows int can't wrap negative past the bounds check.

Methods

NegotiateAcceptSet / AcceptSetFromRequest
Parse Accept-Encoding (+ UA) into an AcceptSet; zero-alloc for the universal single-line header, reads the wire header directly.
SmallestAccepted / BuildPickTable / PickTable.For
Select the smallest accepted variant; precompute and look up the selection per accept-set.
AnyVariant
The 406-vs-500 discriminator for a None selection.
ETagMatch / ValidBlobSlice
Token-parsing cache revalidation and overflow-safe blob slicing.

Negotiating one response

Parse, select, serve the smallest.

  1. Negotiate AcceptSetFromRequest parses Accept-Encoding (+ UA) into an AcceptSet, honoring q=0 and the Safari zstd exclusion.
  2. Revalidate ETagMatch on If-None-Match; a match replies 304 without a body.
  3. Select PickTable.For (or SmallestAccepted) returns the smallest variant the set can read, or None.
  4. Serve or error Serve the variant with its Content-Encoding + Vary; on None, AnyVariant decides 406 vs 500.

Fail-closed negotiation

Unknown/refused encodings dropped
An unknown token or q=0 encoding never enters the AcceptSet; JSON parsing rejects the whole value on any unknown token.
Never serve unaccepted
SmallestAccepted only returns a variant the set accepts (and that has content).
406 vs 500 stays distinct
AnyVariant separates 'client refuses available bytes' (406) from 'asset empty everywhere' (500).
Overflow-safe slicing
ValidBlobSlice rejects nil/negative/out-of-range and overflow-crafted off+ln.

System fit

  • encoding is NOT a global pipeline slot: only the compiled-blob handlers (html, ballast) negotiate, and exactly one runs per request, so they call AcceptSetFromRequest inline.
  • It imports core, ua (Safari detection), and ceremony; it owns the BlobEnc/ETag/slice machinery shared by the blob handlers.
  • PickTables are built offline by cmd/compile and pinned by the generated-data consistency ratchets via ValidateFor.
  • It registers through ceremony as the stateless Negotiator Wirable (always Active, no callees).

Tests

acceptset_test.go
The full NegotiateAcceptSet table (token boundaries, the exhaustive q=0 grammar incl. case-folds/proxy-suffixes/freq=0 lookalikes, Safari zstd exclusion), AcceptSetFromRequest multi-line join, SmallestAccepted selection buckets + never-serves-unaccepted sweep, Has/AnyVariant, zero-alloc, concurrency, and a fuzz target.
none_discriminator_hostile_test.go
The BUG-710 contract as a unit: each scenario where SmallestAccepted returns None, paired with AnyVariant, yields the correct 406 (client refuses available bytes) vs 500 (asset empty everywhere) status.
blobenc_boundary_test.go
ETagMatch protocol buckets (exact/wildcard/list/weak + the BUG-794 overlap and prefix attacks), zero-alloc, and the BUG-132 ValidBlobSlice bounds incl. the off+ln overflow attack.
wire_test.go / picktable_test.go / contracts_test.go
The AcceptSet/BlobEnc String/JSON round-trips and fail-closed marshal, the PickTable validate/drift/every-row/For contracts, and the error identities.

emailrender

emailrender

Turns typed message structs into the final HTML (or plain text) for the three kinds of message this system sends — a transactional email, an internal admin/lead notification, and an SMS — escaping every piece of dynamic content so untrusted input can never inject markup.

Email HTML is hostile: clients strip <style> and external CSS and only honor inline styles on table layouts, so the templates are hand-built strings with inline style on every element. The dangerous part is that the content (brand name, username, reset link, a lead's submitted email) is untrusted and must be escaped before it enters the HTML — or an attacker delivers <script> or breaks out of an attribute into an event handler, straight to an inbox. The single safety job: every dynamic value passes through html.EscapeString before it is written.

Vocabulary

Transactional email (Message)
the branded card sent to a user (magic link, code, welcome), with optional highlight/steps/action/signature/preheader sections.
Admin/lead email (AdminMessage)
a light-themed card listing labeled fields, sent internally when (e.g.) a lead form is submitted.
SMS (SMSMessage)
either a verification code wrapped in the standard copy, or a plain subject line.
Preheader
the hidden preview text email clients show next to the subject.
Escape
html.EscapeString turns < > & ' " into entities; escaping the quote is what stops attribute breakout.

Core concepts

Escape text, validate URLs
Text fields are written via html.EscapeString (escaping <> stops <script>); no code path writes a dynamic text value raw. URL fields (action/highlight/signature-image/admin) are validated and rejected instead — markup, attribute-breakout, javascript:, scheme-less, userinfo, and control-char URLs fail Validate before any HTML is built.
Validate before building
Each Render* calls Validate first and returns core.ErrInvalidInput before assembling anything, so a malformed message never produces partial output.
All-or-nothing action
An Action's label and URL are both set (button renders) or both empty (omitted); a half-action is rejected, so there's never a button with a missing href or label.
Bounded admin fields
AdminMessage requires 1..core.EmailRenderMaxAdminFields fields, so one notification can't balloon the email; each field's label and value are required.
Optional sections omit cleanly
Preheader, highlight (and its link form), steps, action button, and each signature piece render only when their inputs are present; a minimal valid Message is well-formed with no empty scaffolding.

Methods

RenderMessage
Validate + assemble the dark branded transactional card.
RenderAdmin
Validate + assemble the light admin/lead field-list card.
RenderSMS
Validate + emit the standard code copy, or the subject verbatim when there's no code.
Validate (Message/AdminMessage/SMSMessage/Action/Step/AdminField)
The fail-closed input contracts each Render* runs first.

Rendering one message

Validate, escape, assemble.

  1. Validate Reject a malformed struct with ErrInvalidInput before any HTML is built.
  2. Open the document Write the doctype + body with the theme's inline style.
  3. Write sections Emit header/hero/steps/action/signature/footer (or admin fields), escaping every dynamic value and skipping empty optional blocks.
  4. Return the string Hand the assembled HTML (or, for SMS, the plain text) back to the sender.

Fail-closed rendering

Malformed rejected
Missing required copy, an out-of-range StepCount, a half-action, or an empty admin field list returns ErrInvalidInput before any output.
No raw markup
Every dynamic value is escaped, so untrusted input can't inject <script> or break out of an attribute.
Bounded size
Admin fields are capped at MaxAdminFields.
No partial output
Validation runs before building, so a rejected message yields an empty string + error, never half an email.

System fit

  • emailrender is a pure rendering library: structs in, string out, no I/O or network.
  • The mail-sending layer (outbox / bridge email sender) builds the typed message and calls the right Render* for the body.
  • It imports only the standard library's html and strings, plus core (copy constants + ErrInvalidInput).
  • It owns no transport or scheduling.

Tests

emailrender_test.go
A representative escaping check, the empty-admin and half-action rejections, and the SMS code/empty paths.
escaping_hostile_test.go
The per-field escaping matrix (every Message/AdminMessage text field independently mutated to an XSS payload and proven escaped), the URL-rejection matrix (every href/src field rejects markup/attribute-breakout/javascript:/scheme-less/userinfo/control URLs with ErrInvalidInput), the full Message/AdminMessage validation sweep (incl. the MaxAdminFields ceiling and exact-limit acceptance), the SMS code/subject/both/empty paths, and optional-section omission for a minimal message.

identity

identity

The ID generator: it hands out monotonic ULIDs (the primary, sortable identifier for rows and events) and UUID v7 values, safe to call from many goroutines at once.

A ULID is a 128-bit ID whose text form sorts in time order (48-bit millisecond timestamp + random), so it makes a great primary key AND pagination cursor. Within the same millisecond, plain random ULIDs could come out unordered or collide; this generator is monotonic — each ULID is strictly greater than the previous one — so the sort order is always correct and two IDs never collide. That strict ordering is the property everything downstream relies on.

Vocabulary

ULID
a 128-bit lexicographically-sortable ID: a 48-bit millisecond timestamp followed by random bits, so it sorts by creation time.
Monotonic
each ULID is strictly greater than the previous one, even within one millisecond, so ordering is correct and IDs never collide.
Entropy
the random part of a ULID; here a monotonic source that increments within a millisecond rather than drawing fresh randomness.
lastMS
the millisecond of the most recent ULID, remembered so the next one can never go backwards.
Clock regression
the wall clock jumping backwards (NTP step, VM migration, leap second); the generator must survive it without an out-of-order ID.

Core concepts

Monotonic by three forces
ULID() computes the millisecond as max(now, lastMS) so the timestamp can't regress; the monotonic entropy strictly increments within a millisecond; and entropy exhaustion resets the source and bumps the millisecond above lastMS (BUG-176). Break any one and you get out-of-order or colliding sortable keys.
Clock-regression safe
Even if the wall clock jumps backwards every call, max(now, lastMS) keeps each ULID strictly increasing and its embedded timestamp from regressing — pagination cursors and unique keys stay correct.
Concurrent and zero-alloc
The hot path is mutex-guarded (callers serialize) and targets zero allocations; concurrent callers all get unique, ordered IDs.
Fail-closed construction
New() is backed by crypto/rand with monotonic ordering and panics at boot if it validates as incomplete; Validate and the production methods reject a nil receiver / nil entropy / nil clock with a typed ErrInvalidInput.
UUID v7 + parse
V7() returns a time-ordered UUID v7 (never the zero value); Parse() decodes a 26-char Crockford-base32 ULID (case-insensitive) into the core ULID type, wrapping ErrIdentityParse on malformed input.

Methods

Generator.ULID
The monotonic, concurrency-safe, zero-alloc hot path.
Generator.V7
A time-ordered UUID v7, validated at entry.
Parse
Decode a textual ULID into the core ULID type, wrapping ErrIdentityParse.
New / Validate
Construct a crypto/rand-backed generator (panic at boot if incomplete); reject nil entropy/clock.

Issuing one ULID

Clamp the clock, increment entropy, never regress.

  1. Lock Take the mutex so concurrent callers serialize.
  2. Clamp the millisecond ms = max(ulid.Timestamp(now), lastMS) — never below the last ID.
  3. Draw monotonic entropy Generate at ms; within a millisecond the random part strictly increments.
  4. Recover + record On entropy exhaustion, reset and bump ms above lastMS; store the new lastMS and return.

Ordered or it errors

Never regresses
max(now, lastMS) keeps timestamps from going backwards even under a backwards clock.
Never collides
Every mint pairs a fresh entropy draw with one wall observation, so identities stay unique; ordering inside one millisecond is entropy, by design.
Nil receiver rejected
A nil receiver fails Validate (and every production method at entry) with ErrInvalidInput; the generator itself is stateless.
Bad input rejected
Parse wraps ErrIdentityParse on a wrong-length or non-Crockford string.

System fit

  • Nearly every package that creates a row, event, or correlation ID depends on identity.
  • It imports core (the ULID/ID vocabulary + error identities, riding the Primitive id, keygen, and temporal doors) and ceremony.
  • It owns no storage or transport — only ID generation and parsing.
  • It registers through ceremony as the Generator Wirable (always Active, no callees).

Tests

identity_test.go
10k-ID uniqueness on the real clock, concurrent generation, uniqueness across generators, the full Parse table (valid/invalid/boundary lengths/Crockford lowercase/non-alphabet collapse), and the V7 round-trip/never-zero/unique/concurrent contracts.
clock_regression_hostile_test.go
Monotonicity when the wall clock jumps backwards every call (the max(now, lastMS) clamp keeps IDs strictly increasing and timestamps from regressing), and under a frozen clock (every ID in one millisecond, stressing the monotonic-entropy path for strict ordering and no duplicates).
hostile_test.go / contracts_test.go / regression_test.go
Nil-receiver/entropy/clock validation at every entry, race-freedom of validate/ceremony/generation under load, the CeremonyDetails core-contract rendering, and BUG-176 (entropy reset advances the timestamp).

keygen

keygen

Provisions the cryptographic secrets an LFW deployment needs (the ed25519 signing keypair, CSRF secret, pepper, HMAC key, encryption key) into the per-environment .env files — idempotently, filling what's missing without ever clobbering a secret that's already there.

Running keygen again preserves every existing non-empty value, because regenerating a live secret is a disaster: a new PEPPER_HEX invalidates every stored password hash, a new signing key breaks every issued token. Secrets are generated only when the slot is empty (or force is set), and each file is rewritten atomically (temp-file then rename) so a crash can't leave a truncated .env.

Vocabulary

Secret
one managed value: a Key (env name like PEPPER_HEX), its Value, and an optional Linked companion (the ed25519 pair).
Pepper
a secret mixed into password hashing before the per-user salt; NOT stored with the hash, so the same pepper must be present wherever those hashes are verified.
ed25519 keypair
the signing (private) and verify (public) keys; a matched pair — a mismatched half can't verify what the other signed.
force
regenerate even non-empty values (key rotation).
Result
what happened to one secret: Generated (new value written) or Skipped (existing preserved).

Core concepts

Idempotent, never clobbers
An empty slot is filled (Generated); a non-empty slot is preserved (Skipped) unless force. Regenerating a live pepper or signing key would invalidate stored hashes / issued tokens, so the default never touches existing secrets.
Atomic writes
Each file is rewritten via temp-file → chmod → write → sync → close → rename, removed on error, so a crash mid-write can't leave a half-written .env with a truncated secret.
Linked ed25519 derivation
If the private key exists but the public is empty, the public is re-derived from the private rather than minting a new pair; a private key that can't be decoded/used returns ErrKeygenCorruptedKey instead of silently leaving a mismatched sign/verify pair.
Stored-secret reconciliation
EnsureAll forces every env file sharing a FIRESTORE_DATABASE_ID to share one PEPPER_HEX and HMAC_KEY_HEX (each canonical = first NON-EMPTY in dev→stage→prod order); divergence would break password verification or persisted device identity. CSRF/ENC/ed25519 stay per-file.
Locked + deterministic
EnsureAll holds an O_EXCL lock for the whole pass (exactly one concurrent caller wins) and the reconciliation is deterministic + idempotent — a second run rewrites nothing.

Methods

Secrets
Mint the six managed secrets (ed25519 pair + CSRF/pepper/HMAC/ENC hex), zeroizing the bytes after use.
EnsureFile
Idempotently provision one env file, atomically, returning a Result per secret.
EnsureAll
Provision dev/stage/prod under a lock, then reconcile shared-database peppers and HMAC keys.
setEnvValue / envValue
Parse/rewrite KEY=value lines (skip comments, preserve indentation, append if missing).

Provisioning the env files

Fill the gaps, then reconcile stored-data secrets.

  1. Generate Secrets() asks Primitive Keygen to construct the Ed25519 pair and four bounded 32-byte secrets, projects them to base64/hex, destroys Primitive custody, and zeroizes projected bytes after use.
  2. EnsureFile per file Fill empty slots, preserve non-empty ones, re-derive a linked public key, write atomically only if changed.
  3. Reconcile stored secrets Group files by database id and rewrite each group to its canonical pepper and HMAC key.
  4. Release Drop the O_EXCL lock; report a Result per secret.

Fail-loud provisioning

Corrupt key rejected
An undecodable/unusable private key returns ErrKeygenCorruptedKey rather than a silent mismatched pair.
Primitive entropy failure surfaced
Primitive secrets are generated eagerly so typed CSPRNG failure aborts before any partial write.
Read/write failures wrapped
Provisioning read/write and atomic-write step failures wrap ErrKeygenProvision with the path.
Concurrent callers serialized
The O_EXCL lock means exactly one EnsureAll runs; the rest fail fast rather than racing the shared files.

System fit

  • keygen is run by the provisioning tooling (cmd/lfw) at setup/rotation time, not on the request path.
  • It composes Primitive Keygen for construction/custody, Primitive Filestore for physical writes, Kernel core for product policy, and professor only for persisted-key validation/re-derivation.
  • It owns the .env files it writes and nothing else.
  • It registers through ceremony as the Descriptor Wirable (callee professor, always Active).

Tests

keygen_test.go
Idempotent preservation of an existing value, force regeneration, all-keys-present-no-force all-skipped, the setEnvValue table (incl. whitespace), atomic write + rollback on rename failure, secret zeroization, and the EnsureAll file lock acquire/release.
keygen_hostile_test.go
Fail-fast on a corrupted private base64, orphan public-key rotation + re-derivation, duplicate-key first-match, concurrent EnsureAll (exactly one wins) + lock release on failure, atomic full-replacement + non-existent-dir failure, and the pepper reconciliation (shared-DB shares dev's canonical, distinct-DB keeps its own, idempotent second pass, reported rewrites, empty-DB-id no-merge, fresh-provisioning per-file peppers).
pepper_canonical_hostile_test.go
The canonical-selection order when an earlier file's pepper is empty (falls through to the first non-empty, not the first file), and three-way shared-DB convergence to dev's pepper/HMAC while CSRF/ENC stay per-file.
primitive_projection_hostile_test.go
Exact base64/hex projection, typed Primitive/Kernel failure identities, missing and failing generators, partial-result destruction, and the Kernel 32-byte policy inside Primitive's admitted interval.
regression_test.go
Primitive contract round trips and the bug ratchets.

ledgerwriter

ledgerwriter

The single append authority for the audit ledger: it stamps each entry with the next sequence number and a chain hash linking it to the previous entry, then persists it — so the ledger is a tamper-evident, gap-free, strictly-ordered record of everything that happened.

An audit ledger is an append-only log; to trust it you must be able to prove later that nobody edited, deleted, or reordered an entry. Each entry stores a chain hash = hash(previous chain hash + this entry's contents), so changing any past entry breaks every hash after it (detectable by replay). Two more invariants make it meaningful: a monotonic gap-free sequence (1,2,3,… no holes), and a single serialized append authority so two writers can't both claim one sequence. This package is that authority, plus the transactional path that records ledger truth atomically with a domain mutation.

Vocabulary

Entry
one audit record; carries a Seq, a ChainHash, and the event payload.
Chain hash
the per-entry hash linking it to its predecessor (stored as hex).
Rolling hash
the writer's in-memory copy of the latest chain hash, used as the previous input for the next entry.
Genesis hash
the fixed starting hash for entry 1 (an empty ledger).
Conflict (ErrConflict)
another writer/instance already took the sequence we tried; we must resync and retry.

Core concepts

Tamper-evident hash chain
Each entry's chain hash depends on the previous one, so editing, dropping, or reordering any entry breaks every hash after it; replay verification catches content tamper, hash tamper, truncation, reorder, and duplicate seq.
Monotonic, gap-free, single authority
The writer assigns Seq = lastSeq + 1 under a mutex held for the assign+persist critical section, so the sequence has no holes and two writers never claim the same number.
The hashing rule (BUG-697)
The chain hash is computed over the entry with its ChainHash field BLANK, then the hex hash is filled in; the verifier recomputes the same way, so chain_hash must stay out of the hashed input. Both the standalone and transactional paths use the same ledger.HashEntry, producing identical chains.
Rollback + bounded conflict retry
A store write failure rolls back the seq and rolling hash (no phantom advance); a conflict resyncs (reading entries newer than the lost seq first, BUG-744) and retries, bounded by LedgerwriterMaxConflictRetries — a persistent conflict returns ErrConflict rather than looping forever. The resync read runs outside the mutex (BUG-669/734).
Transactional write ordering
Strategy.WriteTx prepares the ledger append, runs the domain sinks, then commits the ledger entry; if any sink fails the entry is NOT committed, so the audit log never gains an entry for a mutation that didn't happen.

Methods

Writer.Record / New
The standalone append authority: seed from the store, then assign+persist with rollback and bounded conflict resync.
AppendTx / PrepareAppendTx / CommitPreparedAppendTx
Assign seq+chain hash inside a store transaction and record, for atomic ledger+domain writes.
Strategy.WriteTx
Prepare the append, fan out to domain sinks, then commit — aborting the commit if a sink fails.
Mutation.Validate
Enforce the two valid shapes: append-only (no aggregates) or sink-bound (valid resource+kind + matching aggregate).

Appending one entry

Refresh, assign, persist, or resync.

  1. Refresh Re-read the store tail first so stale boot reads / other instances don't strand the chain (BUG-732/742).
  2. Assign under lock Seq = lastSeq + 1; chain hash from the rolling hash over the blank-ChainHash entry; persist.
  3. Rollback or resync On store failure roll back seq+hash; on conflict resync from the store and retry (bounded).
  4. Commit (tx path) PrepareAppendTx → run sinks → CommitPreparedAppendTx; a sink failure aborts before the commit.

Ordered, gap-free, fail-closed

No phantom advance
A store write failure rolls back the seq and rolling hash.
Bounded conflict
Persistent ErrConflict returns after LedgerwriterMaxConflictRetries, never an infinite loop.
Bad chain hash rejected
A recovered chain hash that won't decode or is the wrong length fails with ErrLedgerwriterBadChainHash.
Truth not committed on sink failure
WriteTx never records the ledger entry if a domain sink fails.

System fit

  • ledgerwriter lives outside core because it needs a sync.Mutex; core is deterministic compute with no concurrency primitives (BUG-679 keeps sync out of core/ledger's canonical hashing).
  • It depends on core/core/ledger (the entry type + canonical hashing), store (persistence), and the domain aggregate types for the sinks.
  • The auth/account flows append through it; replay verification lives in core/ledger.
  • It registers through ceremony as the Writer Wirable (callee store) and satisfies store.LedgerRecorder.

Tests

chain_replay_test.go
Full-chain replay verification: faithful streams accept; content tamper, hash tamper, truncation (end + mid-stream), reorder, duplicate seq, and corrupt hash encoding all reject; empty-stream handling; and a concurrently-written chain still verifies.
ledgerwriter_test.go
Cold-start recovery, chain-hash population, BUG-697 hasher parity, BUG-732 dual-hash divergence, rollback on store failure, resync on conflict, and BUG-052 resync-failure propagation.
conflict_exhaustion_hostile_test.go
A persistent conflict terminates — Record returns ErrConflict after exactly LedgerwriterMaxConflictRetries store attempts (bounded, no infinite loop) — and Strategy.WriteTx aborts the ledger commit when a sink fails (the entry is never recorded).
strategy_test.go / contracts_test.go / boundary_buckets_test.go
The AppendTx genesis/monotonic/linkage/chain-hash-excluded/not-mutated contracts, the Mutation two-shape validation matrix, the enum Valid/IsValid/JSON sweeps, and the error identities.

store

store

Defines the persistence contract — the repository interfaces (ports) every backend must implement — plus the request-scoped plumbing that hands the right backend to each request (including tenant isolation) and a few shared helpers (verified family CAS, ceremony reporting, error sanitization).

This package is almost entirely interfaces. StoreBackend composes the per-aggregate repositories (user/family/credential/ledger/outbox/admin) plus the transaction primitives (atom.Doer for single-aggregate, WriteTx for multi-aggregate atomicity); concrete implementations live in store/sql, store/firestore, store/dual, store/atom. Code depends on the interface here so it can run against any backend. Tx is the multi-aggregate handle inside WriteTx — the same methods minus Do/WriteTx, so transactions can't nest.

Vocabulary

Backend (StoreBackend)
the full persistence dependency for a request.
Port
a repository interface — the contract a backend implements.
Tenant
an isolated customer namespace; each request resolves to one and its store must only see that tenant's data.
CAS (compare-and-swap)
an optimistic-concurrency update — write only if the version is still the one I read — preventing lost updates.
Descriptor
the ceremony view of the store's runtime health.

Core concepts

Ports, not implementations
Depend on the interfaces here; the concrete SQL/Firestore/dual/atom backends implement them in subpackages. Tx excludes Do/WriteTx so a transaction can't nest inside itself.
Request-scoped backend
The backend (and admin-stats store) ride the request context via a private zero-size key type; WithBackend/BackendFrom are nil-safe and wrong-type-safe, never panicking.
Tenant isolation fails closed
TenantMiddleware reads the tenant from normalize.HostInfo (must sit after normalize); an empty tenant passes through, but a nil scoped backend returns 500 rather than falling through to the unscoped backend — which would be a cross-tenant leak. Context is mutated in place (BUG-606).
Verified family CAS routing
CommitVerifiedFamilyCAS picks the strongest path a Tx supports: VerifiedFamilyCASRepository.CASUpdateVerified (read-before-write, validated first) or the fallback plain CASUpdate mapping FID=Current.FID and ExpectedVersion=Current.Version. Nil tx/ctx fail closed; a wrong mapping would CAS the wrong version/row.
Safe ceremony + error display
Describe panics on a malformed config (boot-wired invariant); SanitizeStoreErr strips newlines/tabs (log injection), drops everything after a ? (connection-string credential leak), and truncates by rune count without splitting a multibyte rune (BUG-567).

Methods

WithBackend / BackendFrom / AdminStats*
Inject and extract the request-scoped backend (and admin-stats store) safely from context.
TenantMiddleware
Scope the backend per tenant, failing closed on a nil scoped backend.
CommitVerifiedFamilyCAS / SupportsVerifiedFamilyCAS
Commit a family CAS via the strongest available contract; report which path a Tx takes.
Describe / SanitizeStoreErr
Ceremony posture reporting and safe, truncated, credential-stripped error display.

A request reaching the store

Resolve tenant, scope, serve.

  1. Normalize normalize resolves the tenant slug onto the context.
  2. Scope TenantMiddleware scopes the backend to that tenant (empty → passthrough, nil scoped → 500) and injects it.
  3. Resolve Handlers/services pull the backend via BackendFrom and call the ports.
  4. Transact Single-aggregate work uses atom.Doer; multi-aggregate work uses WriteTx with the non-nesting Tx handle.

Fail-closed persistence boundary

No cross-tenant fallthrough
A nil scoped backend returns 500 rather than serving the unscoped backend.
CAS guards fail closed
CommitVerifiedFamilyCAS rejects nil tx/ctx with ErrInvalidInput and validates the verified input before writing.
No credential leak
SanitizeStoreErr drops query params after ? and strips control characters before display.
Malformed posture panics at boot
Describe panics on an invalid CeremonyConfig — a wiring bug, surfaced loudly.

System fit

  • store sits between the domain/service code and the concrete database adapters.
  • Handlers pull the backend from context; TenantMiddleware scopes it; the service/ledger layers call the ports.
  • It imports core (types + error identities), normalize (the tenant slug), ceremony, and store/atom.
  • It registers through ceremony as the store Descriptor Wirable (callee identity).

Tests

context_test.go
BackendFrom/AdminStatsFrom across nil/empty/direct/wrapped/deadline/multi-layer contexts, key-collision isolation, parent-value preservation, concurrent access, and the TenantMiddleware paths incl. the nil-scoped fail-closed 500.
store_test.go / store_fuzz_test.go
SanitizeStoreErr (the full table: ?-credential/query-param drop, newline/tab/CRLF strip, rune-safe truncation at the cap, multibyte non-split, null bytes) + a fuzz target, and the Descriptor ceremony tables.
verified_family_cas_hostile_test.go
CommitVerifiedFamilyCAS routing: nil-tx/nil-ctx fail-closed, the verified-repo path (validate-first + forward intact), validation gating the write on a FID mismatch, the fallback field-mapping (Current.FID/Current.Version), and SupportsVerifiedFamilyCAS.
tenant_middleware_test.go / contracts_test.go
The middleware wiring and the error/contract identities.

store-atom

store/atom

Defines the Doer port — the contract for running several repository calls inside one atomic transaction — plus the context plumbing that carries a typed transaction handle (and an operation-dedup ID) down to repository methods so they detect and join the outer transaction.

When a request must write several aggregates together, they must all commit or all roll back. A Doer's Do(ctx, fn) opens that boundary; repository methods called inside fn find the live transaction on the context and join it instead of opening their own, so the boundary propagates implicitly without threading a *Tx parameter everywhere. This is a port (interfaces + context helpers); the SQL and Firestore backends implement it.

Vocabulary

Doer
the interface with Do(ctx, fn); storage adapters implement it.
Transaction handle
the backend-specific *Tx stored on the context.
InTx
reports whether any transaction is active on the context.
Operation ID
an optional idempotency/dedup token on the context for retryable writes.
Nop
a no-op Doer for tests/wiring that runs fn without a real transaction.

Core concepts

Type-scoped handles
WithTx[T]/TxFrom[T] store the handle under a key parameterized by its type (txKey[T]), so a SQL *Tx and a Firestore *Tx coexist in one context without overwriting each other; TxFrom returns a handle only for the exact stored type.
Implicit propagation
A separate txMarkerKey records that some transaction is active, so InTx answers without knowing the concrete type; repository methods join the boundary they find.
Empty operation ID is no ID
OperationID treats an explicit empty string as absent (("", false)), so an empty token can never be mistaken for a real dedup key that would collapse distinct operations.
Nop still guards the boundary
The no-op Doer rejects a nested transaction context (ErrStoreNestedTx) so a test/noop adapter can't silently flatten a real boundary, rejects a nil fn (ErrInvalidInput), and propagates an fn error wrapped.
Nil-context safe
Every entry point tolerates a nil context — WithTx/WithOperationID return nil; TxFrom/InTx/OperationID return safe zeros — none panic.

Methods

WithTx / TxFrom
Store and retrieve a typed transaction handle scoped by its type.
InTx
Report whether a transaction is active on the context.
WithOperationID / OperationID
Carry and read the dedup token (empty = absent).
Nop.Do
Run fn without a real transaction, still rejecting nested boundaries and nil callbacks.

Boundary integrity

No silent flatten
Nop.Do rejects a nested transaction with ErrStoreNestedTx.
Nil callback rejected
Nop.Do rejects a nil fn with ErrInvalidInput rather than panicking.
fn errors surface
An error from fn is propagated wrapped so a failed unit of work never looks successful.
Empty dedup token ignored
OperationID reports absent for an empty string.

System fit

  • atom is a store-layer port (interfaces + context helpers), not an implementation.
  • The SQL and Firestore backends implement Doer and store their typed *Tx via WithTx; repository methods call TxFrom/InTx to join.
  • store.WriteTx builds on this for multi-aggregate atomicity.
  • It imports only core (error identities and format strings).

Tests

atom_test.go
InTx fresh-context, WithTx/TxFrom round-trip + parent-immutability + wrong-type-miss + outermost-wins, OperationID round-trip, Nop.Do executes/receives-ctx/rejects-nested/fn-not-called/implements-Doer, the wrapped nested-tx error, and no-alloc (BUG-653) / context-value (BUG-858).
atom_hostile_test.go
Two distinct tx types coexisting without collision (+ a never-stored pointer type missing), Nop.Do nil-callback rejection and fn-error wrapping, OperationID empty-string-is-absent, and nil-context safety across every entry point.

store-dual

store/dual

A store.Backend that writes to two backends at once — a primary (Firestore) and a secondary (SQL) — so the system can run both stores in parallel during a migration while reads come only from the primary.

Migrating a live system can't be a switch flip; you need a window where both stores stay in sync so you can cut over (and roll back) safely. dual provides it: every write goes to both stores, every read comes from the primary (source of truth). When the secondary is verified, you promote it; until then the primary is authoritative.

Vocabulary

Primary
the authoritative backend (Firestore); all reads and the first write go here.
Secondary
the shadow backend (SQL) that receives the same writes to become a verified copy.
WriteBoth
the contract — a write must reach both stores and a secondary failure must be reported, not swallowed.
Divergence
the failure dual exists to surface — the two stores disagreeing because a write reached one but not the other.

Core concepts

Reads go to primary only
Every read delegates to the primary; the secondary is a write target until promoted, so reading it could return stale/incomplete data.
WriteBoth contract (BUG-061)
A non-tx write validates, writes the primary (failure → return, secondary never attempted, no orphan write), then writes the secondary (failure → propagated to the caller). Swallowing the secondary error would silently diverge the stores; this holds across every write method.
WriteTx best-effort replay (BUG-058)
WriteTx wraps the primary transaction; each write goes to primary immediately and enqueues a secondary op, replayed only after the primary commits. A failed callback rolls back and never replays (BUG-662); replay stops on the first secondary failure (BUG-702); a ledger Record replay clears seq/chain-hash so the secondary assigns its own (BUG-737).
Do is primary only
Single-aggregate transactions delegate to the primary.
Tenant scoping fails closed
ForTenant/ScopeTenant scope both backends via tenantScoper (BUG-059); if either can't be scoped it returns nil, and secondary lag on cursor/stats CAS must not fail the primary advance (BUG-215/216).

Methods

Writes (Create/Update/CASUpdate/Record/…)
Primary-then-secondary with secondary errors propagated (WriteBoth).
WriteTx / Do
Transactional best-effort secondary replay after primary commit; Do is primary only.
Reads (FindByID/LastEntry/…)
Delegate to the primary only.
ForTenant / ScopeTenant
Scope both backends to a tenant, failing closed if either can't be scoped.

Surface divergence, never hide it

Primary failure short-circuits
A failed primary write returns before the secondary is touched.
Secondary failure propagates
A failed secondary write is returned to the caller (BUG-061).
No replay of uncommitted writes
A failed WriteTx callback leaves the secondary untouched (BUG-662).
Partial replay stops early
Secondary replay halts on the first failure (BUG-702).

System fit

  • dual is one concrete store.Backend, chosen at boot during a migration.
  • It composes firestore (primary) + sql (secondary) and presents the single store.Backend upward, so nothing above knows there are two stores.
  • It imports core, the domain aggregate types, and store.
  • It registers through ceremony (callee store).

Tests

dual_test.go
Reads-primary-only (+ read-error propagation), the WriteBoth sweep over every Store write method (primary short-circuit + secondary propagation), WriteTx/Do primary-only + error paths, PruneRevoked primary-count + secondary-error, logSecondary, ForTenant fail-closed (BUG-059), BUG-058/662/702 replay mechanics, BUG-737 seq reset, BUG-215/216 secondary-lag tolerance.
writetx_replay_hostile_test.go
The transactional WriteBoth counterpart: every dualTx write method replays to the secondary after commit (a missing enqueue would silently diverge), and a failing callback leaves the secondary untouched.
boundary_buckets_test.go
The logSecondary and error-shape buckets.

store-sql

store/sql

A store.Backend on top of database/sql — the relational adapter that maps every repository port onto parameterized SQL, with a dialect layer isolating the syntax differences between database engines.

Two ideas run through it: parameterized queries only (values are bound as positional parameters, never concatenated into SQL, so injection is structurally impossible) and one dialect interface (engine differences — placeholder syntax, LIMIT vs FETCH NEXT, error codes — live behind Dialect, so repository code is written once). Production ships SQLite.

Vocabulary

Dialect
the per-engine adapter: Placeholder, Limit/LimitOne, SchemaSQL, TranslateError.
TranslateError
maps a raw driver error to a domain sentinel (ErrNotFound, ErrConflict) so callers use errors.Is, never string matching.
CAS
a compare-and-swap update gated on a version column; a stale version writes zero rows.
checkAffected
turns zero-rows-affected into ErrNotFound — the signal a CAS/update used a missing key.

Core concepts

Parameterized-only
Values are bound as positional parameters supplied by the dialect; SQL is never built by concatenating user data, so injection is structurally impossible.
One dialect interface
Placeholder syntax, the LIMIT clause (MSSQL needs OFFSET/FETCH NEXT, BUG-113), the schema DDL (every dialect includes the tenants table, BUG-428), and error translation live behind Dialect.
TranslateError code discrimination
nil→nil, sql.ErrNoRows→ErrNotFound, a unique/primary-key violation (SQLite codes 2067/1555)→ErrConflict, anything else wrapped. Only the unique/PK codes become a retryable ErrConflict — a NOT-NULL/CHECK/FK violation stays a plain error, or a real data bug would retry forever as a phantom conflict.
Defensive row helpers
fmtTime/parseTime (RFC3339Nano, zero on NULL/corrupt), marshalJSON (pooled buffer, HTML-escape off, trailing-newline trimmed, BUG-581), nullStr, checkAffected (0 rows → ErrNotFound), parseULID/parseUUID (corrupt → zero, callers validate).
Transaction-transparent repositories
Do/WriteTx carry the tx handle on the context (store/atom); the shared querier uses the active transaction when present, the pooled *sql.DB otherwise, so the same repository code works inside and outside a transaction.

Methods

Dialect.TranslateError / Placeholder / Limit
Map driver errors to sentinels and emit engine-correct SQL syntax.
Do / WriteTx
Single- and multi-aggregate transactions via the shared querier.
checkAffected / marshalJSON / parse*
The defensive row helpers shared by every repository.

Typed, injection-proof

No string-built SQL
All values are bound parameters.
Conflict only for unique/PK
Only SQLite codes 2067/1555 map to ErrConflict; other constraint codes stay wrapped.
Missing row is typed
Zero rows affected → ErrNotFound; sql.ErrNoRows → ErrNotFound.
Corrupt reads fail safe
parseULID/parseUUID/parseTime fall back to zero values for callers to validate.

System fit

  • store/sql is one concrete store.Backend, selected at boot (and the secondary during a store/dual migration).
  • It implements the store ports and depends on core, store/atom, and database/sql.
  • The schema DDL it ships is the source of truth for the relational layout.
  • Its dialect interface keeps the door open for non-SQLite engines.

Tests

store_test.go / per-aggregate *_test.go
Full CRUD + CAS + query against a real SQLite DB, including duplicate-insert → ErrConflict and missing-row → ErrNotFound through the real driver.
sqlite_translate_hostile_test.go
The production dialect's TranslateError directly: nil→nil, ErrNoRows→ErrNotFound, codes 2067/1555→ErrConflict, and the exact discrimination that other constraint codes (generic/NOT-NULL/FK/CHECK) stay wrapped with the original preserved.
non_sqlite_dialects_test.go
The MySQL/Postgres/MSSQL dialect shapes (placeholder/limit/error-translation) so the abstraction stays portable.
helpers_fuzz / user_safety / boundary_buckets / regression / external_regression / querier / sqlite
Helper fuzzing, user-write safety, boundary buckets, the bug ratchets (BUG-190 tenant table, BUG-630 injected clock, BUG-084 driver codes), and the querier contract.

store-firestore

store/firestore

A store.Backend on Google Cloud Firestore — the document-database adapter that maps every repository port onto collections, with tenant-prefixed collection names for isolation and read-before-write transactions for compare-and-swap.

Firestore isn't relational, which shapes two choices: no DB-enforced constraints (the app validates every aggregate and uses explicit uniqueness/dedup documents), and reads-before-writes in a transaction (the family CAS reads the current snapshot first, then writes — the VerifiedFamilyCAS shape store routes to). It is the primary backend in production and the primary side of a dual migration.

Vocabulary

Collection
a Firestore container of documents (the rough analog of a table).
Collection prefix
the per-tenant string (<tenant>_) prepended to every collection name so each tenant's data lives in its own collections.
translateError
maps a Firestore/gRPC error to a domain sentinel (ErrNotFound, ErrConflict).
Test namespace
extra nesting used only in tests so parallel runs don't collide in a shared emulator.

Core concepts

Tenant isolation by prefix
ForTenant sets collPrefix=tenant+"_"; collectionName maps a logical name to the prefixed physical name via a typed table. An unknown logical collection resolves to "" and coll rejects it, and tenant A's prefix never addresses tenant B's collections.
App-enforced invariants
Firestore has no UNIQUE/NOT NULL, so every aggregate is Validate()'d before writing and uniqueness/dedup use explicit documents; reads decode through typed structs (DataTo), not untyped maps (BUG-864).
translateError never reclassifies
nil→nil, gRPC NotFound→ErrNotFound, AlreadyExists→ErrConflict, any other code wrapped (%w) and never reclassified — mapping a PermissionDenied (IAM misconfig) to ErrNotFound would mask it as empty; to ErrConflict would make it look retryable. grpcCode returns Unknown for a non-status error so the default wrap fires.
Reads before writes
Do/WriteTx run Firestore transactions with all reads preceding all writes; the projection cursor advances via compare-and-swap so concurrent advances don't lose updates.

Methods

ForTenant / collectionName
Scope a Store to a tenant and resolve logical collection names to prefixed physical ones (unknown → rejected).
translateError / grpcCode
Map gRPC errors to domain sentinels; extract the status code (Unknown for non-status errors).
Do / WriteTx
Firestore transactions with reads-before-writes; CAS for family and projection cursor.

Isolated, typed, never-reclassified

Unknown collection rejected
A logical name not in the typed table resolves to "" and is rejected — no access to an unprefixed/unknown collection.
NotFound/AlreadyExists typed
gRPC NotFound→ErrNotFound, AlreadyExists→ErrConflict.
Other codes wrapped
PermissionDenied/Unavailable/etc. are wrapped, never turned into not-found/conflict.
Validate before write
Aggregates are Validate()'d before persistence, since the DB enforces no constraints.

System fit

  • store/firestore is the primary backend in production (and the primary side of a store/dual migration).
  • It implements the store ports and depends on core, store, store/atom, the Firestore client, and gRPC codes/status.
  • The typed collection-name table is the source of truth for the physical layout.
  • It registers through ceremony.

Tests

store_test.go / ledger_test.go / outbox_test.go
Full CRUD/CAS/query/ledger-chain/outbox against the emulator, including typed-and-scoped collection names, unknown-collection rejection, ForTenant, and not-found / duplicate-slug paths through the real client.
translate_error_hostile_test.go
The production translateError/grpcCode directly: nil→nil, NotFound→ErrNotFound, AlreadyExists→ErrConflict, every other gRPC code wrapped-not-reclassified with the original preserved, and a non-gRPC error → Unknown → wrapped.
contracts / regression / boundary_buckets / pagination_streaming / helpers_fuzz
The typed contracts, the bug ratchets (BUG-475 unique claims, BUG-523 double-translate, BUG-864 typed decoding), streaming pagination (O(1) memory), boundary buckets, and helper fuzzing.

core

core

The single source of truth: the one package that owns every shared contract — typed structs, typed enums, iota constants, typed error identities, validation rules, paths, and protocol values — so nothing important is a loose string, magic number, or duplicated literal.

If two packages must agree on something — a route's protection level, an error's identity, a header name, a validation rule — that agreement lives in core, once, typed, and compiler-visible. The alternative (each package copying the constant or re-checking the string) is the hidden coupling core exists to prevent. Because everything imports core, core imports almost nothing: it is deterministic compute with no concurrency primitives, safe for everyone to depend on.

Vocabulary

Contract
a shared rule expressed as a type: a struct, an enum, an error identity, a constant, or a Validate() method.
Typed enum
a uint8 (etc.) with fixed iota values, a Valid() method, a String(), and JSON/text round-tripping — never a bare string.
Error identity
a sentinel error (ErrInvalidInput, ErrConflict, ErrNotFound, …) matched with errors.Is, never by string.
Validate()
the method a type carries to enforce its own invariants at ownership boundaries (ingress, package crossing, persistence, output).
Fail closed
when a value is unrecognized or unset, deny rather than allow; the safe default is the restrictive one.

Core concepts

Everyone imports core; core imports no sibling
Shared rules and constants live here so packages never import each other merely to share them — the coupling coefficient stays at zero. If the compiler can't see it, enforce it, or break the build when it changes, it isn't a real contract and belongs in core.
Typed enums with exhaustive contracts
Each enum has Valid() (the zero/iota-zero value typically invalid so it fails closed), String(), and JSON/text round-trip that fails closed on an unknown value — never a silent default.
Core-owned error identities
Stable sentinels live here; packages wrap with local context but the identity stays compiler-visible so callers and tests use errors.Is / errors.As, never string matching.
RouteGate is the worked example
It classifies every API route's protection. Its zero value (GateUnclassified) is deliberately NOT Valid (BUG-493 — an unclassified route fails closed, never silently public); GuaranteesRequestAuthenticity()/IsPrivate() are exact compiler-checked sets (the CSRF ratchet accepts only the request-authenticity gates, never GatePublic).
Validation at the owner
Validation belongs to the type that owns the rule; domain aggregates (user/family/credential/ledger/outbox/admin/policy/tenant) each own their Validate() in a core subpackage.

Methods

Enum contracts (Valid/String/Marshal/Unmarshal)
Every typed enum's fail-closed, round-tripping wire contract.
RouteGate.GuaranteesRequestAuthenticity / IsPrivate
The exact route-protection membership sets the CSRF ratchet and authz rely on.
Validate() (per type)
Each struct/enum enforces its own invariants at the boundary.
Error sentinels (ErrInvalidInput/ErrConflict/ErrNotFound/…)
The shared identities every package wraps and every test matches with errors.Is.

Typed, fail-closed, compiler-visible

Zero values fail closed
An iota-zero enum value (e.g. GateUnclassified) is invalid, so an unclassified contract denies rather than defaults open.
Unknown values rejected
Marshal/unmarshal of an invalid enum fails with the typed identity, never a silent default.
Identity over strings
Errors carry core-owned identities matched with errors.Is/As, not substring checks.
Validate at boundaries
Types validate themselves at ingress/persistence/output, owned by the type that holds the rule.

System fit

  • core is the base of the dependency graph — every other package imports it for types, constants, and error identities.
  • Nothing in core imports a sibling business package; shared rules move INTO core, typed and validated.
  • Domain aggregates live in subpackages (core/user, core/family, core/credential, core/ledger, core/outbox, core/admin, core/policy, core/tenant).
  • It is deterministic compute with no sync/channels (those live in the store/ledgerwriter layers).

Tests

route_gate_hostile_test.go
RouteGate: the zero value fails closed (BUG-493), the exhaustive Valid() sweep over all uint8, the exact membership of GuaranteesRequestAuthenticity() and IsPrivate() (with negative anchors for lookalikes like GatePublic), and JSON/text round-trip with fail-closed marshal/unmarshal.
per-contract suites
A hostile, exhaustive test file per contract — route method, breaker state, pay currency/provider, host validation, the error-identity tables, and the domain aggregates' Validate — each sweeping every enum value and asserting errors.Is identities, never string matches.

breaker

breaker

A circuit breaker: a small state machine that protects calls to an external dependency by tripping open and rejecting calls immediately once the dependency starts failing — so a sick dependency can't tie up resources on doomed requests.

When a downstream service is down, naively retrying every request wastes goroutines/connections/time and can worsen the outage. The breaker watches the failure rate; once it crosses a threshold it stops calling for a cooldown (fails fast), then lets a single probe through — if that succeeds, normal traffic resumes. The pattern is borrowed from an electrical breaker: it trips to protect the circuit, then a probe (or an admin Reset) closes it.

Vocabulary

Closed / Open / HalfOpen
the three states — healthy (calls pass), tripped (calls rejected), probing (one call allowed to test recovery).
Threshold
how many failures within the window trip the breaker open.
Window
the rolling period over which failures are counted; when it expires the counter resets.
Cooldown
how long the breaker stays open before allowing a probe.
Probe
the single call allowed through in HalfOpen to test recovery.

Core concepts

Two-call protocol
Ask Allow() before calling the dependency (nil = proceed, ErrBreakerOpen = don't), then Record(success) the outcome so the breaker learns.
State transitions
Closed+failure increments the windowed counter → Open at >= Threshold (a window expiry resets it first, so failures spread across windows don't accumulate); Closed+success resets the counter; Open rejects until cooldown → HalfOpen; HalfOpen admits exactly one probe; HalfOpen success → Closed, failure → Open.
Single probe + stuck-probe reset
In HalfOpen only one Allow() returns nil (a CAS gate); concurrent callers are rejected, and a probe stuck in flight longer than the cooldown (a panicked/leaked goroutine) is reset so a new probe can fire.
Lock-free reads, serialized writes
Allow() reads an immutable snapshot via atomic.Pointer (never blocks); Record() serializes transitions under a mutex and swaps a brand-new snapshot so readers never see partial state; a fast-path skips the mutex for the steady state (closed, no failures, success).
Fail closed on misuse
A nil or zero-value Circuit (no clock/snapshot, never from NewCircuit) makes Allow() reject and State() report Open, with Record/Reset/Name as safe no-ops; Config.Validate rejects a non-positive Threshold/Window/Cooldown (ErrBreakerInvalidConfig + ErrInvalidInput) and NewCircuit propagates it. Failing open on misuse would silently remove protection.

Methods

NewCircuit / Config.Validate
Build a breaker from a validated config (positive threshold/window/cooldown), starting Closed.
Allow / Record
The lock-free gate and the serialized outcome reporter.
State / Name / Reset
Read the current state (lock-free), the configured name, and force back to Closed for manual recovery.

Protecting one call

Gate, call, record, learn.

  1. Allow Read the atomic snapshot; reject with ErrBreakerOpen if Open (or if HalfOpen and the single probe is taken).
  2. Call If allowed, call the dependency.
  3. Record Report success/failure; transitions are serialized under the mutex.
  4. Trip / recover Cross the threshold → Open; cooldown elapses → HalfOpen probe; probe succeeds → Closed.

Reject, never falsely allow

Open rejects
Allow() returns ErrBreakerOpen while open or when the single probe is taken.
Misconstructed fails closed
A nil/zero Circuit rejects via Allow() and reports State()==Open; Record/Reset/Name are safe no-ops.
Bad config rejected
A non-positive threshold/window/cooldown fails Config.Validate with ErrBreakerInvalidConfig; NewCircuit returns it, not a breaker.
Exactly one probe
Concurrent HalfOpen callers get ErrBreakerOpen except the single admitted probe.

System fit

  • Runtime Circuit instances are owned by the bridge clients that call external providers (Stripe/PayPal/email).
  • Circuit implements core.Breaker; the package exposes a stateless Descriptor for the boot manifest.
  • It imports core (the BreakerState enum + error identities) and ceremony, plus sync/atomic/time.
  • It registers through ceremony as the breaker Descriptor Wirable.

Tests

breaker_test.go
The full transition behavior: Open→HalfOpen at exact cooldown, HalfOpen success→Closed / failure→Open, allows-only-one-probe, window-expiry resets the counter, success resets failures, Reset from every state, the window boundary tick, and probe-success-then-burst.
hostile_test.go
The adversarial concurrency suite: the record/probe race (exactly one probe, BUG-668), concurrent Allow/Record, concurrent failures serializing to exactly the threshold, concurrent Reset, the stale-probe reset, the full transition and Allow (state × clock) matrices, and name stability.
failclosed_hostile_test.go
Config.Validate rejecting each non-positive field (threshold/window/cooldown, zero and negative) with both error identities + NewCircuit propagation, and the nil/zero-value circuit failing closed (Allow→ErrBreakerOpen, State→Open, Record/Reset/Name safe no-ops).
contracts_test.go / regression_test.go
Error-identity distinctness + wrap-verb contracts, BreakerState string derivation, the snapshot/Circuit Validate field rejections, and the bug ratchets.

auth-starter

auth/starter

The authentication service — the Authority that implements registration, sign-in (password and WebAuthn), magic links, password reset/change, sessions, token issuance, step-up elevation, and account recovery — turning each auth request into the right ledger-audited, transactional state change.

The Authority is a façade over a set of flows, one per file. It depends on a store (users/credentials/ledger/outbox), a hasher (Argon2id via professor), a pepper (a server-held secret mixed into every hash), and a scopedMAC (signs short-lived scoped tokens). Every mutation runs inside a store.WriteTx, appends a ledger entry, and (where relevant) enqueues an outbox email — atomically.

Vocabulary

Anchor version
a per-user counter bumped on every credential change; a token minted against an old anchor version is dead — this makes reset/magic links single-use and revokes outstanding tokens after a password change.
Scoped token
a short-lived MAC-signed token bound to a single purpose (ScopeReset, ScopeElevation, …) and a user + anchor version; a token for one scope can't be used for another.
Pepper
a secret added to the password before hashing, held by the server (not the database), so a stolen hash dump alone can't be brute-forced.
Enumeration
leaking whether an email/username exists; the flows never reveal it.
Step-up / elevation
re-proving identity for a sensitive action even within an active session.

Core concepts

Enumeration-safe reset
RequestPasswordReset always returns {ok:true} — for a malformed email (rejected before any DB read, BUG-234), a non-existent email, or an ineligible account — so an attacker can't probe which emails are registered; only a real, eligible user gets a ScopeReset token emailed.
Anchor-version CAS
ConfirmPasswordReset re-reads the user inside WriteTx and rejects a token whose AnchorVersion no longer matches (already used, or password changed since); on success it bumps the anchor version, invalidating the token and its siblings.
Change-password fail-closed CAS
ChangePassword verifies the current password (constant-time), hashes the new one OUTSIDE the tx (BUG-714), then CAS-guards on the verified hash with a constant-time compare (BUG-906) — failing closed if the stored hash changed under it — and bumps the anchor version.
Conflict → user-safe retry
A ledger conflict retries once reusing the one precomputed Argon2 hash (BUG-750); a persistent conflict surfaces as a user-safe retry DomainError (AuthMsgRetryChangePassword) that keeps the ErrConflict identity for callers but doesn't leak the raw conflict (BUG-749).
Uniform security disciplines
Fail closed (inactive users rejected before any password work, BUG-052; unknown tokens/wrong scopes/anchor mismatches deny), constant-time comparisons, one verification per denied path (no timing enumeration), and ledger+outbox in the same transaction as the mutation.

Methods

RequestPasswordReset / ConfirmPasswordReset
Enumeration-safe reset request and anchor-CAS-protected confirm-and-sign-in.
ChangePassword
Authenticated password change with constant-time CAS guard and conflict-retry.
Register / SignIn / Magic / WebAuthn / Elevate / Recovery
The other auth flows, each transactional + ledger-audited.
verifyCurrentPassword
The shared constant-time current-password verifier.

An auth mutation

Validate, verify, transact, audit.

  1. Validate + classify Validate the typed request; reject inactive users and malformed input before any expensive or revealing work.
  2. Verify Check the scoped token / current password (constant-time), and the anchor version.
  3. Hash outside the tx Compute the Argon2 hash before WriteTx so the ledger mutex isn't held during hashing.
  4. Transact Inside WriteTx: CAS-guard, write the user, append the ledger entry, enqueue the outbox email, bump the anchor version — atomically.

Fail closed, leak nothing

No enumeration
Reset requests look identical whether or not the account exists; denied paths run one verification.
Tokens are single-use + scoped
An anchor-version mismatch or wrong scope is rejected (ErrStarterTokenAnchorMismatch / ErrUnauthorized).
Inactive users rejected
Locked/suspended users are rejected before any password verification (BUG-052).
Conflicts are user-safe
A persistent ledger conflict becomes a retry DomainError, not a leaked internal conflict (BUG-749).

System fit

  • auth/starter is the concrete implementation behind the auth package's ports.
  • userapi/switchboard route HTTP auth requests into its Authority methods; it owns no transport.
  • It depends on core (+ core/user/ledger/outbox), store, ledgerwriter, professor (hashing + MAC), and auth.
  • Every mutation is transactional, ledger-audited, and outbox-notified atomically.

Tests

password_test.go
Reset token single-use + anchor split (BUG-699/700), ChangePassword Argon2-outside-tx (BUG-713/717), the constant-time CAS guard failing closed on a concurrent hash change (BUG-906), the wasted-Argon2 retry (BUG-750), and scoped-token concurrent replay (BUG-903).
change_password_conflict_hostile_test.go
changePasswordError mapping a (possibly wrapped) conflict to the user-safe retry DomainError while passing non-conflict errors through (BUG-749), and the end-to-end persistent-conflict path (both retries fail → user-safe error, ErrConflict identity intact, Argon2 hashed once).
security_invariants / adversarial / register_enumeration_hostile / token_wrong_user
Enumeration safety, cross-user token rejection, one-verification-per-denied-path, and the adversarial token/scope matrix.
regression_test.go (6k lines)
The full BUG-xxx ratchet across every flow — anchor bumps inside WriteTx, locked-user rejection, pre-auth token rejection, stale-read protection, and more.

Core domain aggregates

core-ledger

core/ledger

Owns the audit-ledger value types and the canonical hashing — the Entry struct, the Type enum of auditable events, the typed Details, and the deterministic serialization the chain hash is computed over.

core/ledger is the pure, deterministic half of the ledger: given a previous hash and an entry, HashEntry returns the next chain hash — no concurrency, no I/O. The stateful append authority (sequence assignment, persistence, conflict resync) is the separate ledgerwriter package, which calls HashEntry. The split exists because core forbids sync/channels (BUG-679); the canonical hash path must stay pure so writer and verifier compute byte-identical results.

Core concepts

Tamper-evident canonical hash
HashEntry writes prevHash + the entry's canonical JSON (fixed lexicographic key order, byte-stable) and SHA-256s it. Every tamper-relevant field is in the hash (incl. IP/device fingerprint, BUG-764); ChainHash itself is excluded (BUG-697) so the field storing the result isn't part of the input.
Chain linkage
Each entry's hash depends on the previous one, so editing/dropping/reordering any entry breaks every later hash. GenesisHash is the fixed first link.
Fail-closed Type enum
Type is a multi-segment iota driven by a label table; the zero TypeUnknown and any gap value are invalid — MarshalJSON rejects with ErrInvalidType, ParseType rejects unknown strings.
Typed details
Details carries typed string/int/bool values with its own canonical marshalling and key validation — no free-form blob on the wire.

System fit

  • Imported by ledgerwriter (append), the store backends (persist), and the auth/pay/admin flows (construct via ledger.New).
  • Depends only on core and professor (SHA-256); owns no state.

Tests

chain_test.go / canonical_test.go
HashEntry prev/entry sensitivity, the ChainHash-excluded property (BUG-697), the canonical field coverage + key ordering, and the network fields in the hash (BUG-764).
type_exhaustive_hostile_test.go
The all-256-uint8 Type cross-method agreement (Valid/MarshalJSON/String/ParseType consistent for every value) and TypeUnknown failing closed.
event/details/fuzz/perf_ratchet
The Type valid-values + JSON, the typed detail values + key validation, the canonical fuzz target, and the hot-path alloc ratchets.

core-user

core/user

Owns the User aggregate — the typed user record, its Role and Status enums, the validation rules, the role-assignment authz, and the lifecycle state machine — so every package agrees on one definition.

The two authz-critical functions are the heart of the package: AssignableRolesFor (which roles an actor may grant) and ValidTransition (the legal user-status state machine). Both are deny-narrow and fail closed.

Core concepts

Role-assignment privilege model
AssignableRolesFor(actor): SuperAdmin may assign anyone; Admin may assign ONLY Staff (never Admin/SuperAdmin — no escalation, and not Customer); everyone else assigns nothing. Widening Admin's set would be a direct escalation vulnerability.
Lifecycle state machine
ValidTransition(from,to): Deleted is terminal (no un-delete), Banned can only go to Deleted (no un-ban), no transition to the invalid zero, and self-transitions are illegal.
Validation + predicates
User.Validate enforces identity fields + profile caps; IsLocked/IsActive/CanReceiveAuthRecovery/IsPurgeEligible are the time-based predicates the auth/admin flows consult.

System fit

  • Imported by auth/auth/starter, the store backends, adminapi, and the viewmodels.
  • Depends only on core; no other package re-implements role assignment or lifecycle transitions.

Tests

authz_matrix_hostile_test.go
The exhaustive AssignableRolesFor privilege matrix (every actor → exact set, Admin no-escalation anchor) and the full ValidTransition lifecycle matrix (every from×to, with Deleted-terminal / Banned-only-to-Deleted / no-self / no-to-Unknown anchors).
model_test.go / snapshot / boundary_buckets
Role/Status Valid/String/Parse/JSON, User.Validate field rules, predicates, and the snapshot projection.

core-outbox

core/outbox

Owns the outbox message types — the Intent (a queued notification), its Type/Status/Channel enums, the typed Payload, the required-payload-key contract per type, and the retry backoff schedule.

The outbox pattern stores the intent to send in the same transaction as the state change; a worker delivers it later with retries. core/outbox is the typed contract for those stored intents.

Core concepts

Two validation gates
Intent.Validate checks payload structure + the size cap (ErrPayloadTooLarge); ValidateRequiredPayload separately rejects an intent missing the keys its Type declares (ErrMissingRequiredPayloadKey).
Bounded, jittered backoff
Retries use an exponential delay capped at one hour; BackoffDelayWithJitter spreads them ±10% (still capped) to avoid a thundering herd. Bounded for every attempt and seed.

System fit

  • Imported by the store backends (OutboxStore port), auth/pay (enqueue inside write transactions), and the delivery worker.
  • Depends only on core; owns the message contract, not the delivery.

Tests

already saturated
One source file with ~1,900 test lines + five fuzz targets — needed no new test. outbox_test (enums, known-types closed set, Type→Channel/action-link/required-key contracts, Payload set/get + reserved-key collision), backoff buckets + BUG-749 jitter bounds, payload size cap + duplicate-key rejection, and fuzz over every surface.

core-family

core/family

Owns the refresh-token family (the rotating-credential state for one login) and the pure Advance state machine that detects a stolen token and revokes the whole family.

A session is kept alive by a refresh token exchanged for a new one on each use (rotation). The descendants of one login form a family. If an OLD token is presented after rotation, two parties hold tokens from the family — a fork — meaning theft; the safe response is to revoke the entire family.

Core concepts

Theft-detecting Advance
Advance runs security → rate-limit → sequence gates: seq ahead → Revoked (tamper); same seq, different JTI → Fork; one behind with the recorded previous JTI → idempotent retry, else Fork; further behind → Fork (stale old token replayed, the theft signal); a valid advance rotates forward; seq exhaustion (MaxInt64) revokes rather than overflow.
Pure + CAS-persisted
Advance has no I/O/concurrency (singleflight lives in auth/gate); auth persists the result via store.CommitVerifiedFamilyCAS, and a Fork/Revoked outcome tears the session down.

System fit

  • Imported by auth (calls Advance on each refresh) and the store (family CAS persistence).
  • Depends only on core.

Tests

advance_security_hostile_test.go
The previously-uncovered theft/forgery branches: vertical fork (stale old token), horizontal-fork-on-previous, the tamper guard (seq ahead → Revoked), and sequence exhaustion (MaxInt64 → Revoked) — each pinned to its exact outcome + reason, family left unchanged.
advance_test / fuzz / contracts / revoke_hostile
The main outcome matrix, FuzzAdvance(+TamperGuard+Deterministic), the AssuranceLevel/AdvanceOutcome enums + AdvanceResult validation, and revocation behavior.

core-policy

core/policy

The authorization decision table: given a user Role and an Action, Evaluate returns Allow or Deny — the single, deny-by-default source of truth for who may do what.

Evaluate returns Allow only when an explicit rule grants that role that action; an unknown role, out-of-range action, or the sentinel falls through to Deny. The Decision zero value is Deny, so a missing decision is safe.

Core concepts

Deny by default
You add an allow rule deliberately; you never have to remember to add a deny. The zero Decision is Deny.
Privilege gradient
SuperAdmin may do everything; Admin most things except the most dangerous (can't delete a user); Staff read/update + view admin/ledger but not mutate roles or manage blocks; Customer essentially read-only.

System fit

  • Consulted by adminapi/admin handlers before a privileged action; complements core/user.AssignableRolesFor and core.RouteGate.
  • Depends only on core and core/user; pure, no state.

Tests

already saturated
policy_test pins the full Role×Action matrix cell-by-cell (super_admin all-allow, admin's allow/deny split, staff/customer limited sets), deny-by-default, the admin-action boundary, the sentinel-denied-for-all-roles case, and the Action/Decision enum contracts — needed no new test.

core-credential

core/credential

Owns the WebAuthn credential aggregate plus the security predicates that decide whether a credential may be used and whether a presented sign-count indicates a cloned authenticator.

WebAuthn's defense against a duplicated authenticator is the monotonic sign counter; revocation removes a lost/stolen key. core/credential owns those rules.

Core concepts

Clone detection
CounterRegressed(presented) is true when presented <= stored (the counter didn't advance → clone/replay), except when both are 0 (the authenticator has no counter). A true result must make the caller reject the assertion.
Revoked-exclusion filter
IsUsable is !Revoked; UsableCredentials filters a list to only usable ones — a revoked authenticator must never survive into the set offered for login, or it could still authenticate.
Validation + constant-time match
Credential.Validate bounds the id/key/AAGUID/transports; MatchesCredentialID compares in constant time (professor.Equal) so matching can't leak via timing.

System fit

  • Imported by auth/starter's WebAuthn flows, the store's CredentialRepository, and admin credential management.
  • Depends only on core and professor.

Tests

usability_validate_hostile_test.go
The previously-untested core: a table-driven Credential.Validate sweep rejecting every field gap with the exact sentinel (+ at-cap acceptance), and the UsableCredentials revoked-exclusion matrix (interleaved/all-revoked/order-preserving) proving a revoked credential never leaks into the usable set.
boundary_buckets_test.go
The AttestationType enum, the CounterRegressed clone-detection buckets, and MatchesCredentialID boundary buckets.

core-admin

core/admin

Owns the admin/operations value types — the IP BlockEntry, the dashboard Stats, the GeoSignal (a geo-located auth event for anomaly detection), and the GeoEventType enum — each with a Validate that keeps garbage out of the admin and abuse-detection paths.

These types cross into the admin store and the abuse-detection pipeline, so their Validate methods are the gate that stops corrupt data.

Core concepts

BlockEntry validation
IP/reason/blocked-by required and length-bounded; an ExpiresAt before CreatedAt is rejected (a block born already expired protects nothing).
Stats can't go negative
A negative user count or ledger sequence is corruption, not a value.
Real coordinates only
GeoSignal.Validate requires a valid event type, bounded text, and coordinates that are real — latitude in [-90,90], longitude in [-180,180], never NaN/±Inf. A NaN coordinate would poison every downstream anomaly computation.

System fit

  • Imported by adminapi, the store's AdminBlockManager/AdminStatsStore ports, and the geo/abuse detection.
  • Depends only on core (caps, error identities, lat/long validators).

Tests

validate_hostile_test.go
The previously-untested Validate methods, all table-driven: BlockEntry (empty/over-cap fields, expires-before-created, at-cap + equal-timestamp acceptance), Stats (negative counters incl. MinInt64), and GeoSignal (invalid type, over-cap text, the coordinate sweep — just-over/just-under ±90/±180, exact boundaries accepted, NaN/±Inf rejected).
geo_event_type_test.go
The GeoEventType enum Valid/String/JSON round-trip, sentinel check, and boundary buckets.

core-tenant

core/tenant

Owns the Tenant aggregate — the per-customer namespace record — and its Status enum, so the multi-tenant routing and storage layers agree on one typed definition of a tenant.

The tenant slug is the isolation key: store's TenantMiddleware and the Firestore collection-prefixing both derive from it, and normalize resolves it from the host. A malformed tenant must never persist, or it could break those isolation boundaries.

Core concepts

Strict, fail-closed model
Tenant.Validate requires a non-zero id, a valid slug and name within caps, and a valid status; Status is a typed enum whose zero value fails closed and whose ParseStatus rejects unknown/hostile strings rather than defaulting.
Slug = isolation key
Because the slug drives tenant scoping and storage namespacing, its validity is a security boundary, not cosmetic.

System fit

  • Imported by store (TenantRepository + scoping), normalize (slug resolution), and the SQL/Firestore backends.
  • Depends only on core.

Tests

already saturated
model_test (Status Valid/String/JSON, ParseStatus rejecting hostile strings, Tenant.Validate boundary buckets) and contracts_test (error-identity distinctness, deep-nesting survival, format-verb preservation, string/limit contracts) — needed no new test.

Telemetry

telemetry-telescope

telemetry/telescope

The structured-logging layer: it builds the slog logger the whole system writes through, and provides the access-log middleware that emits one structured line per HTTP request (method, path, status, duration, size).

telescope owns the process logger (a colored dev console handler vs JSON in prod) and the per-request access log. The access log's load-bearing rule is its sampling policy: INFO lines are thinned to control volume, but error lines are always kept.

Core concepts

Level by status
levelForStatus maps 5xx→ERROR, 4xx→WARN, else INFO — the response status decides the log level.
Sampling never drops errors
INFO lines are logged 1-in-N (SampleN) to cut volume, but WARN/ERROR are never sampled — you can afford to drop some success logs, never a 4xx/5xx. SampleN 0/1 disables sampling; SampleN<0 is rejected at boot (it would invert the modulo and swallow every INFO line).
Plain-text log files
StripWriter removes ANSI color escapes before writing a dev log file, holding its parser state across writes (a color code can span two Write calls) and reusing its buffer.
Deterministic tee
TeeHandler fans a record to two handlers; if both fail, the primary error wins.

System fit

  • telescope.New builds the process logger at boot; every binary logs through it.
  • The access-log Middleware sits in the global pipeline and wraps the ResponseWriter (via telemetry/httpwrap) to capture status + size.
  • Depends on core (log keys, event types, error identities) and telemetry/httpwrap; registers through ceremony.

Tests

sampling_hostile_test.go
The sampling invariant as an exact-count sweep: INFO logged 1-in-N (SampleN 0/1 disabling it), 3xx counts as INFO, and WARN/ERROR emitted for every request even at extreme SampleN — the errors-never-dropped guarantee.
telescope_test.go
Identity/format/string contracts, Validate gating, typed write-failure identities, level dispatch on the typed enum, tee primary-error-wins, one-line-per-request, and levelForStatus by family.
strip_test.go / strip_fuzz_test.go
ANSI stripping, state across writes (BUG-222), buffer reuse (BUG-859), nil-handler safety (BUG-223), and a fuzz target.
boundary_buckets / contracts / event
Dev-handler level/duration/sparkline boundary buckets, the error/ceremony contracts, and the emit/source behavior.

telemetry-cyclops

telemetry/cyclops

The outermost request middleware: the single writer of the Golden Thread (a trace id that follows a request through the whole system), the real-client-IP resolver, and the panic recovery that stops one bad handler from crashing the server.

cyclops opens the root OpenTelemetry span, resolves the caller's true IP (spoofing-safe), and defers a panic recovery around the inner handler. Because rate limiting and the IP blocklist key off the client IP, and because an unrecovered panic would take down the process, this is the first middleware in the global pipeline.

Core concepts

Spoofing-safe client IP
Forwarding headers (X-Forwarded-For/CF-Connecting-IP/X-Real-IP) are trusted only when the direct peer is in TRUSTED_PROXY_CIDRS; otherwise the IP is the direct peer's RemoteAddr. The empty default means a client can never spoof the value the rate-limiter/blocklist use.
Panic containment
recoverPanic recovers any handler panic (no propagation → no crash), records it on the span, emits a Critical signal with the trace id + method/path (its own nested recover so signalling can't crash recovery), logs the stack, and writes a 500 only if no bytes were written yet.
Golden Thread
cyclops is the single writer of the trace id; GetTraceID/AddSpan read and extend it, while OutboundTraceHeaders projects it into Primitive Exchange so logs, signals, and downstream calls all correlate.
Excluding-sampler
newEndpointExcluder drops high-volume excluded routes (health/probes) outright and delegates the rest to a ratio sampler; it Validates at boot because a mis-wired sampler would silently drop every span.

System fit

  • cyclops is the first middleware in the global pipeline, so its trace + panic recovery wrap everything inside.
  • Depends on core (trace/config contracts), alert (signal), telescope (logging), and telemetry/httpwrap (status-capturing writer).
  • Registers through ceremony as the tracer.

Tests

panic_recovery_hostile_test.go
The runtime recovery path: a handler panic (error and non-error value) is contained (no propagation), returns 500 when nothing was written, keeps the sent status when headers were already written, emits exactly one Critical signal with the trace id + method/path, and a no-panic request emits no signal.
client_ip_hostile_test.go
The spoofing defense: untrusted peer can't spoof via headers, trusted peer honors the hierarchy, header trust needs the exact peer, malformed TRUSTED_PROXY_CIDRS rejected.
cyclops_test.go / contracts_test.go
Config/tracing/excluded-route Validate, trace-id-absence invariants, Describe/Middleware/InitTracing panic-on-invalid-config, trace-parent length, and ceremony details (probe count).

telemetry-siren

telemetry/siren

The operational alert dispatcher: it takes a signal, de-duplicates it so the same alert doesn't spam responders, and delivers it through a notifier — with the dedup window and bypass rules varying by the system's threat mode.

When something breaks the same condition can fire thousands of times a second; sending each would bury on-call (and could DoS them). siren keeps a per-alert cooldown and suppresses identical alerts within it, with mode-aware rules: the cooldown and which severities may bypass dedup differ between Peace and War.

Core concepts

Fingerprint dedup
The dedup key is (name, severity); same name with a different severity is a different alert. After one fires, identicals are suppressed for the cooldown.
Mode-aware bypass (crown jewel)
Cooldown is PeaceCooldown in Peace, WarCooldown otherwise. A Critical alert BYPASSES dedup in Peace (a critical must always get through in peacetime) — but in War even Criticals are deduped, to prevent an alert storm during an incident. Mode is resolved per-signal, not cached.
Failed delivery un-arms dedup
The window is armed optimistically before delivery (so concurrent identicals suppress immediately); if delivery fails it's un-armed, so a failed alert doesn't block the retry.
Bounded + always-delivers
The dedup map is bounded with oldest-eviction (a flood of distinct alerts can't grow it unbounded); delivery runs with bounded retries even if the request context was canceled; a never-configured engine no-ops rather than panicking.

System fit

  • siren is the sink for alert.SignalFunc across the system — cyclops panic recovery, nonce CSRF rejections, and other security signals flow here.
  • Depends on core (limits, error identities), alert (payload + severity), and wall (the mode).
  • Registers through ceremony; the orchestrator supplies the notifier and mode function at boot.

Tests

already saturated
siren_test/hostile_test: cooldown boundary (at/1ns-before), fingerprint name-vs-severity, the mode-aware matrix (Critical bypasses in Peace, War dedups Critical, mode per-call), War-mode suppression, canceled-context delivery, retry count, never-configured no-panic, empty-msg name synthesis. regression_test: failed-delivery-no-dedup, dedup race (virtual clock), bounded map, config-mode. contracts/fuzz: the Validate boundaries + dedup-contract fuzz. Needed no new test.

telemetry-horizon

telemetry/horizon

Collects RED metrics — Rate (request count), Errors, and Duration (a latency histogram) — per route, on the request hot path with a single atomic write per request.

RED is the three numbers you need to see a service's health. horizon keys metrics by route class (method, path-pattern) registered up front, then freezes the route set into an immutable map so the hot path is lock-free. Latency is a histogram of non-cumulative bucket counts; the cumulative (Prometheus) form is computed at scrape time.

Core concepts

One atomic add per request
Record increments requests + duration sum, classifies status (5xx→Errors, 4xx→ClientErrors, else neither), and does exactly one atomic add to the matching bucket. A duration above every bucket still counts toward Rate/Errors/Sum but increments no bucket (no stored +Inf bucket — the early return keeps the hot path cheap).
Non-cumulative storage
Buckets store non-cumulative counts to avoid the O(buckets) contended atomic writes a write-time cumulative histogram would cost; Snapshot computes the cumulative form.
Lock-free reads, safe reset
Snapshot is lock-free after freeze (locked before); percentiles use ceil nearest-rank over the cumulative buckets; Reset zeroes counters while preserving registrations, snapshotting route pointers under lock so it can't race Register while Record stays lock-free.
Bucket hygiene
NewWithBuckets dedups, sorts, caps over-wide input, and falls back to core defaults for empty input; Register is idempotent and returns false after freeze; Middleware panics at boot on an invalid construction.

System fit

  • horizon sits in the global middleware pipeline and exposes its snapshot to the metrics/admin surface.
  • Depends on core (route-class contracts, default buckets, error identities) and telemetry/httpwrap (status capture).
  • Registers through ceremony.

Tests

already saturated
horizon_test: status→Errors/ClientErrors/neither matrix with 399/400/499/500/599 boundaries, exact histogram boundaries incl. durations above all buckets (no bucket incremented), zero-duration, sum accumulation, register idempotency + post-freeze false, snapshot point-in-time, reset zeroing, bucket default/dedup/sort/cap, hot-path zero-alloc. bucket_equivalence_hostile: non-cumulative==cumulative + single-write-under-concurrency. contracts/race: Validate boundaries, percentile ceil/bounds, middleware panic-on-invalid, ceremony, reset-vs-register race. Needed no new test.

telemetry-httpwrap

telemetry/httpwrap

The shared ResponseWriter wrapper: it captures status, byte count, optional hijack support, and trusted client IP resolution once so telemetry middleware does not stack competing writer wrappers.

httpwrap is the leaf utility used by telescope, cyclops, and horizon. It wraps an http.ResponseWriter, records the final status and bytes written, preserves supported optional interfaces, and resolves client IPs through typed core contracts. The hot path uses a bounded pool so each request can reuse the same wrapper shape instead of allocating another recorder.

Core concepts

One writer wrapper
Acquire returns a response writer that records explicit and implicit status plus byte count; Release returns it to the bounded pool after the middleware stack is done.
No hidden interface loss
Hijack support is preserved only when the embedded writer supports it; unsupported hijack returns the typed ErrHTTPWrapHijackUnsupported identity.
Typed client IP
ClientIPFromRequest and ClientIPFromRequestTrusting return core.HTTPWrapClientIPResult, folding invalid or oversized values to the typed unknown sentinel.
Trusted proxies
Forwarded headers are trusted only through configured netip.Prefix values, keeping spoofable header parsing out of callers.

System fit

  • telescope uses httpwrap for access-log status and byte counts.
  • cyclops uses it around panic recovery so it can decide whether a 500 can still be written.
  • horizon uses the captured status to record RED metrics without installing another writer wrapper.
  • Registers through ceremony as the shared telemetry writer boundary.

Tests

httpwrap_test.go
Default, explicit, and implicit status; WriteHeader idempotence; byte counts; pool reuse; optional hijack behavior; and client IP parsing.
hostile_test.go
Released-wrapper misuse, write failures, hijack error identity, oversized IP folding, spoofed forwarding headers, malformed RemoteAddr, and boundary byte counts.
trusted_proxy_parse_hostile_test.go / contracts_test.go
Trusted proxy parsing rejects malformed CIDRs and all core error identities/format strings stay typed and wrap-safe.

telemetry-photon

telemetry/photon

The client-metrics ingest package: it accepts browser-side metric payloads at the API boundary, validates the typed core payload, and records the signal without letting malformed client telemetry affect the request path.

photon is the metrics-side companion to prism. Browser code can report performance and client runtime metrics, but those reports are attacker-controlled input. photon keeps the boundary narrow: decode a typed payload, validate the core-owned contract, and hand off only accepted metrics.

Core concepts

Client telemetry is untrusted
Every payload arrives from a browser and is treated as optional signal, never truth. Invalid payloads are rejected without mutating downstream state.
Core-owned payload
The wire shape lives in core.ClientMetricPayload so the API route, transport binder, and package handler all share the same compiler-visible contract.
API-only ingress
photon is mounted through the API binary; website/webapp/admin report metrics by calling API rather than owning a parallel ingest path.

System fit

  • cmd/api wires photon through switchboard/api for the client metrics endpoint.
  • transport handles the HTTP decode/response boundary; photon owns metric payload validation and package behavior.
  • Works with cyclops trace ids and telescope logs so client-side events can be correlated with server-side request handling.

Tests

photon_test.go
Valid payloads are accepted, invalid core payloads are rejected, and handler behavior stays tied to core-owned metric contracts.

telemetry-prism

telemetry/prism

Client-side structured log ingestion: it validates and sanitizes a log payload the browser sends (message, stack, URL, tags, …) before it is recorded server-side.

Client logs are attacker-influenced free text: a malicious browser can send arbitrarily large fields and arbitrary bytes (including invalid UTF-8). prism both validates the payload against limits and sanitizes it — truncating each field to its cap and coercing it to valid UTF-8 — so what gets stored is always bounded and well-formed.

Core concepts

UTF-8-safe truncation
TruncateUTF8 coerces the input to valid UTF-8, then backs the cut point up to a rune boundary so the result is never a half-multibyte sequence (a naive s[:n] could slice a 3-byte € in half and break the JSON log encoder). Result is always valid UTF-8 and ≤ maxBytes.
Sanitize every field
SanitizePayload truncates message/stack/URL/component/user-agent/trace-id and each tag key+value to their core-owned caps (BUG-218).
Validate the contract
ValidatePayload enforces the payload limits including the trace-id length (BUG-219); Primitive Exchange performs size-capped strict JSON ingress before prism sanitizes and validates.
Maintainer note
TruncateUTF8 assumes a non-negative maxBytes (its only callers pass positive core constants); a negative value would reach s[:maxBytes] and panic — see the tracker finding for the suggested guard.

System fit

  • prism is the typed contract behind the client-log ingestion endpoint.
  • Depends only on core (payload type, field caps, log level, error identities) and the standard library; sanitization is pure.

Tests

already saturated
prism_test: ValidatePayload across valid/invalid shapes, TruncateUTF8 multibyte-boundary cases (é/€/𝄞 cut points must not split a rune) + result-is-always-valid-UTF-8, SanitizePayload per-field truncation, tag key/value truncation (BUG-218), oversized-trace-id rejection (BUG-219), and fuzz targets for both ValidatePayload and TruncateUTF8. Needed no new test for its real (non-negative) domain.

Service workers

service-relay

service/relay

The outbox delivery worker: it pulls pending notification intents (emails, SMS) out of the durable outbox and actually sends them, marking each one sent or failed.

Request handlers don't send email inline — they record an intent in the durable outbox inside the same transaction as the business change (the transactional outbox pattern), and relay drains it later. Delivery is at-least-once: retried until it sticks. relay is the sole drainer so deliveries can't be duplicated.

Core concepts

The drain loop
handleDrain loads up to RelayMaxBatch pending intents and, per intent: skip+fail if the channel's sender is unconfigured, else deliver via the right sender, recording failed (typed, length-capped message) on error or sent on success. Result is DrainResult{Processed, Succeeded, Failed} with Succeeded+Failed==Processed.
Poison-message isolation (crown jewel)
A delivery failure on one intent is recorded and the loop keeps going — one bad recipient can never wedge the queue behind it. Only a store (persistence) error aborts the batch; a cancelled context stops it cleanly (BUG-057).
At-least-once via MarkSent retry
After a successful send, MarkSent is retried with bounded backoff (BUG-659): if the send succeeded but MarkSent failed, the intent would stay pending and be delivered again next drain (BUG-054/055). relay is the only outbox drain worker (BUG-695) so notify can't double-deliver.
Typed rendering
Each email type renders subject+body from core-owned copy and a typed action URL (e.g. the password-reset landing link), with a company-name fallback body (BUG-815) and no runtime template cache (BUG-564).

System fit

  • relay is a ceremony-registered worker run by the webapp binary (and reachable via the worker route).
  • Depends on core/core/outbox (intent + retry contracts), store (outbox queries), emailrender/bridge (rendering), and the injected email/SMS senders.
  • It is the consumer side of every outbox.Enqueue in the system.

Tests

poison_isolation_hostile_test.go
The poison-message isolation crown jewel: a real deliver() error in the first/middle/last slot of a batch is recorded failed while both other intents are still delivered and marked sent.
relay_test.go
Drain delivery routing, single-intent email failure, no-sender/no-store gates, max-batch + count invariant, MarkSent-failure-non-fatal (BUG-054), pre-cancelled context, Validate/boundary buckets, and the formatting/truncation helpers.
regression / render matrix / action
BUG-695 no-duplicate-delivery, every routed auth/member email rendering its typed landing link, action-origin correctness, and email-type→channel routing.

service-projection

service/projection

The event projection worker: it replays the append-only ledger of events into a queryable read model, keeping a cursor of how far it has gotten.

The ledger is the source of truth but is an append-only event log — terrible to query. projection walks it in order and applies each event to a read model (a normal table shaped for reads). This is the read side of CQRS: write to the log, read from the projection; the read model is eventually consistent, lagging the ledger by the time since the last drain.

Core concepts

The replay loop
One drain loads the cursor, reads a batch after it with a one-entry overlap (so a late lower-seq entry is retried, BUG-738) trimmed to the contiguous run, applies each entry in order to the sink (serialized — the sink isn't concurrent-safe), and persists the cursor only after the whole batch succeeds.
No-skip on failure (crown jewel)
If Apply fails on an entry, the loop returns immediately: the cursor is NOT advanced and entries after the failed one are NOT applied. Next drain re-reads from the same cursor, so the failed entry is retried, never skipped — a transient sink error can't silently drop an event from the read model.
Idempotent apply
Because the cursor advances only after a full batch, a batch that fails partway is re-read in full next time; the user sink is idempotent (update-first upsert, delete-missing = success) so re-applying already-applied entries is harmless (BUG-696).
CAS cursor
The cursor advances via compare-and-swap (CASProjectionCursor) expecting the current value and setting the next, so two projection runs can't both move it; the sink Validates before every apply and refuses a corrupted receiver.

System fit

  • projection is a ceremony-registered worker run by the webapp binary.
  • Depends on core/core/ledger (entry + cursor contracts), core/user (read-model type), and store (ledger reader, cursor CAS, users store).
  • It is the read-side complement to the ledger that ledgerwriter appends to.

Tests

cursor_safety_hostile_test.go
The correctness crown jewel: an Apply failure on the first/middle/last entry never advances the cursor and never applies the entries after it; a subsequent run re-reads from the pinned cursor and applies the whole contiguous batch (failed entry retried, nothing skipped).
projection_test.go
Happy-path drains (single/contiguous batches), cursor/persist boundary buckets, user-sink create/update/delete mapping, apply-validates-before-every-apply, corrupted-receiver + nil-ledger gates, fail-closed register body.
projection_fuzz / contracts
Fuzz targets for event deserialization, sequence progression, nil-ledger; plus error-identity distinctness/ratchet, Validate gates, ceremony details.

service-policy

service/policy

The RBAC authorization worker: it answers 'what role does this user have?' on the hot path, backed by a short-lived cache so every request doesn't hit the database.

core/policy is the pure decision table (given role + action, may they?). service/policy is the worker that fetches the role from the store and caches it, then feeds it to that decision. One decides; the other looks up and caches — with TTL, singleflight, and bounded eviction.

Core concepts

Cached CheckRole
Cache hit (fresh within TTL) returns immediately; a miss enters the singleflight for that user id — the leader loads from the store and caches only if the generation is unchanged (BUG-035), while waiters block on the leader's result selecting on their own context (no goroutine leak on cancellation, BUG-424/474).
Fail-closed, no poisoning (crown jewel)
If the store load errors, CheckRole returns the error — never a default/allow role — and does NOT cache the failure; the singleflight key is forgotten so the next call retries. A transient blip neither locks a user out for the whole TTL nor poisons the cache, and every concurrent waiter on a failing load gets the error (no false allow leaks).
Singleflight + bounded
Concurrent misses for the same user collapse to one store load (BUG-171); the cache is capacity-bounded so it can't grow without limit.
HandleRefresh
POST /worker/policy (Cloud Tasks/cron) bumps the generation and refreshes the cache so role changes (a demotion) take effect promptly instead of waiting out every TTL (BUG-013); refuses a nil users store and fails closed without the worker middleware.

System fit

  • service/policy is a ceremony-registered worker used by the admin/api/webapp binaries to authorize requests.
  • Depends on core/core/policy (role-check contracts + error identities), core/user (role type), and a UserFinder (the store).
  • It is the lookup layer in front of core/policy's decision table.

Tests

failclosed_cache_hostile_test.go
The fail-closed cache crown jewel: a failed store load returns the error (not an allow) and is not cached (next call re-loads and succeeds; FindByID called twice), and a concurrent burst of failing loads fails every waiter without poisoning the cache (recovery then succeeds).
policy_test.go
CheckRole valid/error/boundary, cache-hit serves cached, eviction bounded by capacity, stale-after-demotion (BUG-013) and stale-after-refresh (BUG-035), nil-users → ErrForbidden, zero-ULID rejected, HandleRefresh valid/error + cache-size, ceremony/routes, with/without worker-MW fail-closed registration.
regression / contracts
Cache TTL expiry (BUG-161), singleflight single-load on concurrent miss (BUG-171), scoped backend from context (BUG-279), waiter cancellation (BUG-474), goroutine-leak freedom (BUG-424, goleak), error-identity ratchet/distinctness, Validate gates.

service-counter

service/counter

The ledger aggregate counter worker: it walks the ledger of events and maintains the admin dashboard's running totals (e.g. TotalUsers), persisting a cursor so it never recounts.

Computing 'how many users?' by scanning every row on each dashboard load is wasteful when the ledger already records every UserSignedUp. counter keeps a small admin.Stats aggregate up to date incrementally: each drain reads the new entries and bumps the totals. It's a sibling of projection (both replay the ledger forward from a cursor) but maintains counts rather than a full read model.

Core concepts

The counting fold (crown jewel)
applyEntries folds a batch into the aggregate with three rules: skip entries at/below the cursor (overlap-read dedup, no double-count), count only what should be counted (only TypeUserSignedUp increments TotalUsers — counting a sign-in as a new user would inflate the metric), and always advance LastSeq for every processed entry (a non-counted event that didn't advance the cursor would stall the worker).
No double-count across restarts
Stats (cursor+totals) is persisted and LastSeq recovered on restart so a cold start doesn't recount (BUG-017); reading from the persisted cursor avoids recount (BUG-431); a missing stats doc triggers initial backfill (BUG-484); a recovered cursor must not skip when stats lag the ledger (BUG-063).

System fit

  • counter is a ceremony-registered worker run by the webapp binary, exposing POST /worker/count.
  • Depends on core/core/ledger (entry/type contracts), core/admin (the Stats aggregate), and store (ledger reader + stats persistence).
  • It feeds the admin dashboard's totals.

Tests

event_type_matrix_hostile_test.go
The counting-contract crown jewel: an exhaustive sweep of every valid ledger type proving only UserSignedUp increments TotalUsers while every type still advances the cursor; a mixed batch counts exactly the signups and advances past the whole batch; and overlap re-reads (entries at/below the cursor) are skipped so a re-delivered signup can't double-count.
counter_test.go
HandleCount valid/error/boundary, signup→TotalUsers (BUG-314), persist-LastSeq-avoids-recount (BUG-431), skip-late-entries (BUG-739), cold-restart-no-double-count (BUG-017), missing-stats backfill (BUG-484), recover-without-skip (BUG-063), sequence tracking, data-race guard (BUG-031).
contracts / boundary / regression
Identity/wrapping, Validate gates, fail-closed typed handler errors, read-cursor + contiguous-entries boundary buckets, exactly-one-route contract.

service-hasher

service/hasher

The chain-hash verification worker: it re-walks the ledger and recomputes each entry's chain hash to prove the log hasn't been tampered with.

Each ledger entry stores a chain hash H(previous hash || this entry's canonical bytes), so changing or deleting any past entry breaks every hash after it — like a blockchain. But a stored hash is only evidence if someone checks it. hasher is that checker: a background worker that periodically recomputes the chain and reports any break.

Core concepts

The verification pass (crown jewel)
Cold-start init establishes prevHash from the persisted cursor's entry; then for each entry in order: a gap check (Seq must be exactly lastSeq+1, else Broken + stop so the missing seq is retried not skipped, BUG-740), advance the cursor even on hash failure (BUG-603), recompute H(prevHash||canonical) and compare to the stored hash (empty/wrong-algorithm/different → Broken). A mismatch records the break but continues (following the stored chain, BUG-736); only a gap stops. OK is true only if nothing broke.
Threats caught
Modification (payload edit changes canonical bytes → hash mismatch), deletion (sequence gap + broken following hashes), forgery (inconsistent hash), and algorithm-downgrade/empty-hash (treated broken not valid, BUG-016/031).
Continuous chain
prevHash is persisted across invocations so multi-batch verification is one continuous chain (BUG-054); cold start with lastSeq>0 initializes prevHash from the ledger rather than genesis (BUG-138).

System fit

  • hasher is a ceremony-registered worker run by the webapp binary, exposing POST /worker/hash.
  • Depends on core/core/ledger (the entry + HashEntry canonical-hash contract and genesis) and store (the ledger reader).
  • It is the auditor of the chain that ledgerwriter writes.

Tests

already saturated
hasher_test: clean chains of 1/2/5/10/maxVerify verify fully valid; wrong-algorithm + empty-chain-hash rejected (BUG-016/031); advance-from-tampered + mismatch-follows-stored-chain (BUG-034/735/736); sequence-gap stop (BUG-740); prev-hash persisted (BUG-033); concurrent-no-panic, MaxInt64 seq, idempotent-empty, alloc-budget, invalid-hex-retains-prev. regression: prev-hash persistence/genesis (BUG-054), cold-start recovery + error bubbling (BUG-138/473/547), hash-error advances cursor without cascading (BUG-603/162). contracts/race: identity, Validate, VerifyResult boundary buckets, immutable-lastSeq guard, fail-closed register, data race. Needed no new test.

service-anchorer

service/anchorer

The ledger anchor status worker: it publishes the ledger's current anchor — the latest entry's sequence number and chain hash — as a single checkpoint of where the tamper-evident log stands.

An anchor is a snapshot of the ledger's tip: (latest Seq, that entry's chain hash). Publishing it serves monitoring (how far has the ledger progressed?) and tamper-evidence (a recorded anchor compared against the live ledger reveals a rewrite). anchorer only reports the tip; recomputing/verifying the whole chain is hasher's job.

Core concepts

The anchor pass
handleAnchor fails closed if invalid or the ledger isn't configured (ErrForbidden), reads the last entry, and returns {Seq, ChainHash, OK:true} — or for an empty ledger AnchorResult{OK:true} with zero seq and empty hash (an empty log legitimately has no anchor). The only result validation is Seq>=0; an empty chain hash is allowed for the empty-ledger case.
Reports, doesn't verify
anchorer publishes the tip's stored hash; full chain recomputation/verification is hasher. It's the lightweight 'where's the tip?' companion.

System fit

  • anchorer is a ceremony-registered worker run by the webapp binary, exposing POST /worker/anchor.
  • Depends on core/core/ledger (entry + error contracts) and store (the ledger reader, via LastEntry).

Tests

already saturated
anchorer_test: HandleAnchor valid (last entry → seq+hash), error (read error wrapped), boundary, empty-ledger {OK:true}, idempotency, concurrent access, context-cancellation. anchorer_fuzz: chain-hash verification, empty ledger, checkpoint validation, nil ledger. contracts: identity/wrapping, string contracts, Validate gates (incl. non-negative Seq), fail-closed typed handler errors, ceremony metadata/desc/callees, exactly-one-route. Needed no new test.

service-health

service/health

The readiness/liveness probe surface: it exposes the /health, /status, /ready, and /live endpoints that load balancers and orchestrators poll to decide whether this instance should receive traffic.

A platform needs to know two things about a process: is it alive (don't kill it) and is it ready (send it requests). It learns this by polling probe endpoints. Every binary — website, api, admin, webapp — mounts health, which is why it's the one service/* package imported by all four.

Core concepts

Liveness vs readiness
/live: is the process running at all (a failing liveness probe restarts the instance); /ready: can it serve right now with dependencies up (a failing readiness probe takes it out of rotation without killing it).
Thin wrapper that fails closed
health.Monitor embeds api.Monitor (which holds the real probe logic). This package constructs a validated monitor (New panics at boot on an invalid one), mounts the four routes, and gates each handler: a nil/missing embedded monitor returns 500 'monitor unavailable' BEFORE delegating, so corrupted DI wiring fails closed (a probe with no monitor must not report healthy).

System fit

  • health is a ceremony-registered route surface (CompPulse) mounted by all four services.
  • Depends on api (Monitor/Prober + probe route definitions), core (error identities/messages), and switchboard (route mounting); its dependency for the actual check is the store (CompStore).
  • The actual probe behavior — store checks, ready-vs-live semantics — is owned and tested in api.Monitor.

Tests

already saturated
health_test: String renders the configured monitor via the health format, Register mounts every route + passes a valid monitor, Routes returns exactly the public probe routes, Validate gates production inputs. contracts_test: protocol boundary buckets, the nil-handler gate swept across all four handlers (health/status/ready/live each return 500 monitor-unavailable when the embedded monitor is missing), format-verb/string contracts. Thin delegation layer — needed no new test.

service-notify

service/notify

The notification delivery orchestration worker: it drains pending outbox intents, routes each to the sender for its channel (email/SMS), sends it, and marks it sent or failed.

notify and relay are two implementations of the same job (drain the outbox and send). In the current webapp relay is the SOLE outbox drain worker — running both would double-deliver — so notify is deliberately not wired into the webapp (BUG-695, enforced by sentinel/notify_relay_contract_test.go). notify is kept as a fully tested, core-governed alternative whose distinguishing feature is explicit sender routing; for outbox delivery in the running system, use relay.

Core concepts

Retained, not the webapp drainer
notify is ceremony-registered (CompNotify) but not mounted in the running webapp (relay is). It's the routing-centric sibling of relay, kept (not deleted) with full tests and core-owned contracts.
Sender routing
RouteFor(intent.Type) picks the channel's sender via the single authoritative outbox channel contract, so routing can't drift from the type→channel mapping; an unknown type or unconfigured channel marks the intent failed.
Poison isolation (crown jewel)
The process loop routes+sends each intent; a send failure (or unconfigured sender) marks that intent Skipped (with a length-capped error) and the loop continues — one bad recipient can't wedge the batch. Counters always satisfy Routed+Skipped==Processed.

System fit

  • notify is a ceremony-registered worker (CompNotify) exposing a process route, but not mounted in the running webapp (relay is the sole drainer).
  • Depends on core/core/outbox (intent + channel contracts, error identities), store (outbox queries), and the injected email/SMS senders.
  • All its identities/formats/strings/limits are core-owned (enforced by sentinel/notify_relay_contract_test.go).

Tests

poison_isolation_hostile_test.go
The orchestration poison-isolation crown jewel: a real send failure in the first/middle/last slot of a batch is marked Skipped while both other intents are still routed and sent (Processed=3, Routed=2, Skipped=1; one MarkFailed).
notify_test.go
HandleProcess valid/error/boundary, email/SMS-type routing, unknown-type-sender-nil → failed, no-email-sender → skips, single sender-error → skips+marks-failed, all-success mixed email+SMS, RouteFor + delegation to the outbox channel contract, senderRoute JSON round-trip, ProcessResult boundary buckets + counter invariant, nil-store gate, fail-closed register, error-message truncation.
fuzz
SenderFor, HandleProcess error truncation, arbitrary intents, and the ceremony descriptor.