Commit Graph

7 Commits

Author SHA1 Message Date
Paul Payne
517471605a core: drop two manifest fields that were never wired up
`SystemdSpec.readiness` (ReadinessHttpGet) and `SystemdSpec.requires` were
parsed and validated but read nowhere — no generator, CLI or API touched
either. Both looked load-bearing and were not:

- readiness duplicated `expose.http.health_path`, which wildpc-api already
  polls. wild-life-api.yaml declared both spellings of the same /health
  probe; only one did anything.
- requires would mean systemd `Requires=`, a lifecycle binding (stop
  postgres, stop every dependent). `manage.systemd.after` already gives the
  ordering callers actually want, and a deployment's own `requires:` is the
  relationship model, not systemd wiring.

Unknown keys parse as ignored, so existing deployment YAML keeps loading.
2026-10-16 10:42:54 -07:00
Paul Payne
0fefa143e4 core: bind a raw-TCP service as <name>.<domain>:<port>
A `{ref, bind}` to a TCP-exposed deployment (e.g. postgres) projected
nothing, so consumers hardcoded the docker bridge IP. Project the address
it's already documented as reachable at.
2026-10-16 03:11:38 -07:00
Paul Payne
55c2f269ec core: GPU containers, bind sub-paths, and stop containers on unit stop
- container launcher: `gpus:` passes through as docker `--gpus`.
- requires: `path:` is appended to the bound URL, for consumers that want a
  sub-path as their base (e.g. an OpenAI-compatible server's /v1).
- container services now get `ExecStop=-docker stop wildpc-<name>`. SIGTERM
  only reached the `docker run` client, so a stop/restart left the container
  running unsupervised and holding its name, and the next start failed with
  a name conflict until systemd gave up.
2026-10-16 03:10:01 -07:00
Paul Payne
8a9843658c core: back off and give up on crash-looping services
Generated service units took systemd's default start limit (5 starts in
10s), which can never trip when each failed attempt outlives the window.
openclaw failed ~5s in and restarted every ~5.5s, so the counter reset on
every cycle: 260k restarts at ~7.4s CPU each, over a core burned
continuously for days, with the unit stuck in `activating (auto-restart)`
rather than anything a status check would flag.

Service units now get exponential restart backoff (RestartSteps=8,
RestartMaxDelaySec=300) and a start limit sized against it (10 starts per
hour, in [Unit] where systemd reads it). A broken service retries with a
growing delay for ~17min — wide enough to ride out a boot-time dependency
race — then lands in `failed` where it's visible and stops consuming CPU.
All four values are SystemdSpec fields, overridable per deployment.

Timer-driven oneshot jobs are unaffected; they never restart. systemd
older than 254 ignores RestartSteps/RestartMaxDelaySec with a warning and
keeps the flat RestartSec, so the start limit still applies there.
2026-08-24 09:24:16 -07:00
Paul Payne
6cf25ad2c7 core: evaluate node semver ranges instead of guessing the leading major
resolve_node_bin() could not parse a range, so it fell back to "pin by the
first number in the string" and took the newest installed version of that
major. For openclaw's engines pin `>=22.19.0 <23 || >=23.11.0` that picked
v22.17.0 — below the range's own floor — and baked it into the service's
unit PATH. The daemon refused to start on it and crash-looped 260k+ times
(~7.4s CPU per attempt, every ~5.5s, since before the journal's retention
window). StartLimitBurst never tripped because five attempts span ~27s,
outside the 10s StartLimitIntervalUSec.

Parse the pin properly: split on `||`, expand each comparator (>=, >, <=,
<, =, ^, ~, hyphen ranges, x/* wildcards, partial versions) into bounds
with node-semver's partial semantics, and pick the newest installed version
satisfying any alternative. openclaw now resolves to v24.18.0 via its
second alternative. A pin this parser cannot read at all still falls back
to newest-installed rather than guessing a version.

When nothing satisfies the pin it now fails with the installed versions
listed, so the next occurrence is diagnosable at apply time rather than
six days later in a restart loop.
2026-08-21 10:37:26 -07:00
Paul Payne
15957677ff gateway: serve the dashboard at wild-pc.<domain>, 404 unrouted subdomains
The dashboard's deployment kept the pre-rename name `wildpc`, so it was
published at wildpc.<domain> while everything else had moved to Wild PC
naming. Rename the deployment and its program to `wild-pc` (the subdomain
is the deployment name) and follow the `_DASHBOARD` constant in both the
Caddyfile generator and doctor — that constant also drives the `:9000`
redirect and doctor's `wildpc program build` hint, which is why the
program is renamed alongside the deployment.

Browsing the old name exposed a second problem: in acme mode every route
lives inside one `*.<domain>` wildcard site, so an unrouted host still
matches the site and completes its TLS handshake. Caddy then ran off the
end of the block and returned a bare 200 with an empty body — a typo'd or
renamed subdomain looked *up* in a browser while rendering nothing. Each
wildcard site now ends with an unmatched `handle` that responds 404.

The API subdomain is untouched: the dashboard derives its API origin by
stripping the first label, so wild-pc.<domain> still resolves to
wildpc-api.<domain>, and the WebSocket origin gate matches on
gateway.domain rather than a hostname.

Also seed the bootstrap dashboard deployment with the `requires:
wildpc-api` block the live deployment already carried, so a fresh install
declares the dependency.
2026-08-02 16:37:04 -07:00
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