The rewrite kept the pre-rewrite claim that leaving site_routes unset restores
the old behaviour exactly. It does not: the HTTP-looking fallback is
reverse-proxied with the original Host and edge forwarding headers
(ARCHITECTURE.md, sowerd Data Flow steps 13-14) and NextProtos is http/1.1-only,
which this same file already calls a deliberate decision. Say only what holds:
fake_site stays the single fallback target.
admin-console.md and site-routing.md still carried the full design and
implementation checklists for work that shipped and was written back to
ARCHITECTURE.md. Keep only what cannot be recovered from the code -- the two
frontend constraints and the site-routing trade-offs -- and point at the
authoritative documents. Drop the plan-to-plan "Supersedes" reference from
admin-persistence-config.md so plans stop sourcing rules from other plans.
security.md gains the rejected directions those rewrites now point at:
wildcard site_routes domains (rejected in validateSiteRoutes, and the SNI
dispatch is an exact-match siteRouter.lookup), additional listen addresses or
configurable 80/443 ports, per-site certificate binding, and domain routing on
plain HTTP.
Review of the anti-probing hardening found no code defects; the fixes
are documentation:
- docs/security.md: authoritative threat model, the three camouflage
layers, unknown-SNI certificate fallback rules (whitelist source,
error surfacing, capability propagation, concrete first entry), the
client_hello fingerprint knob, rejected directions (QUIC, h2) with
evidence, and residual accepted signals.
- Promote the plan doc to a non-authoritative historical record per
AGENTS.md docs rules; index security.md from docs/README.md and
ARCHITECTURE.md.
- config/sowerd.toml: document cert.domains; the first entry answers
unknown-SNI handshakes.
A handshake failure on unknown SNI is an active-probing signal: real
sites present their default vhost certificate. Whitelisted names keep
the plain autocert path with errors surfaced as-is; unknown or empty
SNI falls back to the primary domain's certificate, carrying the
client hello over so autocert derives the same ECDSA/RSA cache key.
The h2-on-fallback companion idea was investigated and rejected:
httputil.ReverseProxy has no RFC 8441 support and x/net gates
extended CONNECT behind GODEBUG only, which would break WingGate WSS.
ARCHITECTURE.md and README.md now describe editable config fields,
in-place restart, rule hit/miss stats, and session persistence; the
admin console plan is marked implemented.
- StateStore persists rule deltas and whitelisted config overrides atomically
- Config display/edit API with optimistic concurrency (revision)
- Traffic charts with time-axis linking across all views
- Split server.go into auth/rules/traffic/config by responsibility
- internal/admin: session-cookie auth server, rule CRUD API, bounded
per-domain traffic stats with pre-parse byte accounting
- cmd/sower: wrap proxied connections and DNS queries into stats, serve the
admin console on its own listener
- web/: Svelte 5 + Vite admin console with shadcn-svelte UI and an advanced
breadcrumb header (section/context dropdowns, favorites), embedded via
go:embed and committed to web/dist
- Makefile: sower build depends on the frontend build (bun); sowerd does not
- docs: ARCHITECTURE, README, and plan updated for the admin surface