Files
wild-pc/docs/developing-wildpc.md
Paul Payne 05b28cb584 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.
2026-07-18 22:55:08 -07:00

3.5 KiB

Developing Wild PC

How to work on Wild PC's own code — the CLI, core library, control-plane API, and dashboard. For using Wild PC to manage software (create/deploy/expose programs), see the operator guide in AGENTS.md.

The Wild PC monorepo

Wild PC's own programs live in this git repo (source: repo:<name>) — distinct from the programs you manage, which live under /data/repos/<name>/:

  • cli/ — the wildpc CLI (installed via uv tool install --editable cli/)
  • core/wildpc_core: manifest models, config loader, generators
  • wildpc-api/ — the FastAPI control-plane service (port 9020)
  • app/ — the dashboard frontend (React/Vite; program name wildpc, served at wildpc.<gateway.domain>)

The root pyproject.toml is the uv workspace (core, cli, wildpc-api).

Key files

  • core/src/wildpc_core/manifest.py — Pydantic models: ProgramSpec, DeploymentSpec (discriminated union on manager), LaunchSpec, AgentSpec, …
  • core/src/wildpc_core/config.py — config loader (wildpc.yaml + programs/ + deployments/WildpcConfig), the two roots, source: resolution
  • core/src/wildpc_core/generators/ — systemd unit/timer + Caddyfile generation
  • cli/src/wildpc_cli/ — resource-first CLI commands; templates/scaffold.py
  • wildpc-api/src/wildpc_api/ — routes, health polling, SSE stream.py, mesh, and the agent terminal UX (agents.py, pty_session.py, agent_sessions.py, agent_registry.py)
  • ruff.toml / pyrightconfig.json — shared lint/type config

Commands (all projects use uv)

uv sync
uv run pytest tests/ -v
uv run ruff check . && uv run ruff format .
uv run pyright <paths>

Running uv sync from a member subdir trims the shared workspace venv — resync from the repo root to restore all members. Code style: ruff (100-char lines), pyright, pytest + pytest-asyncio; Python 3.13 for services, 3.11+ for tools/libraries.

wildpc-api endpoints (port 9020)

  • Core: GET /health, GET /stream (SSE: health, service-action, mesh)
  • Converge: POST /apply ({name?, plan?}) — the one lifecycle endpoint
  • Deployments: GET /deployments[/{name}], GET /status
  • Catalog + editing: GET /programs[/{name}], POST /programs/{name}/{action} (dev verbs only), PUT|DELETE /programs/{name}; likewise /services, /jobs
  • Config: GET|PUT /, POST /config/apply, PUT /config/deployments/{name}/enabled
  • Gateway: GET /gateway, GET /gateway/caddyfile, PUT /gateway/config (making routes live is convergence: POST /apply)
  • Mesh: GET /mesh/status, GET /nodes[/{hostname}]
  • Agents (dashboard terminal UX): GET /agents, GET /agents/sessions, GET /agents/history, DELETE /agents/sessions/{id}, WS /agents/{name}/session
  • Service actions: POST /services/{name}/restart, GET /services/{name}/unit

Infrastructure internals

  • Generated Caddyfile at ~/.wildpc/artifacts/specs/Caddyfile; a plugin Caddy at /usr/local/bin/caddy when tls: acme.
  • Systemd user units at ~/.config/systemd/user/wildpc-*.service (+ .timer); the unit for program X is wildpc-X.service. Use drop-in *.service.d/*.conf for extra env wildpc apply shouldn't overwrite.
  • The container launcher resolves docker via shutil.which("docker") (preferred over rootless podman on this box).
  • Service data at $WILDPC_DATA_DIR/<name>/; secrets at ~/.wildpc/secrets/ (mode 700) — never in project directories.