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.
This commit is contained in:
2026-03-09 22:03:37 -07:00
parent b7628c590b
commit 8be129259c
86 changed files with 1222 additions and 13989 deletions

View File

@@ -274,11 +274,11 @@ async def apply_config() -> ApplyResponse:
errors.append(f"Failed to restart {name}: {output}")
# Reload gateway
from castle_core.config import GENERATED_DIR, ensure_dirs
from castle_core.config import SPECS_DIR, ensure_dirs
from castle_core.generators.caddyfile import generate_caddyfile_from_registry
ensure_dirs()
caddyfile_path = GENERATED_DIR / "Caddyfile"
caddyfile_path = SPECS_DIR / "Caddyfile"
caddyfile_path.write_text(generate_caddyfile_from_registry(registry))
actions.append("Generated Caddyfile")

View File

@@ -8,7 +8,7 @@ from pathlib import Path
from fastapi import APIRouter, HTTPException, status
from castle_core.config import GENERATED_DIR
from castle_core.config import SPECS_DIR
from castle_core.generators.caddyfile import generate_caddyfile_from_registry
from castle_core.manifest import ProgramSpec, JobSpec, ServiceSpec
from castle_core.stacks import available_actions
@@ -834,8 +834,8 @@ def get_caddyfile() -> dict[str, str]:
async def reload_gateway() -> dict[str, str]:
"""Regenerate Caddyfile and reload Caddy."""
registry = get_registry()
GENERATED_DIR.mkdir(parents=True, exist_ok=True)
caddyfile_path = GENERATED_DIR / "Caddyfile"
SPECS_DIR.mkdir(parents=True, exist_ok=True)
caddyfile_path = SPECS_DIR / "Caddyfile"
# Include remote registries for cross-node routing
remote_regs = {h: n.registry for h, n in mesh_state.all_nodes().items()}

View File

@@ -86,7 +86,7 @@ def registry_path(tmp_path: Path, castle_root: Path) -> Generator[Path, None, No
run_cmd=["uv", "run", "test-svc"],
env={
"TEST_SVC_PORT": "19000",
"TEST_SVC_DATA_DIR": "/data/castle/test-svc",
"TEST_SVC_DATA_DIR": "/home/user/.castle/data/test-svc",
},
description="Test service",
behavior="daemon",