docs: clean up pre-existing staleness across the guides

Sweep of accumulated drift unrelated to the CLI reorg:
- Source paths: ~/.castle/code/<name> → /data/repos/<name> (REPOS_DIR), and
  source: code/X → absolute /data/repos/X. registry.md source-resolution table
  and 'how programs get in' section rewritten for the /data/repos layout.
- Old field name component: → program: in all YAML examples.
- Old term 'component' → 'program' throughout design.md and the stack guides.
- Frontend serving: corrected the copy-to-artifacts/content model to serve-in-
  place from the repo (<source>/<dist>), matching Phase 2b; dropped the dead
  _copy_app_static reference and the 'runtime never references the source tree'
  claim (frontends are the deliberate exception).
- Stale endpoints/commands: /components → /deployments; castle create/add →
  castle program create/add in the create.py/add.py docstrings.

CLAUDE.md was already current. cli 24 green; ruff clean.
This commit is contained in:
2026-06-14 16:32:13 -07:00
parent 5c6d8ec6f1
commit fd562f7468
7 changed files with 98 additions and 101 deletions

View File

@@ -20,7 +20,7 @@ is self-sufficient. The mesh is optional.
Castle service is just a well-behaved Unix daemon that happens to
be registered in a manifest.
3. **Stack and behavior.** Each component has a *stack* (development
3. **Stack and behavior.** Each program has a *stack* (development
toolchain: python-fastapi, python-cli, react-vite) and a *behavior*
(runtime role: daemon, tool, frontend). Scheduling, systemd management,
and proxying are orthogonal operations — not behaviors.
@@ -36,7 +36,7 @@ is self-sufficient. The mesh is optional.
source tree.
6. **AI-manageable.** The CLI and API exist so that AI assistants can
discover, create, and manage components programmatically. Humans
discover, create, and manage programs programmatically. Humans
use the dashboard. Agents use the CLI and API.
7. **Simple until proven otherwise.** Filesystem over databases. HTTP
@@ -123,25 +123,25 @@ Castle generates systemd unit files and Caddyfile entries from the
registry. It doesn't run a daemon itself — it configures OS-level
infrastructure and gets out of the way.
Critically, the runtime layer references only standard paths — never
the source tree. Systemd units point to installed binaries (on PATH
or in `~/.local/bin/`), not to repo subdirectories. Caddy serves
from `$CASTLE_HOME/artifacts/content/`, not from build output directories in the repo.
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
(`<source>/<dist>/`), with the serve prefix baked into the build via `VITE_BASE`.
### Registry Layer
The registry is the central concept in Castle. It tracks what components
The registry is the central concept in Castle. It tracks what programs
exist, what they can do, and how they're configured. But it's not a
single thing — it's three distinct concepts:
**1. Component spec** — what a component *is*. Description, capabilities,
**1. Component spec** — what a program *is*. Description, capabilities,
build instructions, default configuration. This is source-level
information, version-controlled in the repo. It answers: "what components
information, version-controlled in the repo. It answers: "what programs
could exist?"
**2. Node config** — what's *deployed on this machine*, with what concrete
ports, data paths, and env vars. This is per-machine. Two Castle nodes
might run different subsets of components with different parameters. It
might run different subsets of programs with different parameters. It
answers: "what's running here, and how?"
**3. Runtime state** — what's *actually happening*. PIDs, health, uptime,
@@ -158,11 +158,11 @@ These map to two files:
programs:
central-context:
description: Content storage API
source: code/central-context
source: /data/repos/central-context
services:
central-context:
component: central-context
program: central-context
run:
runner: python
tool: central-context
@@ -178,7 +178,7 @@ services:
jobs:
backup-collect:
component: backup-collect
program: backup-collect
run:
runner: command
argv: [backup-collect]
@@ -191,7 +191,7 @@ Programs define *what software exists* (identity, source, install, tools).
Services define *how daemons run* (run config, expose, proxy, systemd).
Jobs define *how scheduled tasks run* (run config, cron schedule, systemd).
Services and jobs can reference a component via `component:` for description
Services and jobs can reference a program via `program:` for description
fallthrough and source code linking. They can also exist independently
(e.g., `castle-gateway` runs Caddy — not our software).
@@ -239,7 +239,7 @@ Three interfaces expose the registry:
- **CLI** (`castle`) — For AI agents and terminal users. Structured
output via `--json`. Commands for listing, inspecting, creating,
and managing components.
and managing programs.
- **API** (`castle-api`) — For programmatic access over HTTP. Used by
the dashboard, other nodes, and remote agents.
- **Dashboard** (`castle-app`) — For human discoverability. Visual
@@ -248,7 +248,7 @@ Three interfaces expose the registry:
### Coordination Layer
Coordination handles discovery and communication — both between
components on a single node and across multiple Castle nodes.
programs on a single node and across multiple Castle nodes.
**Intra-node coordination:**
- Components find each other through the gateway (path-based routing)
@@ -257,7 +257,7 @@ components on a single node and across multiple Castle nodes.
- No service mesh or message broker required for basic operation.
**Inter-node coordination:**
- Each Castle node runs the API, which exposes its component registry.
- 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,
@@ -290,9 +290,9 @@ Local paths always take precedence.
**Why MQTT over custom gossip:**
- Standard protocol, every language has a client library.
- Retained messages give new nodes an immediate view of the network.
- Topic-based routing maps naturally to `castle/{node}/{component}`.
- Topic-based routing maps naturally to `castle/{node}/{program}`.
- Works across networks (not just LAN like mDNS).
- Mosquitto is a single binary, simple to run as a Castle component.
- Mosquitto is a single binary, simple to run as a Castle program.
**Why mDNS/DNS-SD as a complement:**
- Zero-config LAN discovery via python-zeroconf.
@@ -303,9 +303,9 @@ Local paths always take precedence.
### Dashboard
The web dashboard (`castle-app`) is a React SPA served by Caddy from
`$CASTLE_HOME/artifacts/content/castle-app/`. It talks to `castle-api` via the
gateway proxy at `/api`.
The web dashboard (`castle-app`) is a React SPA served by Caddy in place from
its repo build output (`<source>/dist/`) at the root `/`. It talks to
`castle-api` via the gateway proxy at `/api`.
**Layout:**
@@ -344,7 +344,7 @@ Components · Software catalog
backup-collect Python / CLI tool 0 2 * * * —
```
**Key components:**
**Key programs:**
- **GatewayPanel** — Route table with live health badges, reload button,
collapsible Caddyfile viewer. Node column appears when multi-node.
- **MeshPanel** — MQTT connection status (connected/disconnected badge),
@@ -353,7 +353,7 @@ Components · Software catalog
- **NodeBar** — Horizontal list of discovered nodes. Hidden in single-node
mode. Each node links to `/node/{hostname}`.
- **ServiceSection** — Daemon cards in a responsive grid.
- **ComponentTable** — Unified sortable table for all non-daemon components
- **ComponentTable** — Unified sortable table for all non-daemon programs
(tools, frontends) with Stack, Behavior, Schedule, and Status columns.
**Real-time updates:**
@@ -365,11 +365,11 @@ Components · Software catalog
**Multi-node behavior:**
- NodeBar appears when `GET /nodes` returns >1 node.
- GatewayPanel shows a "Node" column when routes span multiple nodes.
- `/node/{hostname}` page shows a specific node's deployed components.
- `/node/{hostname}` page shows a specific node's deployed programs.
## Component Contract
Every Castle component, regardless of language, must satisfy a minimal
Every Castle program, regardless of language, must satisfy a minimal
contract. This is what makes the system uniform above the build line.
### Services (long-running daemons)
@@ -421,11 +421,11 @@ Each step is distinct:
2. **Install** — Makes the artifact available on the system. For tools:
`uv tool install` or compiled binary placed in `~/.local/bin/`. For
services: same — the binary or entry point is on PATH or in a known
location. For frontends: built assets copied to
`$CASTLE_HOME/artifacts/content/`.
location. For frontends: assets built in place under the repo
(`<source>/<dist>/`), served from there — no copy.
3. **Deploy** — Materializes the runtime configuration. Reads the
component spec, merges with node config, generates systemd units
program spec, merges with node config, generates systemd units
and Caddyfile entries that reference *installed* artifacts — never
the source tree. Enables and starts services.
@@ -507,7 +507,7 @@ Key OTP ideas that apply:
can remap these across nodes.
- **Spec vs. config.** In OTP, an application defines its structure
(the `.app` file) and a release provides the deployment config
(`sys.config`). Castle mirrors this: the component spec defines
(`sys.config`). Castle mirrors this: the program spec defines
structure, the node config provides deployment values.
## Current State
@@ -526,9 +526,9 @@ What exists today:
the manifest. Only non-convention values need `defaults.env`.
- **Gateway** — Caddy on port 9000, Caddyfile generated from registry
- **API** — `castle-api` on port 9020, reads from registry (optional
castle.yaml fallback for non-deployed components)
castle.yaml fallback for non-deployed programs)
- **Dashboard** — `castle-app` React/Vite frontend, static assets
served from `$CASTLE_HOME/artifacts/content/castle-app/`
served in place from its repo build output (`<source>/dist/`)
- **Services** — central-context (content storage), notification-bridge
(desktop notification forwarder)
- **Jobs** — protonmail (email sync every 5 min), backup-collect (nightly),
@@ -543,7 +543,7 @@ What exists today:
on port 1883, managed by systemd. Config and data in
`$CASTLE_DATA_DIR/castle-mqtt/`.
- **Node API** — `GET /mesh/status`, `GET /nodes`, `GET /nodes/{hostname}`.
`GET /components?include_remote=true` for cross-node component listing.
`GET /deployments?include_remote=true` for cross-node program listing.
- **Gateway panel** — Dedicated UI showing route table, health per route,
reload button, Caddyfile viewer. Cross-node routes shown when multi-node.
- **Mesh panel** — Dashboard UI showing MQTT connection status, broker
@@ -555,7 +555,7 @@ What exists today:
What doesn't exist yet:
- **Multi-language support** — Rust and Go components (the abstractions
- **Multi-language support** — Rust and Go programs (the abstractions
support them via `command` runner, but no examples exist yet)
- **Build automation** — Castle records build specs but doesn't
orchestrate builds (each project builds independently)