Refactor component terminology to programs in config and manifest

- Updated the terminology from "components" to "programs" across the codebase, including in config loading, saving, and manifest specifications.
- Introduced a new `stacks.py` file to handle lifecycle actions for development stacks, implementing handlers for Python and React Vite stacks.
- Adjusted tests to reflect the new program structure and ensure proper functionality.
- Revised documentation to align with the new terminology and structure, ensuring clarity on the purpose and configuration of programs, services, and jobs.
This commit is contained in:
2026-02-23 22:09:41 -08:00
parent f559fba143
commit 0d36e4f72a
48 changed files with 7512 additions and 632 deletions

View File

@@ -1,21 +1,21 @@
# Component Registry
# Registry
How castle tracks, configures, and manages components. This is the central
reference for `castle.yaml` structure and the registry architecture.
How castle tracks, configures, and manages programs, services, and jobs.
This is the central reference for `castle.yaml` structure and the registry
architecture.
## castle.yaml
The single source of truth for all components. Lives at the repo root.
Three top-level sections:
The single source of truth. Lives at the repo root. Three top-level sections:
```yaml
gateway:
port: 9000
components:
programs:
my-tool:
description: Does something useful
source: components/my-tool
source: programs/my-tool
install:
path: { alias: my-tool }
tool:
@@ -51,22 +51,22 @@ jobs:
| Section | Purpose | Category |
|---------|---------|----------|
| `components:` | Software catalog — what exists | tool, frontend, component |
| `programs:` | Software catalog — what exists | tool, frontend, program |
| `services:` | Long-running daemons — how they run | service |
| `jobs:` | Scheduled tasks — when they run | job |
Services and jobs can reference a component via `component:` for description
Services and jobs can reference a program via `component:` for description
fallthrough and source code linking. They can also exist independently
(e.g., `castle-gateway` runs Caddy — not our software).
## Component blocks
## Program blocks
Components define **what software exists** — identity, source, tools, builds.
Programs define **what software exists** — identity, source, tools, builds.
### `source` — Where the source lives
```yaml
source: components/my-tool
source: programs/my-tool
```
Relative path from repo root to the project directory.
@@ -92,7 +92,7 @@ tool:
This block provides metadata for `castle tool list` and the dashboard.
It's separate from `install` (which handles PATH registration). The source
directory is set via the top-level `source` field on the component, not here.
directory is set via the top-level `source` field on the program, not here.
### `build` — How to build it
@@ -203,7 +203,7 @@ Castle generates a systemd `.timer` file alongside the `.service` unit.
Jobs also support `run` (required), `manage`, and `defaults` — same
semantics as services.
## Registering a new component
## Registering a new program
### Via `castle create` (recommended)
@@ -211,7 +211,7 @@ semantics as services.
# Service — scaffolds project, assigns port, registers in castle.yaml
castle create my-service --stack python-fastapi --description "Does something"
# Tool — scaffolds under components/
# Tool — scaffolds under programs/
castle create my-tool --stack python-cli --description "Does something"
```
@@ -220,20 +220,20 @@ castle create my-tool --stack python-cli --description "Does something"
Add entries to the appropriate sections of `castle.yaml`:
```yaml
# Tool — only needs a component entry
components:
# Tool — only needs a program entry
programs:
my-tool:
description: Does something useful
source: components/my-tool
source: programs/my-tool
install:
path:
alias: my-tool
# Service — needs both component and service entries
components:
# Service — needs both program and service entries
programs:
my-service:
description: Does something useful
source: components/my-service
source: programs/my-service
services:
my-service:
@@ -257,7 +257,7 @@ services:
```bash
castle create my-service --stack python-fastapi # 1. Scaffold + register
cd components/my-service && uv sync # 2. Install deps
cd programs/my-service && uv sync # 2. Install deps
# ... implement ...
castle test my-service # 3. Run tests
castle service enable my-service # 4. Generate systemd unit, start
@@ -277,10 +277,10 @@ castle service disable my-service # Stop and remove systemd unit
```bash
castle create my-tool --stack python-cli # 1. Scaffold + register
cd components/my-tool && uv sync # 2. Install deps
cd programs/my-tool && uv sync # 2. Install deps
# ... implement ...
castle test my-tool # 3. Run tests
uv tool install --editable components/my-tool/ # 4. Install to PATH
uv tool install --editable programs/my-tool/ # 4. Install to PATH
```
### Job lifecycle
@@ -306,7 +306,7 @@ and a `.timer` file.
| What | Where |
|------|-------|
| Component registry | `castle.yaml` (repo root) |
| Registry | `castle.yaml` (repo root) |
| Service data | `/data/castle/<name>/` |
| Secrets | `~/.castle/secrets/<NAME>` |
| Generated Caddyfile | `~/.castle/generated/Caddyfile` |
@@ -317,7 +317,7 @@ and a `.timer` file.
The Pydantic models live in `core/src/castle_core/manifest.py`. Key classes:
- `ComponentSpec` — software catalog entry (source, install, tool, build)
- `ProgramSpec` — software catalog entry (source, install, tool, build)
- `ServiceSpec` — long-running daemon (run, expose, proxy, manage, defaults)
- `JobSpec` — scheduled task (run, schedule, manage, defaults)
- `RunSpec` — discriminated union (RunPython, RunCommand, RunContainer, RunNode, RunRemote)
@@ -325,7 +325,7 @@ The Pydantic models live in `core/src/castle_core/manifest.py`. Key classes:
- `CaddySpec`, `SystemdSpec`, `HttpExposeSpec`, `HttpInternal`
Config loading: `core/src/castle_core/config.py``load_config()` parses
castle.yaml into `CastleConfig` with typed `components`, `services`, and
castle.yaml into `CastleConfig` with typed `programs`, `services`, and
`jobs` dicts.
Infrastructure generators: `core/src/castle_core/generators/` — systemd unit/timer

View File

@@ -153,7 +153,7 @@ These map to two files:
**`castle.yaml`** (in the repo, version-controlled) — Three sections:
```yaml
components:
programs:
central-context:
description: Content storage API
source: components/central-context
@@ -185,7 +185,7 @@ jobs:
systemd: {}
```
Components define *what software exists* (identity, source, install, tools).
Programs define *what software exists* (identity, source, install, tools).
Services define *how daemons run* (run config, expose, proxy, systemd).
Jobs define *how scheduled tasks run* (run config, cron schedule, systemd).

View File

@@ -314,7 +314,7 @@ uv run ruff format . # Format
## Registering in castle.yaml
```yaml
components:
programs:
my-tool:
description: Does something useful
source: components/my-tool
@@ -326,7 +326,7 @@ components:
Tools with system dependencies declare them in the component:
```yaml
components:
programs:
pdf2md:
description: Convert PDF files to Markdown
source: components/pdf2md
@@ -337,7 +337,7 @@ components:
system_dependencies: [pandoc, poppler-utils]
```
Tools live in the `components:` section. If a tool also runs on a schedule,
Tools live in the `programs:` section. If a tool also runs on a schedule,
add a separate entry in the `jobs:` section referencing the component.
See @docs/component-registry.md for the full registry reference.

View File

@@ -99,7 +99,7 @@ settings = Settings()
Castle passes config via env vars in castle.yaml:
```yaml
components:
programs:
my-service:
description: Does something useful
source: components/my-service

View File

@@ -108,12 +108,12 @@ The `build` output is a static SPA in `dist/` — just HTML, JS, and CSS files.
## Registering as a castle component
A frontend component has a `build` spec (produces static output). Register it
in the `components:` section of `castle.yaml`. No `run` block needed if Caddy
in the `programs:` section of `castle.yaml`. No `run` block needed if Caddy
handles serving directly from the build output.
```yaml
# castle.yaml
components:
programs:
my-frontend:
description: Web dashboard
source: components/my-frontend