Sanitized snapshot of private source 144ca0a9544042b0477ae732c1356cf0b9d62b3f. Add package selection, adoption workflow, and optional Sandwich Hime integration guidance. AI-Assistance: OpenAI Codex assisted documentation, verification, and publication. Signed-off-by: Cole Speelman <crspeelman@gmail.com>
This commit is contained in:
@@ -27,6 +27,16 @@ go get gamertan.com/web@v0.1.0-preview.1
|
|||||||
go mod verify
|
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
|
Canonical source, issues, security policy, and release notes live on
|
||||||
[Gamertan Gitea](https://gitea.speelman.ca/gamertan/web). GitHub is a read-only
|
[Gamertan Gitea](https://gitea.speelman.ca/gamertan/web). GitHub is a read-only
|
||||||
discovery snapshot rather than a second release origin.
|
discovery snapshot rather than a second release origin.
|
||||||
@@ -38,17 +48,34 @@ without turning that portability into a maintained compatibility claim.
|
|||||||
|
|
||||||
## Packages
|
## Packages
|
||||||
|
|
||||||
- `requestmeta`: trusted-proxy resolution, HTTPS/origin metadata, and request IDs.
|
- [`requestmeta`](requestmeta): trusted-proxy resolution, HTTPS/origin metadata,
|
||||||
- `requestlog`: bounded versioned records, middleware, sinks, and private JSONL.
|
and request IDs.
|
||||||
- `websec`: headers, origin checks, CSRF, redirects, body limits, and rate limits.
|
- [`requestlog`](requestlog): bounded versioned records, middleware, sinks, and
|
||||||
- `abuse`: application-classified request abuse with pluggable persistence.
|
private JSONL.
|
||||||
- `auth`, `authhttp`, `authsqlite`: passwords, sessions, permissions, cookies,
|
- [`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.
|
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
|
The copyable starter under `starters/basic` demonstrates the packages without
|
||||||
turning them into a router or template system.
|
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
|
## Security boundary
|
||||||
|
|
||||||
Client addresses are accepted from forwarding headers only when the immediate
|
Client addresses are accepted from forwarding headers only when the immediate
|
||||||
|
|||||||
@@ -2,16 +2,17 @@
|
|||||||
|
|
||||||
# Architecture
|
# Architecture
|
||||||
|
|
||||||
The dependency direction is intentionally one-way:
|
The package dependency direction is intentionally one-way:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
net/http application
|
analytics ──> requestlog ──> requestmeta
|
||||||
-> requestmeta
|
abuse ─────────────────────> requestmeta
|
||||||
-> requestlog / websec / abuse / authhttp
|
authhttp ──> websec ───────> requestmeta
|
||||||
-> auth and analytics interfaces
|
authhttp ──> auth <───────── authsqlite
|
||||||
-> optional authsqlite and JSONL adapters
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
An ordinary `net/http` application composes whichever branches it needs.
|
||||||
|
|
||||||
Packages never own application routes, templates, authorization policy, cache
|
Packages never own application routes, templates, authorization policy, cache
|
||||||
policy, or deployment. Middleware communicates through typed request context.
|
policy, or deployment. Middleware communicates through typed request context.
|
||||||
Storage and reporting surfaces are interfaces so an application can retain its
|
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
|
The package model is developed from explicit threat and data contracts, not by
|
||||||
moving an existing application's internals into a shared directory. See
|
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).
|
||||||
|
|||||||
@@ -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
|
implementations into their binaries. Optional GeoIP enrichment is an interface
|
||||||
only; the base toolkit performs no lookup and adds no GeoIP dependency.
|
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
|
`go.sum`, `go mod verify`, checksum-database verification, vulnerability
|
||||||
scanning, and the public snapshot allowlist are release gates. Binary
|
scanning, and the public snapshot allowlist are release gates. Binary
|
||||||
distributors remain responsible for preserving all applicable upstream notices.
|
distributors remain responsible for preserving all applicable upstream notices.
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
<!-- SPDX-License-Identifier: MPL-2.0 -->
|
||||||
|
|
||||||
|
# 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.
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
<!-- SPDX-License-Identifier: MPL-2.0 -->
|
||||||
|
|
||||||
|
# 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.
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<!-- SPDX-License-Identifier: MPL-2.0 -->
|
||||||
|
|
||||||
|
# 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.
|
||||||
@@ -6,7 +6,7 @@ cd "$root"
|
|||||||
failed=0
|
failed=0
|
||||||
while IFS= read -r -d '' file; do
|
while IFS= read -r -d '' file; do
|
||||||
case $file in
|
case $file in
|
||||||
./.git/*|./LICENSES/*|./go.sum) continue ;;
|
./.git|./.git/*|./LICENSES/*|./go.sum) continue ;;
|
||||||
./starters/*|./examples/*) expected=0BSD ;;
|
./starters/*|./examples/*) expected=0BSD ;;
|
||||||
./scripts/*|./.gitea/*|./services/*) expected=AGPL-3.0-only ;;
|
./scripts/*|./.gitea/*|./services/*) expected=AGPL-3.0-only ;;
|
||||||
*) expected=MPL-2.0 ;;
|
*) expected=MPL-2.0 ;;
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ authsqlite/store_test.go
|
|||||||
docs/ADOPTION.md
|
docs/ADOPTION.md
|
||||||
docs/ARCHITECTURE.md
|
docs/ARCHITECTURE.md
|
||||||
docs/DEPENDENCIES.md
|
docs/DEPENDENCIES.md
|
||||||
|
docs/GETTING_STARTED.md
|
||||||
|
docs/MODULES.md
|
||||||
docs/PUBLIC_SNAPSHOT.md
|
docs/PUBLIC_SNAPSHOT.md
|
||||||
|
docs/SANDWICH_HIME.md
|
||||||
docs/SERVICES_ROADMAP.md
|
docs/SERVICES_ROADMAP.md
|
||||||
docs/THREAT_MODEL.md
|
docs/THREAT_MODEL.md
|
||||||
go.mod
|
go.mod
|
||||||
|
|||||||
+1
-1
@@ -10,5 +10,5 @@ go test -race ./...
|
|||||||
go vet ./...
|
go vet ./...
|
||||||
build_dir=$(mktemp -d)
|
build_dir=$(mktemp -d)
|
||||||
trap 'rm -rf "$build_dir"' EXIT
|
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
|
git diff --check
|
||||||
|
|||||||
@@ -15,3 +15,8 @@ go run ./starters/basic -listen 127.0.0.1:8080
|
|||||||
|
|
||||||
Production configuration and secrets belong outside the source tree. This
|
Production configuration and secrets belong outside the source tree. This
|
||||||
starter does not load `.env` files automatically.
|
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).
|
||||||
|
|||||||
Reference in New Issue
Block a user