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:
@@ -1,13 +1,13 @@
|
||||
# Hugo static sites in Castle
|
||||
# Hugo static sites in Wild PC
|
||||
|
||||
> **This is a stack — creation-time guidance for writing _new_ sites.**
|
||||
> A stack is a template + conventions, not a runtime requirement. `castle program
|
||||
> A stack is a template + conventions, not a runtime requirement. `wildpc program
|
||||
> create --stack hugo` scaffolds from it (via Hugo's own `hugo new site`) and seeds
|
||||
> the program's default build verb. An existing Hugo site adopted with `castle
|
||||
> the program's default build verb. An existing Hugo site adopted with `wildpc
|
||||
> program add` doesn't need this stack — it declares its own `commands:` /
|
||||
> `build:`. See @docs/registry.md for `commands:`, `stack:` (optional), and `repo:`.
|
||||
|
||||
How to build, serve, and manage [Hugo](https://gohugo.io) sites as castle programs.
|
||||
How to build, serve, and manage [Hugo](https://gohugo.io) sites as wildpc programs.
|
||||
|
||||
## Stack
|
||||
|
||||
@@ -18,27 +18,27 @@ How to build, serve, and manage [Hugo](https://gohugo.io) sites as castle progra
|
||||
`/posts/index.html`) and 404s missing paths. (The default `spa: true` is a
|
||||
single-page-app fallback that serves the root `index.html` for every unmatched
|
||||
path — correct for React/Vite, but it swallows a Hugo site's in-page links back
|
||||
to the homepage. `castle program create --stack hugo` sets `spa: false` for you.)
|
||||
to the homepage. `wildpc program create --stack hugo` sets `spa: false` for you.)
|
||||
- Package manager (only if a theme needs an asset pipeline): pnpm
|
||||
|
||||
Hugo has one meaningful dev verb — **build**. It has no native lint/test/type-check,
|
||||
so the stack advertises only `build` / `install` / `uninstall`; `castle check` and
|
||||
so the stack advertises only `build` / `install` / `uninstall`; `wildpc check` and
|
||||
friends aren't offered (a site can still declare its own, e.g. an HTML linter, under
|
||||
`commands:` — a declared verb always wins over the stack).
|
||||
|
||||
## Create a new site
|
||||
|
||||
```bash
|
||||
castle program create my-site --stack hugo --description "My site"
|
||||
wildpc program create my-site --stack hugo --description "My site"
|
||||
cd /data/repos/my-site
|
||||
castle program build my-site # hugo --gc --minify -> public/
|
||||
castle apply my-site # serve at my-site.<gateway.domain>
|
||||
wildpc program build my-site # hugo --gc --minify -> public/
|
||||
wildpc apply my-site # serve at my-site.<gateway.domain>
|
||||
```
|
||||
|
||||
The scaffold delegates the canonical skeleton to `hugo new site` (archetypes/,
|
||||
content/, layouts/, static/, themes/, hugo.toml) and overlays the pieces a bare
|
||||
skeleton lacks: minimal `layouts/` so the site **builds and serves without a
|
||||
theme**, an example `content/posts/hello.md`, a castle-flavored `hugo.toml`
|
||||
theme**, an example `content/posts/hello.md`, a wildpc-flavored `hugo.toml`
|
||||
(`baseURL = "/"`, so assets resolve at the root of the site's own subdomain), and a
|
||||
`.gitignore` for the regenerated `public/` and `resources/`.
|
||||
|
||||
@@ -78,14 +78,14 @@ cd themes/<theme> && pnpm install
|
||||
|
||||
## Deployment shape
|
||||
|
||||
`castle program create --stack hugo` writes:
|
||||
`wildpc program create --stack hugo` writes:
|
||||
|
||||
- **`programs/<name>.yaml`** — `source`, `stack: hugo`, `build.outputs: [public]`.
|
||||
- **`deployments/statics/<name>.yaml`** — `manager: caddy`, `root: public`,
|
||||
`reach: internal` (flip to `public` to also expose over the tunnel).
|
||||
|
||||
The gateway serves `<source>/public` in place — no copy, no Node/Hugo process at
|
||||
runtime. `castle program build` regenerates `public/`; `castle apply` renders the
|
||||
runtime. `wildpc program build` regenerates `public/`; `wildpc apply` renders the
|
||||
route and reloads the gateway.
|
||||
|
||||
## Adopting an existing Hugo site
|
||||
@@ -93,7 +93,7 @@ route and reloads the gateway.
|
||||
No stack needed — adopt the repo and declare how it builds:
|
||||
|
||||
```bash
|
||||
castle program add /path/to/site --name my-site
|
||||
wildpc program add /path/to/site --name my-site
|
||||
```
|
||||
|
||||
Then set `build.commands` (as above) and add a `manager: caddy` deployment. This is
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Python Tools in Castle
|
||||
# Python Tools in Wild PC
|
||||
|
||||
> **This is a stack — creation-time guidance for writing _new_ CLI tools.**
|
||||
> A stack is a template + conventions, not a runtime requirement. `castle program create
|
||||
> A stack is a template + conventions, not a runtime requirement. `wildpc program create
|
||||
> --stack python-cli` scaffolds from it and seeds the program's default dev-verb
|
||||
> commands. An existing CLI adopted with `castle program add` doesn't need this stack — it
|
||||
> commands. An existing CLI adopted with `wildpc program add` doesn't need this stack — it
|
||||
> declares its own `commands:`. See @docs/registry.md for `commands:`,
|
||||
> `stack:` (optional), and `repo:`.
|
||||
|
||||
@@ -49,11 +49,11 @@ Examples: `code/pdf2md/`, `code/gpt/`, `code/protonmail/`
|
||||
## Creating a new tool
|
||||
|
||||
```bash
|
||||
castle program create my-tool --stack python-cli --description "Does something"
|
||||
wildpc program create my-tool --stack python-cli --description "Does something"
|
||||
cd /data/repos/my-tool && uv sync
|
||||
```
|
||||
|
||||
This scaffolds the project and registers it in `castle.yaml`.
|
||||
This scaffolds the project and registers it in `wildpc.yaml`.
|
||||
|
||||
## pyproject.toml
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# Web APIs in Castle
|
||||
# Web APIs in Wild PC
|
||||
|
||||
> **This is a stack — creation-time guidance for writing _new_ FastAPI services.**
|
||||
> A stack is a template + conventions, not a runtime requirement. `castle program create
|
||||
> A stack is a template + conventions, not a runtime requirement. `wildpc program create
|
||||
> --stack python-fastapi` scaffolds from it and seeds the program's default
|
||||
> dev-verb commands. An existing service adopted with `castle program add` doesn't need
|
||||
> dev-verb commands. An existing service adopted with `wildpc program add` doesn't need
|
||||
> this stack — it declares its own `commands:`. See @docs/registry.md
|
||||
> for `commands:`, `stack:` (optional), and `repo:`.
|
||||
|
||||
How to build Python web APIs as castle service components. Based on the
|
||||
How to build Python web APIs as wildpc service components. Based on the
|
||||
patterns used in [wild-cloud/api](https://github.com/civilsociety-dev/wild-cloud)
|
||||
and existing castle services (central-context, notification-bridge, event-bus).
|
||||
and existing wildpc services (central-context, notification-bridge, event-bus).
|
||||
|
||||
## Stack
|
||||
|
||||
@@ -103,7 +103,7 @@ class Settings(BaseSettings):
|
||||
settings = Settings()
|
||||
```
|
||||
|
||||
Castle passes config via env vars in the deployment's `defaults.env`:
|
||||
Wild PC passes config via env vars in the deployment's `defaults.env`:
|
||||
|
||||
```yaml
|
||||
# programs/my-service.yaml
|
||||
@@ -127,9 +127,9 @@ manage:
|
||||
systemd: {}
|
||||
```
|
||||
|
||||
The env a service runs with is exactly what's in `defaults.env` — castle injects
|
||||
The env a service runs with is exactly what's in `defaults.env` — wildpc injects
|
||||
nothing implicitly. Map the vars your settings read (above, `env_prefix:
|
||||
"MY_SERVICE_"` → `MY_SERVICE_PORT`/`MY_SERVICE_DATA_DIR`) to castle's computed
|
||||
"MY_SERVICE_"` → `MY_SERVICE_PORT`/`MY_SERVICE_DATA_DIR`) to wildpc's computed
|
||||
values with the `${port}`/`${data_dir}` placeholders — add to
|
||||
`deployments/my-service.yaml`:
|
||||
|
||||
@@ -137,11 +137,11 @@ values with the `${port}`/`${data_dir}` placeholders — add to
|
||||
defaults:
|
||||
env:
|
||||
MY_SERVICE_PORT: ${port} # = expose.http.internal.port
|
||||
MY_SERVICE_DATA_DIR: ${data_dir} # = $CASTLE_DATA_DIR/my-service
|
||||
MY_SERVICE_DATA_DIR: ${data_dir} # = $WILDPC_DATA_DIR/my-service
|
||||
CENTRAL_CONTEXT_URL: http://localhost:9001
|
||||
```
|
||||
|
||||
`castle program create` scaffolds the `${port}`/`${data_dir}` lines for you.
|
||||
`wildpc program create` scaffolds the `${port}`/`${data_dir}` lines for you.
|
||||
|
||||
## Application entry point
|
||||
|
||||
@@ -300,10 +300,10 @@ Mapping convention:
|
||||
|
||||
## Storage
|
||||
|
||||
Castle services use filesystem storage with JSON metadata sidecars:
|
||||
Wild PC services use filesystem storage with JSON metadata sidecars:
|
||||
|
||||
```
|
||||
$CASTLE_DATA_DIR/my-service/ # default /data/castle/my-service/
|
||||
$WILDPC_DATA_DIR/my-service/ # default /data/wildpc/my-service/
|
||||
└── bucket/
|
||||
├── item-name
|
||||
└── item-name.meta.json
|
||||
@@ -447,11 +447,11 @@ uv run ruff format . # Format
|
||||
|
||||
## Scaffolding
|
||||
|
||||
`castle program create` generates all of this automatically:
|
||||
`wildpc program create` generates all of this automatically:
|
||||
|
||||
```bash
|
||||
castle program create my-service --stack python-fastapi --description "Does something useful"
|
||||
wildpc program create my-service --stack python-fastapi --description "Does something useful"
|
||||
```
|
||||
|
||||
See @docs/registry.md for manifest fields, castle.yaml structure,
|
||||
and the full service lifecycle (`castle apply`, logs).
|
||||
See @docs/registry.md for manifest fields, wildpc.yaml structure,
|
||||
and the full service lifecycle (`wildpc apply`, logs).
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Web Frontends in Castle
|
||||
# Web Frontends in Wild PC
|
||||
|
||||
> **This is a stack — creation-time guidance for writing _new_ frontends.**
|
||||
> A stack is a template + conventions, not a runtime requirement. `castle program create
|
||||
> A stack is a template + conventions, not a runtime requirement. `wildpc program create
|
||||
> --stack react-vite` scaffolds from it and seeds the program's default dev-verb
|
||||
> commands. An existing frontend adopted with `castle program add` doesn't need this
|
||||
> commands. An existing frontend adopted with `wildpc program add` doesn't need this
|
||||
> stack — it declares its own `commands:`. See @docs/registry.md for
|
||||
> `commands:`, `stack:` (optional), and `repo:`.
|
||||
|
||||
How to build, serve, and manage web frontends as castle programs.
|
||||
How to build, serve, and manage web frontends as wildpc programs.
|
||||
|
||||
## Stack
|
||||
|
||||
@@ -56,7 +56,7 @@ import { defineConfig } from "vite"
|
||||
import react from "@vitejs/plugin-react"
|
||||
|
||||
export default defineConfig({
|
||||
// Castle sets VITE_BASE=/ at build time (every frontend serves at its
|
||||
// Wild PC sets VITE_BASE=/ at build time (every frontend serves at its
|
||||
// subdomain root). Reading it here keeps the bundle.s absolute asset
|
||||
// URLs resolving at the root — no hand-tuning.
|
||||
base: process.env.VITE_BASE ?? "/",
|
||||
@@ -70,7 +70,7 @@ export default defineConfig({
|
||||
```
|
||||
|
||||
> **Serving behind the gateway.** A static frontend is served at its own subdomain
|
||||
> — `<name>.<gateway.domain>` — rooted at `/`, so `VITE_BASE` is always `/`. Castle's
|
||||
> — `<name>.<gateway.domain>` — rooted at `/`, so `VITE_BASE` is always `/`. Wild PC's
|
||||
> `react-vite` build passes `VITE_BASE=/`, and a `vite.config` that reads it (above)
|
||||
> works unchanged. (Frontends are no longer mounted under a `/<name>/` path.)
|
||||
|
||||
@@ -130,19 +130,19 @@ project root:
|
||||
24.14.1
|
||||
```
|
||||
|
||||
Castle reads this pin and puts the matching node on PATH whenever it runs the
|
||||
program's node — at build time (`castle program build`, incl. builds triggered from
|
||||
the castle web app, which run inside the `castle-api` service) and at run time (a
|
||||
Wild PC reads this pin and puts the matching node on PATH whenever it runs the
|
||||
program's node — at build time (`wildpc program build`, incl. builds triggered from
|
||||
the wildpc web app, which run inside the `wildpc-api` service) and at run time (a
|
||||
`launcher: node` service's systemd unit). This is why a build works from the web app
|
||||
even though the service's default PATH deliberately omits nvm's versioned dirs.
|
||||
|
||||
The pin is standard, tool-agnostic config — the program stays castle-independent; nvm
|
||||
(and editors/CI) honor the same file. Castle resolves it against
|
||||
`~/.nvm/versions/node` (override with `CASTLE_NODE_VERSIONS_DIR`), newest match wins.
|
||||
The pin is standard, tool-agnostic config — the program stays wildpc-independent; nvm
|
||||
(and editors/CI) honor the same file. Wild PC resolves it against
|
||||
`~/.nvm/versions/node` (override with `WILDPC_NODE_VERSIONS_DIR`), newest match wins.
|
||||
An exact pin (`24.14.1`) matches exactly; a partial (`24`) or a range (`>=24`) matches
|
||||
the newest installed major. If the pinned version isn't installed, the verb fails loud
|
||||
with a `nvm install <version>` hint instead of a cryptic `node: not found`. Unpinned →
|
||||
Castle injects no node (it uses whatever ambient node is on PATH, and does not guess).
|
||||
Wild PC injects no node (it uses whatever ambient node is on PATH, and does not guess).
|
||||
|
||||
## Registering as a program
|
||||
|
||||
@@ -194,19 +194,19 @@ See @docs/registry.md for the full registry reference.
|
||||
## Serving with Caddy
|
||||
|
||||
For production, the static build output is served by Caddy rather than a Node
|
||||
process. You do **not** write this block by hand — `castle apply` generates it.
|
||||
process. You do **not** write this block by hand — `wildpc apply` generates it.
|
||||
The flow:
|
||||
|
||||
1. You build the frontend with `castle program build <name>` (deploy does **not**
|
||||
1. You build the frontend with `wildpc program build <name>` (deploy does **not**
|
||||
build — it only points Caddy at `dist/`).
|
||||
2. The Caddyfile generator emits a route per `manager: caddy` deployment (kind
|
||||
**static**), rooted **in place** at `<source>/<root>` — no copy. Each static
|
||||
frontend is served at its own subdomain `<name>.<gateway.domain>` (the
|
||||
dashboard, `castle`, is also the target of the `:9000` redirect).
|
||||
dashboard, `wildpc`, is also the target of the `:9000` redirect).
|
||||
|
||||
The build is run with `VITE_BASE=/`, so a `vite.config` that reads it (see
|
||||
[Vite config](#vite-config)) emits root-relative asset URLs. The generated block
|
||||
(in `~/.castle/artifacts/specs/Caddyfile`) is a host matcher inside the
|
||||
(in `~/.wildpc/artifacts/specs/Caddyfile`) is a host matcher inside the
|
||||
`*.<domain>` site:
|
||||
|
||||
```caddyfile
|
||||
@@ -225,7 +225,7 @@ not hand-configured.
|
||||
|
||||
## API integration
|
||||
|
||||
Frontends talk to castle services via environment variables injected at build
|
||||
Frontends talk to wildpc services via environment variables injected at build
|
||||
time. Vite exposes variables prefixed with `VITE_`:
|
||||
|
||||
```bash
|
||||
@@ -256,7 +256,7 @@ class ApiClient {
|
||||
export const apiClient = new ApiClient()
|
||||
```
|
||||
|
||||
When served behind the castle gateway, the API base URL can use the gateway's
|
||||
When served behind the wildpc gateway, the API base URL can use the gateway's
|
||||
proxy paths (e.g., `/central-context/`) instead of direct ports, avoiding CORS.
|
||||
|
||||
## React Query setup
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Supabase Apps in Castle
|
||||
# Supabase Apps in Wild PC
|
||||
|
||||
> **This is a stack — creation-time guidance for writing _new_ database-backed
|
||||
> web apps.** A stack is a template + conventions, not a runtime requirement.
|
||||
> `castle program create --stack supabase` scaffolds from it and seeds the
|
||||
> `wildpc program create --stack supabase` scaffolds from it and seeds the
|
||||
> program's default dev-verb commands. See @docs/registry.md for `commands:`,
|
||||
> `stack:` (optional), and the deployment `manager` (and derived `kind`).
|
||||
|
||||
How to build tiny, database-backed web apps as castle programs that target a
|
||||
**shared Supabase substrate**. This is Castle's "a stack whose default is a
|
||||
How to build tiny, database-backed web apps as wildpc programs that target a
|
||||
**shared Supabase substrate**. This is Wild PC's "a stack whose default is a
|
||||
substrate": the app owns its code (and stays repo-durable), and rents the boring
|
||||
backend — Postgres + auth + storage + RLS — that an app can't reliably reinvent.
|
||||
|
||||
@@ -16,7 +16,7 @@ backend — Postgres + auth + storage + RLS — that an app can't reliably reinv
|
||||
Unlike the other stacks (which scaffold a self-contained process), a supabase app
|
||||
is **code + migrations that deploy against a shared backend**:
|
||||
|
||||
- **The substrate** is one castle service (`supabase`, the `supabase-substrate`
|
||||
- **The substrate** is one wildpc service (`supabase`, the `supabase-substrate`
|
||||
repo) running self-hosted Supabase via a `manager: systemd` deployment with the
|
||||
`compose` launcher. It is shared by
|
||||
every supabase app. Stand it up once (see that repo's README).
|
||||
@@ -27,10 +27,10 @@ is **code + migrations that deploy against a shared backend**:
|
||||
Apps are isolated on the shared instance by their **own Postgres schema + RLS**
|
||||
(and Storage buckets), under **one identity pool** — correct for a single-operator
|
||||
datalake. Each app owns a schema named after the program (`my-app` → schema
|
||||
`my_app`); `castle program build` creates and grants it, tracks migrations in a
|
||||
per-app `<schema>.schema_migrations`, and PostgREST exposes it (castle derives the
|
||||
`my_app`); `wildpc program build` creates and grants it, tracks migrations in a
|
||||
per-app `<schema>.schema_migrations`, and PostgREST exposes it (wildpc derives the
|
||||
substrate's `PGRST_DB_SCHEMAS` from the registered apps). This gives a clean
|
||||
teardown — `castle delete --purge-data` runs `drop schema <app> cascade` — and
|
||||
teardown — `wildpc delete --purge-data` runs `drop schema <app> cascade` — and
|
||||
means migration version tokens never collide across apps. Substrate-per-app is
|
||||
deliberately not supported: ~14 containers per app doesn't scale to "lots of small
|
||||
ideas," and a DB-backed app is a pet either way.
|
||||
@@ -69,14 +69,14 @@ gateway serves `public/` in place at `<name>.<gateway.domain>` (its own subdomai
|
||||
|
||||
```yaml
|
||||
name: my-app
|
||||
substrate: supabase # the shared castle service this app deploys against
|
||||
substrate: supabase # the shared wildpc service this app deploys against
|
||||
auth: public # public | private | shared: [handles]
|
||||
schema: my_app # this app's isolated Postgres schema (frontend: db.schema)
|
||||
```
|
||||
|
||||
## Migrations
|
||||
|
||||
`migrations/*.sql` are **numbered, forward-only, and idempotent**. `castle program
|
||||
`migrations/*.sql` are **numbered, forward-only, and idempotent**. `wildpc program
|
||||
build my-app` runs the versioned migration runner: it creates + grants the app's
|
||||
schema, ensures a per-app `<schema>.schema_migrations` table, reads applied
|
||||
versions, and applies only the **unapplied** files (in filename order) with
|
||||
@@ -112,14 +112,14 @@ const db = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, { db: { schema: SCHEMA
|
||||
|
||||
### Teardown
|
||||
|
||||
An app's rows live only on the substrate, so an ordinary `castle delete my-app`
|
||||
An app's rows live only on the substrate, so an ordinary `wildpc delete my-app`
|
||||
leaves the schema intact (and says so). To destroy the data too:
|
||||
|
||||
```bash
|
||||
castle delete my-app --purge-data # drop schema my_app cascade
|
||||
wildpc delete my-app --purge-data # drop schema my_app cascade
|
||||
```
|
||||
|
||||
`castle apply` then prunes the schema from `PGRST_DB_SCHEMAS`; **restart the
|
||||
`wildpc apply` then prunes the schema from `PGRST_DB_SCHEMAS`; **restart the
|
||||
`supabase` service** for PostgREST to pick up the added/removed schema list.
|
||||
|
||||
## Auth, RLS & the three privacy layers
|
||||
@@ -158,17 +158,17 @@ service itself is likewise at `supabase.<gateway.domain>`.) See
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
castle program create my-app --stack supabase --description "..." # scaffold + register
|
||||
castle program build my-app # apply unapplied migrations to the substrate
|
||||
castle program test my-app # deno test over functions/ (if deno present)
|
||||
castle apply # serve the static UI at <name>.<gateway.domain>
|
||||
wildpc program create my-app --stack supabase --description "..." # scaffold + register
|
||||
wildpc program build my-app # apply unapplied migrations to the substrate
|
||||
wildpc program test my-app # deno test over functions/ (if deno present)
|
||||
wildpc apply # serve the static UI at <name>.<gateway.domain>
|
||||
```
|
||||
|
||||
## Scaffolding
|
||||
|
||||
`castle program create --stack supabase` generates the full layout above and
|
||||
`wildpc program create --stack supabase` generates the full layout above and
|
||||
registers the program as a static frontend. Set the anon key in `public/config.js`
|
||||
(`cat ~/.castle/secrets/SUPABASE_ANON_KEY`), edit your migrations, and build.
|
||||
(`cat ~/.wildpc/secrets/SUPABASE_ANON_KEY`), edit your migrations, and build.
|
||||
|
||||
See @docs/registry.md for the `compose` launcher, the substrate deployment definition,
|
||||
and the full registry reference. The substrate itself lives in the
|
||||
|
||||
Reference in New Issue
Block a user