Clean up.
This commit is contained in:
@@ -31,8 +31,9 @@ is self-sufficient. The mesh is optional.
|
||||
operates above the line.
|
||||
|
||||
5. **Separate source from runtime.** The repo is for development. The
|
||||
runtime lives in standard Unix locations (`~/.castle/`, `/data/castle/`,
|
||||
systemd units). Nothing running should point into the source tree.
|
||||
runtime lives in standard Unix locations (`$CASTLE_HOME`, default
|
||||
`~/.castle/`, plus systemd units). Nothing running should point into the
|
||||
source tree.
|
||||
|
||||
6. **AI-manageable.** The CLI and API exist so that AI assistants can
|
||||
discover, create, and manage components programmatically. Humans
|
||||
@@ -113,9 +114,10 @@ Manages running processes using standard Linux infrastructure.
|
||||
- TLS termination
|
||||
|
||||
**Filesystem** handles storage:
|
||||
- Service data: `/data/castle/<name>/`
|
||||
- Secrets: `~/.castle/secrets/`
|
||||
- Generated config: `~/.castle/generated/`
|
||||
- Service data: `$CASTLE_DATA_DIR/<name>/` (default `/data/castle/`, on a
|
||||
dedicated volume)
|
||||
- Secrets: `$CASTLE_HOME/secrets/` (default `~/.castle/secrets/`)
|
||||
- Generated config: `$CASTLE_HOME/artifacts/specs/` (Caddyfile, registry.yaml)
|
||||
|
||||
Castle generates systemd unit files and Caddyfile entries from the
|
||||
registry. It doesn't run a daemon itself — it configures OS-level
|
||||
@@ -123,8 +125,8 @@ infrastructure and gets out of the way.
|
||||
|
||||
Critically, the runtime layer references only standard paths — never
|
||||
the source tree. Systemd units point to installed binaries (on PATH
|
||||
or in `~/.castle/bin/`), not to repo subdirectories. Caddy serves
|
||||
from `~/.castle/static/`, not from build output directories in the repo.
|
||||
or in `~/.local/bin/`), not to repo subdirectories. Caddy serves
|
||||
from `$CASTLE_HOME/artifacts/content/`, not from build output directories in the repo.
|
||||
|
||||
### Registry Layer
|
||||
|
||||
@@ -156,7 +158,7 @@ These map to two files:
|
||||
programs:
|
||||
central-context:
|
||||
description: Content storage API
|
||||
source: components/central-context
|
||||
source: code/central-context
|
||||
|
||||
services:
|
||||
central-context:
|
||||
@@ -197,7 +199,7 @@ Convention-based env vars (`<PREFIX>_PORT`, `<PREFIX>_DATA_DIR`) are
|
||||
generated automatically during deploy. Only non-convention values need
|
||||
`defaults.env`.
|
||||
|
||||
**`~/.castle/registry.yaml`** (per-node, not in the repo) — Node config:
|
||||
**`$CASTLE_HOME/artifacts/specs/registry.yaml`** (per-node, not in the repo, generated by `castle deploy`) — Node config:
|
||||
|
||||
```yaml
|
||||
node:
|
||||
@@ -209,7 +211,7 @@ deployed:
|
||||
runner: python
|
||||
run_cmd: [/home/user/.local/bin/central-context]
|
||||
env:
|
||||
CENTRAL_CONTEXT_DATA_DIR: /data/castle/central-context
|
||||
CENTRAL_CONTEXT_DATA_DIR: /home/user/.castle/data/central-context
|
||||
CENTRAL_CONTEXT_PORT: "9001"
|
||||
behavior: daemon
|
||||
stack: python-fastapi
|
||||
@@ -302,7 +304,7 @@ Local paths always take precedence.
|
||||
### Dashboard
|
||||
|
||||
The web dashboard (`castle-app`) is a React SPA served by Caddy from
|
||||
`~/.castle/static/castle-app/`. It talks to `castle-api` via the
|
||||
`$CASTLE_HOME/artifacts/content/castle-app/`. It talks to `castle-api` via the
|
||||
gateway proxy at `/api`.
|
||||
|
||||
**Layout:**
|
||||
@@ -417,9 +419,10 @@ Each step is distinct:
|
||||
execute them implicitly.
|
||||
|
||||
2. **Install** — Makes the artifact available on the system. For tools:
|
||||
`uv tool install` or compiled binary placed in `~/.castle/bin/`. For
|
||||
`uv tool install` or compiled binary placed in `~/.local/bin/`. For
|
||||
services: same — the binary or entry point is on PATH or in a known
|
||||
location. For frontends: built assets copied to `~/.castle/static/`.
|
||||
location. For frontends: built assets copied to
|
||||
`$CASTLE_HOME/artifacts/content/`.
|
||||
|
||||
3. **Deploy** — Materializes the runtime configuration. Reads the
|
||||
component spec, merges with node config, generates systemd units
|
||||
@@ -427,28 +430,32 @@ Each step is distinct:
|
||||
the source tree. Enables and starts services.
|
||||
|
||||
For compiled languages (Rust, Go), build produces a standalone binary
|
||||
and install is just placing it in `~/.castle/bin/`. For interpreted
|
||||
and install is just placing it in `~/.local/bin/`. For interpreted
|
||||
languages (Python, Node), the runtime wrapper (uv, node) handles
|
||||
finding the installed artifact.
|
||||
|
||||
## Runtime Filesystem Layout
|
||||
|
||||
What already exists and what the target looks like:
|
||||
Two roots, each overridable by an env var: `$CASTLE_HOME` (config, code,
|
||||
artifacts, secrets; default `~/.castle`) and `$CASTLE_DATA_DIR` (bulk program
|
||||
data; default `/data/castle`, kept on a dedicated volume):
|
||||
|
||||
```
|
||||
~/.castle/ ← Castle runtime home
|
||||
├── registry.yaml ← Node config (what's deployed here)
|
||||
├── generated/ ← Generated Caddyfile
|
||||
│ └── Caddyfile
|
||||
├── secrets/ ← Secret files (NAME → value)
|
||||
│ └── PROTONMAIL_API_KEY
|
||||
├── bin/ ← Compiled binaries, shims
|
||||
│ └── my-go-tool
|
||||
└── static/ ← Built frontend assets
|
||||
└── castle-app/
|
||||
└── dist/
|
||||
$CASTLE_HOME/ ← Config & artifacts (default ~/.castle)
|
||||
├── castle.yaml ← Registry spec (programs, services, jobs)
|
||||
├── infra.conf ← Infrastructure install choices
|
||||
├── code/ ← Program source (your programs)
|
||||
│ └── <name>/
|
||||
├── artifacts/
|
||||
│ ├── specs/ ← Generated by `castle deploy`
|
||||
│ │ ├── Caddyfile
|
||||
│ │ └── registry.yaml ← Node config (what's deployed here)
|
||||
│ └── content/ ← Built frontend assets
|
||||
│ └── castle-app/ ← (index.html + assets, served at root)
|
||||
└── secrets/ ← Secret files (NAME → value)
|
||||
└── PROTONMAIL_API_KEY
|
||||
|
||||
/data/castle/ ← Persistent service data
|
||||
$CASTLE_DATA_DIR/ ← Persistent service data (default /data/castle)
|
||||
└── <name>/
|
||||
|
||||
~/.config/systemd/user/ ← Systemd units (standard location)
|
||||
@@ -458,8 +465,11 @@ What already exists and what the target looks like:
|
||||
└── ...
|
||||
```
|
||||
|
||||
Compiled-language tools (Rust, Go — planned) install their binaries to the
|
||||
standard `~/.local/bin/`, not under `$CASTLE_HOME`.
|
||||
|
||||
Source (the repo) is referenced only during build and install. Everything
|
||||
the runtime touches lives in `~/.castle/`, `/data/castle/`, or standard
|
||||
the runtime touches lives under `$CASTLE_HOME`, `$CASTLE_DATA_DIR`, or standard
|
||||
systemd paths.
|
||||
|
||||
## OTP as Design Guide
|
||||
@@ -470,7 +480,7 @@ Castle's architecture parallels Erlang/OTP, mapped onto Unix:
|
||||
|-------------|------------------|
|
||||
| Application | Component (independent, self-contained) |
|
||||
| Application resource file | Component spec in `castle.yaml` |
|
||||
| Release config (sys.config) | Node config in `~/.castle/registry.yaml` |
|
||||
| Release config (sys.config) | Node config in `$CASTLE_HOME/artifacts/specs/registry.yaml` |
|
||||
| Release assembly | `castle deploy` (spec + node config → runtime) |
|
||||
| Supervisor | systemd (restart policies, ordering) |
|
||||
| Process | Running service/worker/job |
|
||||
@@ -508,17 +518,17 @@ What exists today:
|
||||
- **Three packages** — `castle-core` (models, config, generators),
|
||||
`castle-cli` (commands), `castle-api` (HTTP API)
|
||||
- **Source/runtime split** — `castle.yaml` (spec) → `castle deploy` →
|
||||
`~/.castle/registry.yaml` (node config). Systemd units and Caddyfile
|
||||
generated from registry with fully resolved paths. No repo references
|
||||
`$CASTLE_HOME/artifacts/specs/registry.yaml` (node config). Systemd units and
|
||||
Caddyfile generated from registry with fully resolved paths. No repo references
|
||||
in runtime artifacts.
|
||||
- **Convention-based env generation** — `castle deploy` auto-generates
|
||||
`<PREFIX>_DATA_DIR=/data/castle/<name>` and `<PREFIX>_PORT` from
|
||||
`<PREFIX>_DATA_DIR=$CASTLE_DATA_DIR/<name>` and `<PREFIX>_PORT` from
|
||||
the manifest. Only non-convention values need `defaults.env`.
|
||||
- **Gateway** — Caddy on port 9000, Caddyfile generated from registry
|
||||
- **API** — `castle-api` on port 9020, reads from registry (optional
|
||||
castle.yaml fallback for non-deployed components)
|
||||
- **Dashboard** — `castle-app` React/Vite frontend, static assets
|
||||
served from `~/.castle/static/castle-app/`
|
||||
served from `$CASTLE_HOME/artifacts/content/castle-app/`
|
||||
- **Services** — central-context (content storage), notification-bridge
|
||||
(desktop notification forwarder)
|
||||
- **Jobs** — protonmail (email sync every 5 min), backup-collect (nightly),
|
||||
@@ -531,7 +541,7 @@ What exists today:
|
||||
Opt-in via `CASTLE_API_MQTT_ENABLED` / `CASTLE_API_MDNS_ENABLED`.
|
||||
- **MQTT broker** — Mosquitto running as `castle-mqtt` Docker container
|
||||
on port 1883, managed by systemd. Config and data in
|
||||
`/data/castle/castle-mqtt/`.
|
||||
`$CASTLE_DATA_DIR/castle-mqtt/`.
|
||||
- **Node API** — `GET /mesh/status`, `GET /nodes`, `GET /nodes/{hostname}`.
|
||||
`GET /components?include_remote=true` for cross-node component listing.
|
||||
- **Gateway panel** — Dedicated UI showing route table, health per route,
|
||||
@@ -559,7 +569,7 @@ What doesn't exist yet:
|
||||
| Process supervision | systemd (user units) | Active |
|
||||
| HTTP routing | Caddy (port 9000) | Active |
|
||||
| Component specs | castle.yaml + Pydantic models | Active |
|
||||
| Node config | `~/.castle/registry.yaml` | Active |
|
||||
| Node config | `$CASTLE_HOME/artifacts/specs/registry.yaml` | Active |
|
||||
| CLI | castle (Python, uv) | Active |
|
||||
| API | castle-api (FastAPI) | Active |
|
||||
| Dashboard | castle-app (React, Vite, shadcn/ui) | Active |
|
||||
@@ -569,7 +579,7 @@ What doesn't exist yet:
|
||||
| Type checking | pyright (Python), tsc (TS) | Active |
|
||||
| Testing | pytest (Python), Vitest (TS) | Active |
|
||||
| Secrets | `~/.castle/secrets/` file-based | Active |
|
||||
| Data storage | Filesystem (`/data/castle/`) | Active |
|
||||
| Data storage | Filesystem (`$CASTLE_DATA_DIR/`, default `/data/castle/`) | Active |
|
||||
| Messaging | MQTT (paho-mqtt client, Mosquitto broker) | Active (opt-in) |
|
||||
| Node discovery | mDNS (python-zeroconf) + MQTT | Active (opt-in) |
|
||||
| Rust packaging | cargo | Planned |
|
||||
|
||||
Reference in New Issue
Block a user