diff --git a/README.md b/README.md index 332777c..e19f9c8 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,16 @@ go get gamertan.com/web@v0.1.0-preview.1 go mod verify ``` +An application may also name the first package it intends to adopt: + +```bash +go get gamertan.com/web/requestmeta@v0.1.0-preview.1 +``` + +The version belongs to the `gamertan.com/web` module. Go compiles and links +only the packages the application imports. See the [getting-started guide](docs/GETTING_STARTED.md) +and [module-boundary policy](docs/MODULES.md) before choosing a first slice. + Canonical source, issues, security policy, and release notes live on [Gamertan Gitea](https://gitea.speelman.ca/gamertan/web). GitHub is a read-only discovery snapshot rather than a second release origin. @@ -38,17 +48,34 @@ without turning that portability into a maintained compatibility claim. ## Packages -- `requestmeta`: trusted-proxy resolution, HTTPS/origin metadata, and request IDs. -- `requestlog`: bounded versioned records, middleware, sinks, and private JSONL. -- `websec`: headers, origin checks, CSRF, redirects, body limits, and rate limits. -- `abuse`: application-classified request abuse with pluggable persistence. -- `auth`, `authhttp`, `authsqlite`: passwords, sessions, permissions, cookies, +- [`requestmeta`](requestmeta): trusted-proxy resolution, HTTPS/origin metadata, + and request IDs. +- [`requestlog`](requestlog): bounded versioned records, middleware, sinks, and + private JSONL. +- [`websec`](websec): headers, origin checks, CSRF, redirects, body limits, and + rate limits. +- [`abuse`](abuse): application-classified request abuse with pluggable persistence. +- [`auth`](auth), [`authhttp`](authhttp), and [`authsqlite`](authsqlite): + passwords, sessions, permissions, cookies, and a no-CGO SQLite adapter. -- `analytics`: safe and sensitive aggregate projections over request records. +- [`analytics`](analytics): safe and sensitive aggregate projections over request + records. The copyable starter under `starters/basic` demonstrates the packages without turning them into a router or template system. +## HTML and templates + +Web Foundations deliberately does not provide a template language. Sandwich +Hime is the preferred companion for Gamertan applications that want HTML-first, +typed, ahead-of-time Go templates. The two projects remain independently +usable: this module does not import the `sando` runtime, and Sandwich Hime does +not own middleware, authentication, logging, routing, or deployment. + +See [HTML with Sandwich Hime](docs/SANDWICH_HIME.md), then follow the official +[first site tutorial](https://sandwichhime.com/docs/tutorial/) and +[application integration tutorial](https://sandwichhime.com/docs/tutorial/application/). + ## Security boundary Client addresses are accepted from forwarding headers only when the immediate diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 9652e21..3c82a85 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -2,16 +2,17 @@ # Architecture -The dependency direction is intentionally one-way: +The package dependency direction is intentionally one-way: ```text -net/http application - -> requestmeta - -> requestlog / websec / abuse / authhttp - -> auth and analytics interfaces - -> optional authsqlite and JSONL adapters +analytics ──> requestlog ──> requestmeta +abuse ─────────────────────> requestmeta +authhttp ──> websec ───────> requestmeta +authhttp ──> auth <───────── authsqlite ``` +An ordinary `net/http` application composes whichever branches it needs. + Packages never own application routes, templates, authorization policy, cache policy, or deployment. Middleware communicates through typed request context. Storage and reporting surfaces are interfaces so an application can retain its @@ -20,4 +21,5 @@ time. The package model is developed from explicit threat and data contracts, not by moving an existing application's internals into a shared directory. See -[ADOPTION.md](ADOPTION.md). +[ADOPTION.md](ADOPTION.md), [GETTING_STARTED.md](GETTING_STARTED.md), and the +[module-boundary policy](MODULES.md). diff --git a/docs/DEPENDENCIES.md b/docs/DEPENDENCIES.md index 251c059..352ff38 100644 --- a/docs/DEPENDENCIES.md +++ b/docs/DEPENDENCIES.md @@ -13,6 +13,13 @@ Applications that do not import `auth` or `authsqlite` do not link those implementations into their binaries. Optional GeoIP enrichment is an interface only; the base toolkit performs no lookup and adds no GeoIP dependency. +All packages currently share one Go module, so these requirements remain +visible in the module graph even when an application imports only +`requestmeta`. Go still avoids compiling or linking unused packages. A future +nested module may isolate a heavyweight adapter such as `authsqlite` when its +independent dependency and release lifecycle justify the additional tags, +vanity metadata, and CI. See [MODULES.md](MODULES.md). + `go.sum`, `go mod verify`, checksum-database verification, vulnerability scanning, and the public snapshot allowlist are release gates. Binary distributors remain responsible for preserving all applicable upstream notices. diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md new file mode 100644 index 0000000..48cb957 --- /dev/null +++ b/docs/GETTING_STARTED.md @@ -0,0 +1,80 @@ + + +# Getting started + +Gamertan Web Foundations is adopted one boundary at a time. Start with the +smallest package that solves a problem the application actually has; do not +install an imagined framework lifecycle around it. + +## Choose a first slice + +| Application need | Begin with | What remains application-owned | +| --- | --- | --- | +| Request IDs and trustworthy client addresses | `requestmeta` | Proxy configuration and operational logs | +| Bounded structured request evidence | `requestmeta`, `requestlog` | Route names, sensitive-field policy, rotation, retention, and access | +| Browser and HTTP safety primitives | `requestmeta`, `websec` | Exact CSP, route authorization, and response policy | +| Persistent request-abuse decisions | `requestmeta`, `abuse` | Route classification, storage, appeals, and operator policy | +| Users, credentials, permissions, and sessions | `auth` | Roles, permissions, login UX, and account policy | +| Secure browser cookies around `auth` | `authhttp` | Login routes, redirects, pages, and authorization decisions | +| SQLite persistence for `auth` | `authsqlite` | Database placement, backup, migration approval, and recovery | +| Aggregate projections over request records | `analytics` | Collection policy, access control, report UI, and retention | + +The packages are ordinary Go imports. Pin the current preview and verify its +module checksum: + +```bash +go get gamertan.com/web/requestmeta@v0.1.0-preview.1 +go mod verify +``` + +## Preserve middleware order + +Packages that consume request metadata must run inside the resolver. Build the +handler from the application outward; the final resolver assignment becomes +the first middleware to receive a request: + +```go +var handler http.Handler = router +handler = requestlog.Middleware(sink, logPolicy)(handler) +handler = websec.Headers(headerPolicy)(handler) +handler = resolver.Middleware(handler) +``` + +The complete, copyable composition is in [`starters/basic`](../starters/basic). +It binds to loopback, shuts down gracefully, and keeps request logging optional. + +Configure trusted proxy networks narrowly. A forwarding header is not evidence +by itself; it becomes usable only when the immediate peer and skipped proxy +hops satisfy the resolver's trust policy. Metadata, authentication, or storage +failures that affect security decisions should stop the request rather than +quietly changing identity or policy. + +## Add HTML without merging responsibilities + +Handlers should convert request and service state into typed display data. +They may then render those values with any HTML system. Gamertan's preferred +companion is [Sandwich Hime](SANDWICH_HIME.md), whose generated components keep +templates typed while leaving this middleware stack and the `net/http` +application in control. + +## Verify the application boundary + +After adopting a package: + +```bash +go mod verify +go test ./... +go test -race ./... +go vet ./... +go build ./... +``` + +Test the composed handler with `httptest`, not only the package in isolation. +Include a normal request, malformed or spoofed metadata, a downstream failure, +and the application's intended response headers. Existing applications should +follow the differential and rollback sequence in [ADOPTION.md](ADOPTION.md). + +Deeper tutorials for accounts, analytics, and persistent abuse policy will be +written after multiple application migrations have validated those seams. The +preview documentation describes demonstrated contracts rather than prescribing +an unfinished application framework. diff --git a/docs/MODULES.md b/docs/MODULES.md new file mode 100644 index 0000000..8648bfc --- /dev/null +++ b/docs/MODULES.md @@ -0,0 +1,72 @@ + + +# Packages, modules, and repositories + +These boundaries solve different problems: + +- a **package** owns one Go responsibility and import path; +- a **module** owns dependency selection and semantic versions; and +- a **repository** owns contribution, security, and release operations. + +The first preview uses one repository and one module, `gamertan.com/web`, with +several independently importable packages. An application may write: + +```go +import "gamertan.com/web/requestmeta" +``` + +and request the containing module at an exact version: + +```bash +go get gamertan.com/web/requestmeta@v0.1.0-preview.1 +``` + +Only imported packages are compiled and linked. The packages nevertheless +share the module's version and dependency graph. + +## Why not one repository per package? + +Separate repositories would multiply release credentials, security updates, +vanity-import records, tags, CI, issue tracking, and coordinated API changes. +A focused pull request can already change and test one package directory. A +repository boundary is reserved for software with an independently operated +lifecycle, such as a future standalone `authd` service. + +## When a nested module is justified + +A package may become a nested module inside this repository when all of these +are true: + +1. it introduces materially heavier or different dependencies; +2. consumers can usefully version it independently; +3. its API boundary has survived real application adoption; and +4. separate tags, release ordering, vanity metadata, and CI are less costly + than keeping it in the root module. + +`authsqlite` is the clearest current candidate because it carries the optional +SQLite implementation and its transitive module graph. A future split could +retain the import path `gamertan.com/web/authsqlite` while giving that directory +its own `go.mod` and tags such as `authsqlite/v0.1.0-preview.1`. + +Do not split merely to make an architecture diagram look modular. Package +interfaces provide source-level modularity today; modules are introduced only +for an independent dependency and release lifecycle. + +## Session boundaries + +Authenticated sessions currently belong to three deliberate packages: + +- `auth` owns opaque token creation, digest-backed session lookup, revocation, + and the storage interface; +- `authhttp` binds those sessions to secure browser cookies and request + context; and +- `authsqlite` persists the storage contract. + +A separate `session` package would be appropriate only for a genuinely +identity-neutral need, such as anonymous application sessions with no user, +role, or credential semantics. It should not duplicate `auth` under a more +general name. + +This policy may evolve before a stable release. Any split must include a +migration guide and preserve already published versions at their original +module coordinates. diff --git a/docs/SANDWICH_HIME.md b/docs/SANDWICH_HIME.md new file mode 100644 index 0000000..4365417 --- /dev/null +++ b/docs/SANDWICH_HIME.md @@ -0,0 +1,71 @@ + + +# HTML with Sandwich Hime + +Gamertan Web Foundations owns reusable web-application boundaries; it does not +own HTML or a template language. [Sandwich Hime](https://sandwichhime.com/) is +the preferred companion for Gamertan applications that want HTML-first, +ahead-of-time templates with typed Go composition. + +The relationship is intentionally optional: + +| Application responsibility | Owner | +| --- | --- | +| Request identity, logging, security primitives, sessions, and analytics | Web Foundations packages selected by the application | +| Routing, authorization decisions, status, headers, caching, and deployment | The application | +| Visible HTML and typed component composition | Authored `.sando` templates | +| Template parsing, contextual analysis, and Go generation | Hime-san during development or CI | +| Rendering generated components | The small `sando` runtime in production | + +Web Foundations does not import Sandwich Hime. Sandwich Hime does not import +Web Foundations. An application chooses both and provides the seam between +them. + +## Request flow + +```text +request + -> requestmeta / selected middleware + -> application router and handler + -> typed view data + -> generated Sandwich Hime component + -> buffered sando.Render + -> application-owned HTTP response +``` + +Buffer the component before committing a successful response so a rendering +error can still become a clean application error: + +```go +func renderHTML(response http.ResponseWriter, request *http.Request, status int, component sando.Component) { + var output bytes.Buffer + if err := sando.Render(request.Context(), &output, component); err != nil { + log.Printf("render page: %v", err) + response.Header().Set("Cache-Control", "no-store") + http.Error(response, "could not render page", http.StatusInternalServerError) + return + } + response.Header().Set("Content-Type", "text/html; charset=utf-8") + response.WriteHeader(status) + _, _ = response.Write(output.Bytes()) +} +``` + +The handler—not the template—should interpret request metadata, principals, +permissions, analytics, or storage errors. It passes only the resulting typed +display data into the component. Templates should not acquire an implicit +request global or turn middleware context into an inheritance framework. + +Handwritten `sando.Component` implementations and `Trust*` values are explicit +trusted-output capabilities. Keep them conspicuous and review them separately +from ordinary untrusted values. + +## Continue with the official lessons + +- [Build a component, page, and small site](https://sandwichhime.com/docs/tutorial/). +- [Follow a request through a larger Go application](https://sandwichhime.com/docs/tutorial/application/). +- [Review the Sandwich Hime security boundary](https://sandwichhime.com/docs/security/). + +Those tutorials own the template syntax and compiler workflow. This repository +documents only the application seam so the two projects do not drift into a +single mandatory framework. diff --git a/scripts/check-licenses.sh b/scripts/check-licenses.sh index 8995fb9..229fe31 100755 --- a/scripts/check-licenses.sh +++ b/scripts/check-licenses.sh @@ -6,7 +6,7 @@ cd "$root" failed=0 while IFS= read -r -d '' file; do case $file in - ./.git/*|./LICENSES/*|./go.sum) continue ;; + ./.git|./.git/*|./LICENSES/*|./go.sum) continue ;; ./starters/*|./examples/*) expected=0BSD ;; ./scripts/*|./.gitea/*|./services/*) expected=AGPL-3.0-only ;; *) expected=MPL-2.0 ;; diff --git a/scripts/public-snapshot.allow b/scripts/public-snapshot.allow index 3fd9c99..c0d4b12 100644 --- a/scripts/public-snapshot.allow +++ b/scripts/public-snapshot.allow @@ -30,7 +30,10 @@ authsqlite/store_test.go docs/ADOPTION.md docs/ARCHITECTURE.md docs/DEPENDENCIES.md +docs/GETTING_STARTED.md +docs/MODULES.md docs/PUBLIC_SNAPSHOT.md +docs/SANDWICH_HIME.md docs/SERVICES_ROADMAP.md docs/THREAT_MODEL.md go.mod diff --git a/scripts/verify.sh b/scripts/verify.sh index d14d937..c879544 100755 --- a/scripts/verify.sh +++ b/scripts/verify.sh @@ -10,5 +10,5 @@ go test -race ./... go vet ./... build_dir=$(mktemp -d) trap 'rm -rf "$build_dir"' EXIT -go build -trimpath -o "$build_dir/basic" ./starters/basic +go build -buildvcs=false -trimpath -o "$build_dir/basic" ./starters/basic git diff --check diff --git a/starters/basic/README.md b/starters/basic/README.md index 6706c5d..116af96 100644 --- a/starters/basic/README.md +++ b/starters/basic/README.md @@ -15,3 +15,8 @@ go run ./starters/basic -listen 127.0.0.1:8080 Production configuration and secrets belong outside the source tree. This starter does not load `.env` files automatically. + +Continue with the [getting-started guide](../../docs/GETTING_STARTED.md) for +package selection and middleware order. To replace the plain-text response +with typed HTML without changing ownership of the server, follow +[HTML with Sandwich Hime](../../docs/SANDWICH_HIME.md).