Commit Graph

25 Commits

Author SHA1 Message Date
Paul Payne
fc5802d823 Add CA download to dashboard: GET /gateway/ca.crt + Gateway-panel button
When the gateway serves internal-CA HTTPS (gateway.tls=internal), other devices
must trust Caddy's root CA to accept *.lan HTTPS. Surface that from the dashboard
instead of hand-copying root.crt around.

- API: GET /gateway/ca.crt returns the public root cert (PEM) as a download,
  sourced from Caddy's admin API (/pki/ca/local — matches the running gateway)
  with an on-disk root.crt fallback. 404 unless tls=internal. GatewayInfo gains
  `tls` and `ca_fingerprint` (SHA-256, for out-of-band verification). The CA
  private key is never exposed — only the public root.
- UI: a "CA cert" button in the Gateway panel, shown only when tls=internal,
  linking to the download with the fingerprint in its tooltip.
- Tests for the tls=off defaults and the 404-without-internal-tls path; docs note
  the button + endpoint.
2026-06-29 08:26:15 -07:00
Paul Payne
0d9c1ae6c2 Add gateway.tls=internal: HTTPS for host routes via Caddy's local CA
The gateway was HTTP-only by design, but a plain-HTTP origin is not a browser
"secure context", so apps that need WebCrypto (device identity, E2E crypto)
can't run when proxied — only localhost or HTTPS qualifies.

Add an opt-in `gateway.tls: internal`. When set, each host route
(proxy.caddy.host) is emitted as its own `<host> { tls internal; reverse_proxy }`
site, served over HTTPS by Caddy's local CA (Caddy binds :443 and redirects :80).
Path-prefix/static routes stay on the HTTP :<port> site — give a service a host
route to put it on HTTPS. Default (unset/off) keeps the existing HTTP-only
Caddyfile (single :<port> block, auto_https off, host matchers inline).

Plumbing mirrors gateway_port: GatewayConfig.tls (castle.yaml load/save) →
NodeConfig.gateway_tls (registry load/save, set in deploy) → the Caddyfile
generator. Tests cover both modes; docs/registry.md documents the option plus
the two operational requirements (binding 443/80 via the unprivileged-port
sysctl, and trusting Caddy's root CA on clients) and the host-route-vs-prefix
guidance that motivates it.
2026-06-29 08:13:17 -07:00
Paul Payne
5987f01749 Refactor configuration handling to use directory-per-resource layout
- Introduced `_write_castle_config` function to scatter nested config into separate YAML files for programs, services, and jobs.
- Updated `castle_root` fixture to utilize the new config writing method.
- Added tests for configuration aggregation and scattering in `test_health.py`.
- Removed legacy unit handling from `manifest.py` and refactored related code in `config.py`.
- Updated documentation to reflect new configuration directory structure.
- Removed obsolete unit tests related to unit expansion.
2026-06-28 14:01:18 -07:00
Paul Payne
26a00c4073 refactor: Run python services in place via uv run
A python-runner service ran from a tool venv (uv tool install --editable),
separate from the project venv that uv sync manages. The editable link kept
source live, but dependencies were resolved independently in each venv, so a
new dependency landed in the project venv and never reached the running
service — leaving no clean rollout for it.

Deploy now emits ExecStart=uv run --project <source> --no-dev <program>,
running the service in place from its own project venv. uv run syncs the env
to the lockfile before launching, so a single restart picks up both code and
dependency changes; the ExecStart is deterministic from source and never goes
stale. The tool-venv install is gated to the command runner only; uv tool
install is reserved for tool-behavior programs, where PATH is the point. A
python service with no resolvable source falls back to a PATH lookup.
2026-06-17 11:03:11 -07:00
Paul Payne
c40f84104d feat: unified gateway route table (static · proxy · remote)
The gateway does two things — reverse-proxy services and serve static
frontends — but the dashboard/API route table only ever showed the proxy
routes, so 'serving a frontend' and 'proxying a service' looked like unrelated
features and castle-app/power-graph-app were invisible in the route view.

Now there's one concept: a route maps an address (path '/foo' or host 'foo.lan')
to a target of one kind — static (a built dist served by file_server), proxy
(a local service port), or remote (a service on another node).

- core: caddyfile.py gains compute_routes() — the single source of truth for
  the route list; generate_caddyfile_from_registry renders it (output byte-for-
  byte identical, verified against the live Caddyfile). Adds a GatewayRoute
  dataclass.
- api: GatewayRoute model → {address, kind, target, name, node}; GET /gateway
  builds from compute_routes (incl. config for static frontends + mesh for
  remote), so the table matches what Caddy actually does.
- app: Gateway panel shows Address · Kind · Target for every route (static
  frontends + host routes now appear); program detail shows 'Reachable at
  /foo/ · served (static)' for static frontends.
- cli: 'castle gateway status' prints the full route table.
- docs: registry.md/design.md/CLAUDE.md describe routes as one concept with
  three target kinds.

core 94 / cli 24 / api 52 green; ruff + app build clean; Caddyfile unchanged.
2026-06-14 17:48:35 -07:00
Paul Payne
7314b5cddb refactor(env): explicit defaults.env with placeholders; drop auto-injection
A service/job's env is now exactly its defaults.env — castle injects no hidden
convention vars. Values support ${port}/${data_dir}/${name} placeholders
(resolved at deploy, alongside ${secret:…}), so a program's own env var names
map to castle's computed values without hardcoding.

Why: the auto-injected <PREFIX>_PORT/<PREFIX>_DATA_DIR were a guess at the
program's env names — right for castle-scaffolded services, dead weight for
adopted ones (lakehouse carried two dead vars; notification-bridge/backup jobs
too). They also weren't visible in the config editor (computed at deploy), which
was the source of the 'four env vars but the UI shows none' mystery.

- core: resolve_env_vars gains a context (${port}/${data_dir}/${name});
  deploy builds env from defaults.env only — no <PREFIX>_* injection, no
  port_env. Removed the port_env field and the dead _env_prefix helper.
- cli: 'service/job create' gains repeatable --env KEY=VALUE (replaces
  --port-env); 'program create' scaffolds <PREFIX>_PORT/_DATA_DIR: ${…} for new
  daemons.
- app: removed the 'Port env' field; the Environment editor (defaults.env) is
  the single place, with a placeholder hint.
- live migration: central-context/castle-api/power-graph/protonmail mapped their
  real vars explicitly; lakehouse → just LAKEHOUSED_DAEMON_PORT: ${port}, data
  stays in ~/.lakehoused. Verified all services healthy on their ports, dead
  vars gone, zero failed units.
- docs: registry.md/design.md/stack guides + findings updated to the explicit
  model.

core 94 / cli 24 / api 52 green; ruff + app build clean.
2026-06-14 17:06:46 -07:00
Paul Payne
fd562f7468 docs: clean up pre-existing staleness across the guides
Sweep of accumulated drift unrelated to the CLI reorg:
- Source paths: ~/.castle/code/<name> → /data/repos/<name> (REPOS_DIR), and
  source: code/X → absolute /data/repos/X. registry.md source-resolution table
  and 'how programs get in' section rewritten for the /data/repos layout.
- Old field name component: → program: in all YAML examples.
- Old term 'component' → 'program' throughout design.md and the stack guides.
- Frontend serving: corrected the copy-to-artifacts/content model to serve-in-
  place from the repo (<source>/<dist>), matching Phase 2b; dropped the dead
  _copy_app_static reference and the 'runtime never references the source tree'
  claim (frontends are the deliberate exception).
- Stale endpoints/commands: /components → /deployments; castle create/add →
  castle program create/add in the create.py/add.py docstrings.

CLAUDE.md was already current. cli 24 green; ruff clean.
2026-06-14 16:32:13 -07:00
Paul Payne
5c6d8ec6f1 refactor(cli): resource-first command surface
Names collide across resource types (a program and a service can share a name),
so the CLI no longer resolves bare names. Operations live under the resource
they act on; platform lifecycle and the cross-resource overview stay top-level.

  castle program  list|info|create|add|clone|delete|run|install|uninstall|
                  build|test|lint|format|type-check|check
  castle service  list|info|create|delete|deploy|enable|disable|start|stop|restart|logs
  castle job      <same verbs>   (create takes --schedule)
  castle gateway  start|stop|reload|status
  castle list | status | deploy [name]        # cross-cutting
  castle start | stop | restart               # whole platform (systemd verbs)

Key changes:
- info/delete/list/run are resource-scoped (no more cross-section name match).
- delete only removes the named resource's entry; program delete still blocks
  when a deployment references it.
- per-resource start/stop/restart (systemctl on the unit/timer); top-level
  start/stop/restart act on the whole platform.
- expose → 'service create' (more general: --program ref, --runner); gained
  'job create' (the CLI never had one).
- dropped up/down — bringing things online is the honest 'deploy && start'.

Docs (CLAUDE.md, registry.md, stack guides) and user-facing messages updated.
core 94 / cli 24 / api 52 green; ruff clean. Verified live: program/service/job
list+info scope correctly, service create+scoped-delete round-trips.
2026-06-14 16:25:34 -07:00
Paul Payne
4840cbe72a feat: castle UX fixes from the lakehouse adoption test
Driven by adopting lakehouse (an existing daemon that bundles its own SPA) —
see docs/findings-lakehouse.md.

#3 deploy reloads the gateway. castle deploy regenerated the Caddyfile but
   left the running Caddy on the old config, so new proxy routes were silently
   dead. Deploy now reloads the gateway when it's running.

#1 castle expose <program>. Turns an adopted program into a service
   (run/port/health/proxy/systemd) in one command — the missing
   daemon-to-service step. Flags: --port --health --path --run --port-env
   --host --no-proxy.

#2 port_env mapping. A service can declare expose.http.internal.port_env so
   castle sets the env var the program actually reads (e.g. lakehoused reads
   LAKEHOUSED_DAEMON_PORT, not castle's convention LAKEHOUSE_PORT). Castle now
   genuinely drives an adopted daemon's bind port.

#4a auto-base for react-vite. The build passes VITE_BASE = the gateway serve
   prefix (/<name>/, or / for castle-app); vite.config reads it. A castle-built
   frontend now works at its subpath with no hand-tuned base. castle-app's
   vite.config updated as the reference.

#4b host-based routing. proxy.caddy.host routes a whole hostname to the backend
   root via a host matcher inside the :9000 site, so a root-based SPA (base="/")
   serves unchanged — the fix for proxying an app castle can't rebuild. Caddyfile
   now emits 'auto_https off' (HTTP-only gateway on a non-standard port).

Nit: activate skips the editable reinstall when the tool is already on PATH.

Tests: core 94, cli 24, api 52; ruff + app build clean. Verified live: lakehouse
runs under systemd, API at /lakehouse, full UI (SPA boots) via host routing.
2026-06-14 13:16:34 -07:00
Paul Payne
3f3b88f17b refactor: Align vocabulary — component → program / deployment
Canonical terms (see docs/registry.md glossary):
- program: any catalog entry (tool/daemon/frontend) — the software
- service / job: how a program is deployed (systemd .service / .timer)
- deployment: umbrella for the unified service+job+program view

Changes:
- core: ServiceSpec/JobSpec component: → program: (component accepted as
  back-compat validation_alias); DeployedComponent → Deployment.
- api: ComponentSummary/Detail → DeploymentSummary/Detail; GET /components
  → /deployments; GatewayRoute.component → program; GatewayInfo
  .component_count → deployment_count; ServiceActionResponse/action keys
  component → program.
- app: matching type renames, /components → /deployments hook, route
  /component/:name → /deployment/:name, GatewayRoute.program.
- docs: component-registry.md → registry.md (+ canonical glossary);
  CLAUDE.md endpoint list refreshed (drop removed /tools, add typed
  program/service/job + config routes).

Tests: core 92, cli 24, api 52 green; app build clean; ruff clean.
2026-06-14 11:05:22 -07:00
Paul Payne
400e0b253b Less stack-centric and location-centric model. 2026-06-13 17:26:49 -07:00
Paul Payne
74a902ee62 Clean up. 2026-06-13 12:24:41 -07:00
Paul Payne
8be129259c refactor: Restructure ~/.castle/ as the instance directory
Move all instance state into ~/.castle/ with clear separation:
- code/        — user project source (was components/)
- artifacts/   — generated specs, built content (was generated/, static/)
- data/        — service runtime data (was /data/castle/)
- secrets/     — credentials

castle.yaml moves to ~/.castle/castle.yaml as the canonical location.
Source paths use repo: prefix for git repo programs (castle-api, app)
and relative paths for user projects (code/central-context).

Add idempotent install.sh for bootstrapping infrastructure (Docker,
Caddy, MQTT, Postgres, Neo4j) with interactive service detection.

Remove components/ from repo (now in ~/.castle/code/), deinit
submodules, remove .gitmodules.
2026-03-09 22:03:37 -07:00
Paul Payne
efab2a7893 refactor: Update component specifications to use 'program' and 'behavior' attributes, enhancing clarity and consistency across the application 2026-02-23 22:56:18 -08:00
Paul Payne
0d36e4f72a 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.
2026-02-23 22:09:41 -08:00
Paul Payne
56b9c8ddf1 refactor: Update component structure to use behavior and stack attributes, enhancing clarity and functionality across services, tools, and frontends 2026-02-23 16:32:55 -08:00
Paul Payne
3343e955fd feat: Implement multi-node support with MQTT and mDNS for service discovery and coordination 2026-02-23 02:30:12 -08:00
Paul Payne
eeaa5045d0 refactor: Decouple roles. 2026-02-23 01:49:24 -08:00
Paul Payne
5e3e01a5b6 refactor: Update runner specifications from 'python_uv_tool' to 'python' across components and documentation 2026-02-22 23:56:37 -08:00
Paul Payne
033a76ccfd refactor: Update project structure to place tools and frontends under components/ 2026-02-22 22:56:54 -08:00
Paul Payne
a5e9835d55 Removes tools from grouping. 2026-02-22 21:31:21 -08:00
Paul Payne
930bc601b7 feat: add ComponentGrid and ComponentTable components for displaying components in grid and table formats
feat: implement HealthBadge and RoleBadge components for displaying health status and roles

feat: create LogViewer component for streaming logs of services

feat: develop SecretsEditor for managing secrets with CRUD operations

feat: introduce ToolCard component for displaying tool information

style: add global CSS variables and styles for consistent theming

feat: set up API client for handling requests to the backend

feat: implement hooks for fetching components, statuses, and tools

feat: create routes for dashboard, component details, and tools

feat: add service management endpoints for starting, stopping, and restarting services

feat: implement event bus for handling real-time updates via SSE

feat: create health check and logs endpoints for monitoring services

feat: add tests for health and tools endpoints to ensure functionality

chore: update project configuration files and dependencies for better development experience
2026-02-21 01:23:37 -08:00
Paul Payne
54ba2ccc62 feat: Refactor tool management and update documentation for clarity 2026-02-21 00:30:31 -08:00
Paul Payne
08c6f3fa83 feat: Enhance tool management and documentation in Castle
- Introduced category tools support in the `castle create` command.
- Added detailed guides for creating components in CLAUDE.md.
- Implemented new API endpoints for listing and retrieving tool details.
- Updated component and tool models to include additional metadata.
- Improved error handling and response structures in service actions.
- Enhanced documentation for component registry and web APIs.
2026-02-21 00:09:34 -08:00
Paul Payne
f39a551aad feat: Add new tools and configurations for Castle platform
- Introduced `uv.lock` for dependency management with various packages including `pytest`, `colorama`, and `pluggy`.
- Added `pyrightconfig.json` for Python type checking configuration.
- Expanded `recommendations.md` with detailed scaling recommendations and project management strategies.
- Created shared `ruff.toml` for consistent linting across projects.
- Developed `Castle Tools` with various utilities including Android backup, browser automation, document conversion, and search tools.
- Implemented `backup-collect` and `schedule` tools for system administration tasks.
- Enhanced `search` functionality with indexing and querying capabilities using Tantivy.
- Added comprehensive documentation for each tool, including usage examples and installation instructions.
2026-02-20 16:41:19 -08:00