Rename Castle -> Wild PC across the repo

Repo-side rename only (Phases 1-3 of the migration plan); the live box
(~/.castle, systemd units, /data/castle, domains) is a separate cutover.

- Slug `castle` -> `wildpc`: CLI command, module names (wildpc_core/cli/api),
  dist names, entry point `wildpc = wildpc_cli.main:main`.
- Identifiers: CastleConfig/NATSClient/DirError/MDNS -> Wildpc*.
- Env/constants: CASTLE_* -> WILDPC_*; ~/.castle -> ~/.wildpc, castle.yaml ->
  wildpc.yaml, /data/castle -> /data/wildpc.
- Systemd UNIT_PREFIX castle- -> wildpc-; own programs castle-api/gateway/etc.
- Display prose "Castle" -> "Wild PC" in docs, agent-guide files, README, frontend.
- Package dirs and bootstrap yaml renamed via git mv; lockfiles regenerated;
  redundant nested uv.lock files dropped (workspace root lock is authoritative).

Tests: core 273, cli 47, wildpc-api 120 all pass. Frontend type-checks + builds.
Fixed a stale test fixture (secret_env_path kind arg) broken pre-rename.
This commit is contained in:
2026-07-18 22:55:08 -07:00
parent 25d7a522cc
commit 05b28cb584
190 changed files with 2428 additions and 3337 deletions

150
README.md
View File

@@ -1,8 +1,8 @@
# Castle
# Wild PC
> Standing to author, run, govern, and maintain your own software.
A personal software platform. Castle manages independent services, tools, and
A personal software platform. Wild PC manages independent services, tools, and
frontends — and launches your coding agents — from a single CLI, with a unified
gateway, systemd integration, and a web dashboard.
@@ -18,27 +18,27 @@ and configurations of existing tools. Many of these programs do not need a publi
release process, an app store listing, or a conventional distribution channel. They
need a reliable place to run.
Castle provides that place.
Wild PC provides that place.
Castle gives simple applications a consistent local environment for development,
Wild PC gives simple applications a consistent local environment for development,
registration, deployment, discovery, and operation. Programs remain independent, but
Castle provides the surrounding structure: process management, routing, metadata,
Wild PC provides the surrounding structure: process management, routing, metadata,
service lifecycle, logs, and a common interface for running and inspecting the
software in your domain.
In this sense, Castle is a **personal software estate**: a practical way to organize
In this sense, Wild PC is a **personal software estate**: a practical way to organize
the software you create and run yourself, without requiring every tool to become a
fully packaged product.
## How it works
Castle separates *what software exists* from *how it runs*:
Wild PC separates *what software exists* from *how it runs*:
- **Programs** — the catalog. A program is a source repo Castle knows how to work
- **Programs** — the catalog. A program is a source repo Wild PC knows how to work
with (dev verbs, build) and where it lives. One file per program under
`~/.castle/programs/<name>.yaml`.
`~/.wildpc/programs/<name>.yaml`.
- **Deployments** — how a program is realized on this node. One file per deployment
under `~/.castle/deployments/<name>.yaml`, discriminated by its **manager**:
under `~/.wildpc/deployments/<name>.yaml`, discriminated by its **manager**:
| manager | what it is | derived **kind** |
|---------|------------|------------------|
@@ -55,33 +55,33 @@ A program can have several deployments — a CLI that is both a `tool` on PATH a
scheduled `job` — so a program has no single kind of its own; it *has deployments*,
each with its own.
Standing everything up is one honest step: `castle apply` renders systemd units and
Standing everything up is one honest step: `wildpc apply` renders systemd units and
gateway config from your config, then reconciles the runtime to match — activating
what's enabled, restarting what changed, deactivating what's disabled. Edit config,
`castle apply`; `castle apply --plan` shows the diff first.
`wildpc apply`; `wildpc apply --plan` shows the diff first.
## Stacks
Castle **stacks** are pre-configured development environments that provide starting
points for building Castle programs. A stack can define the language, framework,
dependencies, tools, conventions, and Castle integration needed for a particular
Wild PC **stacks** are pre-configured development environments that provide starting
points for building Wild PC programs. A stack can define the language, framework,
dependencies, tools, conventions, and Wild PC integration needed for a particular
kind of application — `python-fastapi`, `python-cli`, `react-vite`, `supabase`.
Stacks are designed to work well with coding assistants. They give assistants a
consistent target when generating Castle programs, making it easier to produce
consistent target when generating Wild PC programs, making it easier to produce
applications that are correctly structured, configured, and ready to run under
Castle. If your coding assistant understands Castle, it can help you create,
Wild PC. If your coding assistant understands Wild PC, it can help you create,
register, manage, and evolve custom applications more efficiently.
A stack seeds a new program's scaffold and default dev-verb commands, but it's
optional at runtime: a program stands on its own via its declared `commands:` and
`source:`, so you can adopt any existing repo with `castle program add` — no stack
`source:`, so you can adopt any existing repo with `wildpc program add` — no stack
required.
## Agents
Castle can launch your coding agents. Declare them in `castle.yaml` under `agents:`
— each entry is just a command Castle runs in a terminal:
Wild PC can launch your coding agents. Declare them in `wildpc.yaml` under `agents:`
— each entry is just a command Wild PC runs in a terminal:
```yaml
agents:
@@ -96,7 +96,7 @@ agents:
The dashboard has a terminal dock that launches any declared agent in a
pseudo-terminal (over a WebSocket) and manages live sessions (list, resume, kill).
Castle is **assistant-agnostic** — it only ever runs `command args` in a pty and
Wild PC is **assistant-agnostic** — it only ever runs `command args` in a pty and
never parses the agent's output, so any interactive CLI works. With no `agents:`
block set, a sensible default set (`claude`, `opencode`, `amplifier`, …) is offered.
@@ -104,28 +104,28 @@ block set, a sensible default set (`claude`, `opencode`, `amplifier`, …) is of
**Prerequisites:** a Debian/Ubuntu-family Linux with `apt` and `sudo`, `systemd`
(user services), and `git`. `install.sh` sets up everything else — Docker, Caddy,
`uv`, and the `castle` CLI itself.
`uv`, and the `wildpc` CLI itself.
```bash
git clone <this-repo> ~/castle && cd ~/castle
git clone <this-repo> ~/wildpc && cd ~/wildpc
# One command: installs uv + the castle CLI, sets up infra (Docker, Caddy, MQTT,
# Postgres), creates ~/.castle, registers Castle's own control plane, builds the UI.
# One command: installs uv + the wildpc CLI, sets up infra (Docker, Caddy, MQTT,
# Postgres), creates ~/.wildpc, registers Wild PC's own control plane, builds the UI.
./install.sh
castle apply # converge the runtime to config (units, routes, run)
castle doctor # verify — every check should be green
wildpc apply # converge the runtime to config (units, routes, run)
wildpc doctor # verify — every check should be green
open http://localhost:9000 # the dashboard
```
`castle doctor` is your friend at every step: it inspects setup *and* runtime and,
`wildpc doctor` is your friend at every step: it inspects setup *and* runtime and,
for anything not green, prints the exact next command. Run it any time something
looks off — after an install, a deploy, or a config change.
### Exposure: from localhost to your own HTTPS domain
Localhost is the first rung; you climb only as far as you need. Each rung is a small
config change plus `castle apply`, and `castle doctor` tells you what a rung still
config change plus `wildpc apply`, and `wildpc doctor` tells you what a rung still
needs.
| Rung | You get | What it takes |
@@ -135,86 +135,86 @@ needs.
| **Public** | a chosen service reachable from the internet | `public: true` on the service + a Cloudflare tunnel. → [docs/tunnel-setup.md](docs/tunnel-setup.md) |
The jump to LAN HTTPS is the involved one (DNS + a token + binding `:443`). Set
`gateway.tls: acme` and run `castle doctor` — it enumerates exactly the pieces that
`gateway.tls: acme` and run `wildpc doctor` — it enumerates exactly the pieces that
are still missing, each with its fix.
## Creating programs
`castle program create` scaffolds the source **and** its deployment from a stack:
`wildpc program create` scaffolds the source **and** its deployment from a stack:
```bash
# A service — FastAPI app + a systemd service deployment (health, unit, route)
castle program create my-api --stack python-fastapi --description "Does something"
castle program test my-api
castle apply my-api # render unit + route, then start it
wildpc program create my-api --stack python-fastapi --description "Does something"
wildpc program test my-api
wildpc apply my-api # render unit + route, then start it
# A tool — a CLI installed on your PATH
castle program create my-tool --stack python-cli --description "Does something"
castle apply my-tool # installs its path deployment on PATH
wildpc program create my-tool --stack python-cli --description "Does something"
wildpc apply my-tool # installs its path deployment on PATH
# A static frontend — built once, served by the gateway
castle program create my-app --stack react-vite --description "Web interface"
castle program build my-app && castle apply
wildpc program create my-app --stack react-vite --description "Web interface"
wildpc program build my-app && wildpc apply
# Adopt an existing repo (no stack needed — dev verbs detected or declared)
castle program add ~/projects/some-rust-tool
wildpc program add ~/projects/some-rust-tool
```
## CLI
Operations live under the resource they act on. `program` is the catalog;
`service`, `job`, and `tool` are **views** over the one deployment set. Lifecycle
is convergence — `castle apply` — not a pile of per-kind verbs.
is convergence — `wildpc apply` — not a pile of per-kind verbs.
```
# Programs — the software catalog
castle program list|info|create|add|clone|delete|run
castle program build|test|lint|type-check|check [name] # dev verbs
wildpc program list|info|create|add|clone|delete|run
wildpc program build|test|lint|type-check|check [name] # dev verbs
# Deployment lenses (service = systemd, job = systemd + schedule, tool = path)
castle service list|info|create|delete|restart|logs
castle job …same verbs; create takes --schedule
castle tool list|info # CLIs on your PATH
wildpc service list|info|create|delete|restart|logs
wildpc job …same verbs; create takes --schedule
wildpc tool list|info # CLIs on your PATH
# Platform-wide
castle apply [name] [--plan] # converge runtime to config — the workhorse
castle list [--kind K] [--stack S] [--json] # catalog + every deployment view
castle status # unified runtime status
castle doctor # diagnose setup + health, with fix hints
castle restart [name] # imperative bounce (one or all)
castle gateway # status + route table (inspection)
wildpc apply [name] [--plan] # converge runtime to config — the workhorse
wildpc list [--kind K] [--stack S] [--json] # catalog + every deployment view
wildpc status # unified runtime status
wildpc doctor # diagnose setup + health, with fix hints
wildpc restart [name] # imperative bounce (one or all)
wildpc gateway # status + route table (inspection)
```
To turn a deployment off, set `enabled: false` in its config and `castle apply`
To turn a deployment off, set `enabled: false` in its config and `wildpc apply`
there's no start/stop/enable/install verb; the manager decides the mechanism
(systemd unit, PATH install, gateway route), the verb is always `apply`.
`castle tool list --json` is the machine-readable tool catalog assistants use to
`wildpc tool list --json` is the machine-readable tool catalog assistants use to
build context — it surfaces each tool's actual **executable** (which can differ from
the program name, e.g. `litellm-intent-router` installs `intent-router`), its
description, and whether it's installed.
## Configuration
The registry lives under `~/.castle/`: a global `castle.yaml` plus one file per
The registry lives under `~/.wildpc/`: a global `wildpc.yaml` plus one file per
resource under `programs/` and `deployments/`.
```yaml
# ~/.castle/castle.yaml — globals
# ~/.wildpc/wildpc.yaml — globals
gateway:
port: 9000
repo: /data/repos/castle # resolves `source: repo:<name>` for castle's own programs
repo: /data/repos/wildpc # resolves `source: repo:<name>` for wildpc's own programs
```
```yaml
# ~/.castle/programs/my-api.yaml — the catalog entry
# ~/.wildpc/programs/my-api.yaml — the catalog entry
description: Does something useful
source: /data/repos/my-api
stack: python-fastapi
```
```yaml
# ~/.castle/deployments/my-api.yaml — a systemd service (derived kind: service)
# ~/.wildpc/deployments/my-api.yaml — a systemd service (derived kind: service)
program: my-api
manager: systemd
run: { launcher: python, program: my-api }
@@ -224,21 +224,21 @@ manage: { systemd: {} }
defaults:
env:
MY_API_PORT: ${port} # = expose.http.internal.port
MY_API_DATA_DIR: ${data_dir} # = $CASTLE_DATA_DIR/my-api
MY_API_DATA_DIR: ${data_dir} # = $WILDPC_DATA_DIR/my-api
API_KEY: ${secret:MY_API_KEY}
```
`defaults.env` is the **single, explicit source** of a deployment's environment —
Castle injects nothing implicitly. The placeholders `${port}`, `${data_dir}`,
Wild PC injects nothing implicitly. The placeholders `${port}`, `${data_dir}`,
`${name}`, `${public_url}`, and `${secret:NAME}` map your program's own env var names
to Castle's computed values. Secrets live in `~/.castle/secrets/` (never in a repo).
to Wild PC's computed values. Secrets live in `~/.wildpc/secrets/` (never in a repo).
## Gateway, DNS & TLS
Every gateway-exposed deployment gets its own subdomain — `<name>.<gateway.domain>`
— routed to it by the Caddy gateway (there are no path-prefix routes). Exposure is a
single checkbox: `proxy: true` on a service, while a static deployment is inherently
served. The dashboard is `castle.<domain>` and the API `castle-api.<domain>`; on a
served. The dashboard is `wildpc.<domain>` and the API `wildpc-api.<domain>`; on a
node with no domain, `:9000` serves the dashboard plus a `/api` proxy.
`gateway.tls` is a per-node choice: `off` (plain HTTP on `:9000`) or `acme` (a real
@@ -250,42 +250,42 @@ public internet is separately opt-in via a Cloudflare tunnel (`public: true`). S
## Layout
```
~/.castle/ # instance: config, artifacts, secrets
castle.yaml # globals (gateway, repo, agents)
~/.wildpc/ # instance: config, artifacts, secrets
wildpc.yaml # globals (gateway, repo, agents)
programs/ deployments/ # one file per program / deployment
secrets/ # secret files (mode 700)
artifacts/specs/ # generated Caddyfile, registry.yaml
/data/repos/<name>/ # your program source (absolute source:)
/data/castle/<name>/ # per-deployment data volume
<repo>/ # castle itself: cli/ core/ castle-api/ app/ docs/
/data/wildpc/<name>/ # per-deployment data volume
<repo>/ # wildpc itself: cli/ core/ wildpc-api/ app/ docs/
```
**Independence principle:** your programs never depend on Castle. They accept
configuration (data dir, port, URLs) via environment variables; only Castle's own
programs (CLI, API, gateway) know Castle internals.
**Independence principle:** your programs never depend on Wild PC. They accept
configuration (data dir, port, URLs) via environment variables; only Wild PC's own
programs (CLI, API, gateway) know Wild PC internals.
## Dashboard & API
The **dashboard** (`app/`, served at `castle.<domain>` or `http://localhost:9000`)
The **dashboard** (`app/`, served at `wildpc.<domain>` or `http://localhost:9000`)
lists programs and deployments, edits their config, drives lifecycle, shows the
gateway route table and logs, and hosts the agent terminal dock.
**`castle-api`** (port 9020, proxied at `castle-api.<domain>`) is the control plane:
**`wildpc-api`** (port 9020, proxied at `wildpc-api.<domain>`) is the control plane:
`/deployments`, `/programs`, `/services`, `/jobs`, `/gateway`, `/status`, an SSE
`/stream`, config editing under `/config/…`, and the agent session endpoints under
`/agents`. The full endpoint reference is in [CLAUDE.md](CLAUDE.md).
## Mesh (opt-in)
Castle nodes can discover each other via MQTT + mDNS to form a personal
Wild PC nodes can discover each other via MQTT + mDNS to form a personal
infrastructure mesh — the gateway can route to services on other nodes and the
dashboard shows discovered nodes and cross-node routes. It's all off by default;
single-node needs none of it. Enable on `castle-api` via `CASTLE_API_MQTT_ENABLED`
and `CASTLE_API_MDNS_ENABLED`.
single-node needs none of it. Enable on `wildpc-api` via `WILDPC_API_MQTT_ENABLED`
and `WILDPC_API_MDNS_ENABLED`.
## Docs
- [docs/registry.md](docs/registry.md) — the registry model, `castle.yaml`, deployment fields, lifecycle
- [docs/registry.md](docs/registry.md) — the registry model, `wildpc.yaml`, deployment fields, lifecycle
- [docs/dns-and-tls.md](docs/dns-and-tls.md) — gateway routing, DNS, the `off` / `acme` TLS modes
- [docs/stacks/](docs/stacks/) — per-stack guides (python-fastapi, python-cli, react-vite, supabase)
- [AGENTS.md](AGENTS.md) — the canonical, assistant-agnostic operator guide (recipes, gateway, tunnel, mesh)