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
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
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 ;;
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user