docs: fix stale TLS/routing references (audit sweep)
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 /<name>/: 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.
This commit is contained in:
@@ -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`),
|
Produces migrations/ (applied to the substrate by `castle program build`),
|
||||||
functions/ (deno edge functions), public/ (static UI served in place at
|
functions/ (deno edge functions), public/ (static UI served in place at
|
||||||
/<name>/ by the gateway), and supabase.app.yaml (auth policy + wiring).
|
<name>.<gateway.domain> 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
|
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
|
shared substrate. Each app is isolated in its **own Postgres schema** (the app
|
||||||
|
|||||||
@@ -114,10 +114,8 @@ class GatewayConfig:
|
|||||||
"""Gateway configuration."""
|
"""Gateway configuration."""
|
||||||
|
|
||||||
port: int = 9000
|
port: int = 9000
|
||||||
# None/"off" → HTTP-only gateway. "internal" → Caddy serves host routes over
|
# None/"off" → HTTP-only gateway. "acme" → real Let's Encrypt wildcard cert
|
||||||
# HTTPS with its local CA (browsers get a secure context; trust the root CA).
|
# (*.domain) via a DNS-01 challenge; publicly trusted, no CA to install.
|
||||||
# "acme" → real Let's Encrypt wildcard cert (*.domain) via a DNS-01 challenge;
|
|
||||||
# publicly trusted, no CA to install.
|
|
||||||
tls: str | None = None
|
tls: str | None = None
|
||||||
# acme mode only: the zone for the wildcard cert and host-route subdomains
|
# acme mode only: the zone for the wildcard cert and host-route subdomains
|
||||||
# (e.g. "civil.payne.io" → host routes become <service>.civil.payne.io).
|
# (e.g. "civil.payne.io" → host routes become <service>.civil.payne.io).
|
||||||
|
|||||||
@@ -262,8 +262,8 @@ class ReactViteHandler(StackHandler):
|
|||||||
|
|
||||||
async def build(self, name: str, comp: ProgramSpec, root: Path) -> ActionResult:
|
async def build(self, name: str, comp: ProgramSpec, root: Path) -> ActionResult:
|
||||||
src = _source_dir(comp, root)
|
src = _source_dir(comp, root)
|
||||||
# Build against the gateway serve prefix so absolute asset URLs resolve at
|
# Build against the gateway serve root so absolute asset URLs resolve at /
|
||||||
# /<name>/ (vite.config reads VITE_BASE). Removes the hand-tuned-base footgun.
|
# (vite.config reads VITE_BASE=/). Removes the hand-tuned-base footgun.
|
||||||
rc, output = await _run(
|
rc, output = await _run(
|
||||||
_pnpm("build"), src, env={**_PNPM_ENV, "VITE_BASE": _vite_base(name)}
|
_pnpm("build"), src, env={**_PNPM_ENV, "VITE_BASE": _vite_base(name)}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -114,7 +114,8 @@ Manages running processes using standard Linux infrastructure.
|
|||||||
|
|
||||||
**Caddy** handles HTTP routing:
|
**Caddy** handles HTTP routing:
|
||||||
- Reverse proxy on port 9000
|
- Reverse proxy on port 9000
|
||||||
- Path-based routing to services (`/api` → port 9020)
|
- Subdomain routing to services (`<service>.<domain>`); the `/api` path is
|
||||||
|
reserved for the dashboard's own backend in no-domain (HTTP-only) mode
|
||||||
- Static file serving for frontends
|
- Static file serving for frontends
|
||||||
- TLS termination
|
- 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/`),
|
Systemd units point to installed binaries (on PATH or in `~/.local/bin/`),
|
||||||
not to repo subdirectories. Frontends are the deliberate exception: rather
|
not to repo subdirectories. Frontends are the deliberate exception: rather
|
||||||
than stage a copy, Caddy serves their built assets **in place** from the repo
|
than stage a copy, Caddy serves their built assets **in place** from the repo
|
||||||
(`<source>/<dist>/`), with the serve prefix baked into the build via `VITE_BASE`.
|
(`<source>/<dist>/`) at the root of its own subdomain (`VITE_BASE=/`).
|
||||||
|
|
||||||
### Registry Layer
|
### Registry Layer
|
||||||
|
|
||||||
@@ -258,7 +259,7 @@ programs on a single node and across multiple Castle nodes.
|
|||||||
|
|
||||||
**Intra-node coordination:**
|
**Intra-node coordination:**
|
||||||
- Programs find each other through the gateway or direct port access via env
|
- 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 (`<name>.<domain>`) to a target of one
|
||||||
kind: **proxy** (a local service port), **remote** (a service on another
|
kind: **proxy** (a local service port), **remote** (a service on another
|
||||||
node), or **static** (a built frontend's `dist/`, served as files). The same
|
node), or **static** (a built frontend's `dist/`, served as files). The same
|
||||||
computed route list drives the Caddyfile, `castle gateway status`, and the
|
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.
|
- Each Castle node runs the API, which exposes its program registry.
|
||||||
- Nodes discover each other via MQTT retained messages and mDNS/DNS-SD
|
- Nodes discover each other via MQTT retained messages and mDNS/DNS-SD
|
||||||
(python-zeroconf) for LAN environments.
|
(python-zeroconf) for LAN environments.
|
||||||
- The gateway on each node can proxy to services on other nodes,
|
- The gateway on each node can proxy to services on other nodes via
|
||||||
preserving path-based routing. Components don't know which node
|
host-based routing (`<service>.<domain>` resolves to the local port or,
|
||||||
|
via the remote registry, another node). Components don't know which node
|
||||||
they're talking to.
|
they're talking to.
|
||||||
- MQTT provides pub/sub messaging for events, status, and coordination
|
- MQTT provides pub/sub messaging for events, status, and coordination
|
||||||
across nodes.
|
across nodes.
|
||||||
|
|||||||
@@ -395,8 +395,8 @@ version number"). `gateway.tls` has two values:
|
|||||||
| `off` (default/unset) | `:<port>` HTTP, `auto_https off` | host matcher on `:<port>` | none |
|
| `off` (default/unset) | `:<port>` HTTP, `auto_https off` | host matcher on `:<port>` | none |
|
||||||
| `acme` | one `*.<domain>` `:443` site | matcher inside the wildcard site | **real Let's Encrypt wildcard, no CA install** |
|
| `acme` | one `*.<domain>` `: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 `:<port>` site — the way to
|
In `off` mode all routes stay on the HTTP `:<port>` site — the way to put a
|
||||||
put a service on HTTPS is to set `proxy: true` (and use acme mode). A node with no public
|
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
|
domain stays on `off` (plain HTTP; use `localhost`/direct ports for anything that
|
||||||
needs a secure context).
|
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`
|
#### Publicly-trusted HTTPS — `gateway.tls: acme`
|
||||||
|
|
||||||
A private-CA approach (Caddy's `tls internal`) forces every client device to trust
|
A private-CA approach would force every client device to trust a custom root —
|
||||||
a custom root — which some platforms (e.g. Android browsers, and Firefox, which
|
which some platforms (e.g. Android browsers, and Firefox, which uses its own
|
||||||
uses its own store) make painful. `acme` mode avoids it entirely: Caddy obtains a
|
store) make painful — so Castle doesn't offer one. `acme` mode avoids it entirely:
|
||||||
|
Caddy obtains a
|
||||||
**real Let's Encrypt wildcard cert** (`*.<domain>`) via a **DNS-01** challenge, so
|
**real Let's Encrypt wildcard cert** (`*.<domain>`) via a **DNS-01** challenge, so
|
||||||
every browser trusts it with **zero CA install** — while the services stay
|
every browser trusts it with **zero CA install** — while the services stay
|
||||||
**internal-only**.
|
**internal-only**.
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ my-app/
|
|||||||
|
|
||||||
Registered as a program with `build.outputs: [public]` plus a `manager: caddy`
|
Registered as a program with `build.outputs: [public]` plus a `manager: caddy`
|
||||||
deployment (`root: public`, derived **kind: static**), so the
|
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 `<name>.<gateway.domain>` (its own subdomain root) — no service, no process.
|
||||||
|
|
||||||
## supabase.app.yaml
|
## supabase.app.yaml
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ service itself is likewise at `supabase.<gateway.domain>`.) See
|
|||||||
castle program create my-app --stack supabase --description "..." # scaffold + register
|
castle program create my-app --stack supabase --description "..." # scaffold + register
|
||||||
castle program build my-app # apply unapplied migrations to the substrate
|
castle program build my-app # apply unapplied migrations to the substrate
|
||||||
castle program test my-app # deno test over functions/ (if deno present)
|
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 <name>.<gateway.domain>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Scaffolding
|
## Scaffolding
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ Reachability is the easy half. Anything public also needs, per service:
|
|||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- Requires `gateway.tls: acme` (the tunnel forwards to the gateway's real-cert
|
- 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
|
- 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
|
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
|
self-hosted VPS + WireGuard edge instead — the toggle and generator stay; only the
|
||||||
|
|||||||
Reference in New Issue
Block a user