From 0e8bf2571f0f0d7e4b7d43400556217a4f03d04f Mon Sep 17 00:00:00 2001 From: Paul Payne Date: Thu, 2 Jul 2026 13:56:41 -0700 Subject: [PATCH] docs: fix stale TLS/routing references (audit sweep) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cross-checked every doc against the code and fixed the stragglers the apply/gateway/subdomain refactors left behind: - Retired `tls: internal` mode: dropped the misleading reference in registry.md and the stale comment in config.py GatewayConfig (only off|acme exist; the dns-and-tls.md "was removed" note is kept as accurate history). - Subdomain-only routing: fixed "path-based routing" claims in design.md (proxy list + mesh), "path prefix or host" → host, and the "path-prefix routes stay on :port" line in registry.md. - Frontends serve at their subdomain root (VITE_BASE=/), not //: fixed supabase.md (x2), the scaffold.py docstring, the stacks.py build comment, and design.md's "serve prefix baked in" line. README, AGENTS, developing-castle, dns-and-tls audited clean. Verified against code; core 129 / cli 31 pass; ruff clean. --- cli/src/castle_cli/templates/scaffold.py | 2 +- core/src/castle_core/config.py | 6 ++---- core/src/castle_core/stacks.py | 4 ++-- docs/design.md | 12 +++++++----- docs/registry.md | 11 ++++++----- docs/stacks/supabase.md | 4 ++-- docs/tunnel-setup.md | 2 +- 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/cli/src/castle_cli/templates/scaffold.py b/cli/src/castle_cli/templates/scaffold.py index d0420b9..ef4ef44 100644 --- a/cli/src/castle_cli/templates/scaffold.py +++ b/cli/src/castle_cli/templates/scaffold.py @@ -573,7 +573,7 @@ def _scaffold_supabase(project_dir: Path, name: str, description: str) -> None: Produces migrations/ (applied to the substrate by `castle program build`), functions/ (deno edge functions), public/ (static UI served in place at - // by the gateway), and supabase.app.yaml (auth policy + wiring). + . by the gateway), and supabase.app.yaml (auth policy + wiring). The app owns its code and stays repo-durable; only its rows/blobs live on the shared substrate. Each app is isolated in its **own Postgres schema** (the app diff --git a/core/src/castle_core/config.py b/core/src/castle_core/config.py index 9bc6eba..e8839de 100644 --- a/core/src/castle_core/config.py +++ b/core/src/castle_core/config.py @@ -114,10 +114,8 @@ class GatewayConfig: """Gateway configuration.""" port: int = 9000 - # None/"off" → HTTP-only gateway. "internal" → Caddy serves host routes over - # HTTPS with its local CA (browsers get a secure context; trust the root CA). - # "acme" → real Let's Encrypt wildcard cert (*.domain) via a DNS-01 challenge; - # publicly trusted, no CA to install. + # None/"off" → HTTP-only gateway. "acme" → real Let's Encrypt wildcard cert + # (*.domain) via a DNS-01 challenge; publicly trusted, no CA to install. tls: str | None = None # acme mode only: the zone for the wildcard cert and host-route subdomains # (e.g. "civil.payne.io" → host routes become .civil.payne.io). diff --git a/core/src/castle_core/stacks.py b/core/src/castle_core/stacks.py index c5fc0d2..331f3de 100644 --- a/core/src/castle_core/stacks.py +++ b/core/src/castle_core/stacks.py @@ -262,8 +262,8 @@ class ReactViteHandler(StackHandler): async def build(self, name: str, comp: ProgramSpec, root: Path) -> ActionResult: src = _source_dir(comp, root) - # Build against the gateway serve prefix so absolute asset URLs resolve at - # // (vite.config reads VITE_BASE). Removes the hand-tuned-base footgun. + # Build against the gateway serve root so absolute asset URLs resolve at / + # (vite.config reads VITE_BASE=/). Removes the hand-tuned-base footgun. rc, output = await _run( _pnpm("build"), src, env={**_PNPM_ENV, "VITE_BASE": _vite_base(name)} ) diff --git a/docs/design.md b/docs/design.md index 544add1..8b5b031 100644 --- a/docs/design.md +++ b/docs/design.md @@ -114,7 +114,8 @@ Manages running processes using standard Linux infrastructure. **Caddy** handles HTTP routing: - Reverse proxy on port 9000 -- Path-based routing to services (`/api` → port 9020) +- Subdomain routing to services (`.`); the `/api` path is + reserved for the dashboard's own backend in no-domain (HTTP-only) mode - Static file serving for frontends - TLS termination @@ -131,7 +132,7 @@ infrastructure and gets out of the way. Systemd units point to installed binaries (on PATH or in `~/.local/bin/`), not to repo subdirectories. Frontends are the deliberate exception: rather than stage a copy, Caddy serves their built assets **in place** from the repo -(`//`), with the serve prefix baked into the build via `VITE_BASE`. +(`//`) at the root of its own subdomain (`VITE_BASE=/`). ### Registry Layer @@ -258,7 +259,7 @@ programs on a single node and across multiple Castle nodes. **Intra-node coordination:** - Programs find each other through the gateway or direct port access via env - vars. A gateway route maps an address (path prefix or host) to a target of one + vars. A gateway route maps a host address (`.`) to a target of one kind: **proxy** (a local service port), **remote** (a service on another node), or **static** (a built frontend's `dist/`, served as files). The same computed route list drives the Caddyfile, `castle gateway status`, and the @@ -270,8 +271,9 @@ programs on a single node and across multiple Castle nodes. - Each Castle node runs the API, which exposes its program registry. - Nodes discover each other via MQTT retained messages and mDNS/DNS-SD (python-zeroconf) for LAN environments. -- The gateway on each node can proxy to services on other nodes, - preserving path-based routing. Components don't know which node +- The gateway on each node can proxy to services on other nodes via + host-based routing (`.` resolves to the local port or, + via the remote registry, another node). Components don't know which node they're talking to. - MQTT provides pub/sub messaging for events, status, and coordination across nodes. diff --git a/docs/registry.md b/docs/registry.md index e62d29d..61605f8 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -395,8 +395,8 @@ version number"). `gateway.tls` has two values: | `off` (default/unset) | `:` HTTP, `auto_https off` | host matcher on `:` | none | | `acme` | one `*.` `:443` site | matcher inside the wildcard site | **real Let's Encrypt wildcard, no CA install** | -Path-prefix and static routes always stay on the HTTP `:` site — the way to -put a service on HTTPS is to set `proxy: true` (and use acme mode). A node with no public +In `off` mode all routes stay on the HTTP `:` site — the way to put a +service on HTTPS is to set `proxy: true` (and use acme mode). A node with no public domain stays on `off` (plain HTTP; use `localhost`/direct ports for anything that needs a secure context). @@ -413,9 +413,10 @@ the floor once: `net.ipv4.ip_unprivileged_port_start=80` (persist in #### Publicly-trusted HTTPS — `gateway.tls: acme` -A private-CA approach (Caddy's `tls internal`) forces every client device to trust -a custom root — which some platforms (e.g. Android browsers, and Firefox, which -uses its own store) make painful. `acme` mode avoids it entirely: Caddy obtains a +A private-CA approach would force every client device to trust a custom root — +which some platforms (e.g. Android browsers, and Firefox, which uses its own +store) make painful — so Castle doesn't offer one. `acme` mode avoids it entirely: +Caddy obtains a **real Let's Encrypt wildcard cert** (`*.`) via a **DNS-01** challenge, so every browser trusts it with **zero CA install** — while the services stay **internal-only**. diff --git a/docs/stacks/supabase.md b/docs/stacks/supabase.md index ec13212..38d3862 100644 --- a/docs/stacks/supabase.md +++ b/docs/stacks/supabase.md @@ -63,7 +63,7 @@ my-app/ Registered as a program with `build.outputs: [public]` plus a `manager: caddy` deployment (`root: public`, derived **kind: static**), so the -gateway serves `public/` in place at `/my-app/` — no service, no process. +gateway serves `public/` in place at `.` (its own subdomain root) — no service, no process. ## supabase.app.yaml @@ -161,7 +161,7 @@ service itself is likewise at `supabase.`.) See castle program create my-app --stack supabase --description "..." # scaffold + register castle program build my-app # apply unapplied migrations to the substrate castle program test my-app # deno test over functions/ (if deno present) -castle apply # serve the static UI at /my-app/ +castle apply # serve the static UI at . ``` ## Scaffolding diff --git a/docs/tunnel-setup.md b/docs/tunnel-setup.md index a328ee9..b39d618 100644 --- a/docs/tunnel-setup.md +++ b/docs/tunnel-setup.md @@ -143,7 +143,7 @@ Reachability is the easy half. Anything public also needs, per service: ## Notes - Requires `gateway.tls: acme` (the tunnel forwards to the gateway's real-cert - `:443` host sites). On an `off`/`internal` gateway the origin bridge doesn't apply. + `:443` host sites). On an `off` (plain-HTTP) gateway the origin bridge doesn't apply. - Cloudflare terminates TLS at the edge (it can see plaintext). For a no-third-party-in-path variant, the same `public: true` model can drive a self-hosted VPS + WireGuard edge instead — the toggle and generator stay; only the