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:
150
README.md
150
README.md
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user