# Castle A personal software platform. Castle manages independent services, tools, and frontends from a single CLI, with a unified gateway, systemd integration, and a web dashboard. ## Quick Start ```bash # Install the castle CLI cd cli && uv tool install --editable . && cd .. # Sync all projects (git submodules + dependencies) castle sync # See what's here castle list # Start everything (all services + Caddy gateway) castle services start # Visit the dashboard open http://localhost:9000 ``` ## Creating Components ```bash # Service — FastAPI app with health endpoint, systemd unit, gateway route castle create my-api --type service --description "Does something useful" cd my-api && uv sync castle test my-api castle service enable my-api castle gateway reload # Standalone tool — CLI tool with argparse, stdin/stdout, Unix pipes castle create my-tool --type tool --description "Does something" # Category tool — adds to existing tools// package castle create my-tool --type tool --category document --description "Converts something" ``` ## CLI Reference ``` castle list [--role ROLE] [--json] List all components castle info NAME [--json] Show component details castle create NAME --type TYPE Scaffold a new component castle run NAME Run component in foreground castle test [NAME] Run tests (one or all) castle lint [NAME] Run linter (one or all) castle sync Update submodules + install deps castle logs NAME [-f] [-n 50] View component logs castle gateway start|stop|reload Manage Caddy reverse proxy castle service enable|disable NAME Manage a systemd service castle service status Show all service statuses castle services start|stop Start/stop everything castle tool list List tools grouped by category castle tool info NAME Show tool details + docs ``` ## Registry `castle.yaml` is the single source of truth. Components declare **what they do** (run, expose, manage, install, build, triggers) and roles are **derived** from those declarations. ```yaml gateway: port: 9000 components: central-context: description: Content storage API run: runner: python_uv_tool tool: central-context working_dir: central-context env: CENTRAL_CONTEXT_DATA_DIR: /data/castle/central-context CENTRAL_CONTEXT_PORT: "9001" expose: http: internal: { port: 9001 } health_path: /health proxy: caddy: { path_prefix: /central-context } manage: systemd: {} ``` ## Architecture ``` castle.yaml <- component registry (single source of truth) cli/ <- castle CLI castle-api/ <- Castle API (dashboard backend) app/ <- Castle web app (React/Vite frontend) central-context/ <- content storage API (git submodule) notification-bridge/ <- desktop notification forwarder (git submodule) protonmail/ <- email sync tool/job devbox-connect/ <- SSH tunnel manager tools/ <- category tool packages (document/, search/, system/, etc.) ruff.toml <- shared lint config pyrightconfig.json <- shared type checking config ``` **Independence principle:** Services never depend on castle. They accept configuration (data dir, port, URLs) via environment variables. Only castle components (CLI, API, gateway) know about castle internals. **Gateway:** Caddy reverse proxy at port 9000. Services are proxied under one address (`localhost:9000/central-context/*` -> `localhost:9001/*`). The web app is served at the root. **Systemd:** The CLI generates user units under `~/.config/systemd/user/castle-*.service`. Scheduled jobs get `.timer` files alongside. **Data:** Service data lives in `/data/castle//`, outside the repo. Secrets live in `~/.castle/secrets/`. ## Components ### Services | Component | Port | Description | |-----------|------|-------------| | castle-gateway | 9000 | Caddy reverse proxy gateway | | central-context | 9001 | Content storage API | | notification-bridge | 9002 | Desktop notification forwarder | | castle-api | 9020 | Castle API (dashboard backend) | ### Jobs | Component | Schedule | Description | |-----------|----------|-------------| | protonmail | Every 5 min | ProtonMail email sync | | backup-collect | 2:00 AM | Collect files into backup directory | | backup-data | 3:30 AM | Restic backup of /data to /storage | ### Tools | Tool | Category | Description | |------|----------|-------------| | docx2md | document | Convert Word .docx to Markdown | | pdf2md | document | Convert PDF to Markdown | | html2text | document | Convert HTML to plain text | | md2pdf | document | Convert Markdown to PDF | | mbox2eml | document | Convert MBOX mailboxes to .eml files | | search | search | Manage searchable file collections | | docx-extractor | search | Extract content from Word files | | pdf-extractor | search | Extract content from PDF files | | text-extractor | search | Extract content from text files | | schedule | system | Manage systemd user timers | | backup-collect | system | Collect files for backup | | android-backup | android | Backup Android devices via ADB | | browser | browser | Browse the web via browser-use | | gpt | gpt | OpenAI text generation | | mdscraper | mdscraper | Combine text files into markdown | | devbox-connect | standalone | SSH tunnel manager | ### Frontends | Component | Description | |-----------|-------------| | castle-app | Castle web dashboard (React/Vite/TypeScript) |