diff --git a/.gitignore b/.gitignore index 462bd13..05ef2ea 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,9 @@ Thumbs.db *.swp *.swo +# AI / Agent config (stored at project root ~/development/) +AGENT.md + # Environment .env .env.local diff --git a/AGENT.md b/AGENT.md deleted file mode 100644 index 189a0a7..0000000 --- a/AGENT.md +++ /dev/null @@ -1,82 +0,0 @@ -# NextWorkspace — Architecture & Workflow - -## Deployment Model - -Single unified script: **`tools/nextwks.sh`** (replaces old `deploy.sh`/`install.sh`). - -| Flag | When to use | -|------|------------| -| `--install` | First-time setup on a bare VM (prompts for config) | -| `--update` | Rebuild & restart with latest code (uses saved secrets) | -| `--destroy` | Full greenfield redeploy (tears down everything, rebuilds from `/opt/backup/.env`) | - -**Run WITHOUT sudo.** The script invokes `sudo` only where needed (apt, writing to `/opt/`, iptables). - -## Key Architecture Decisions - -### 1. Rootless Podman (no sudo for containers) -All `podman` / `podman-compose` commands run as the normal user. Containers are rootless. -- Caddy binds to host ports **8080** and **8443** (not 80/443 — unprivileged) -- iptables `PREROUTING` + `OUTPUT -o lo` redirect 80→8080, 443→8443 -- Firewall rules applied by `tools/firewall-routing.sh`, persisted via `netfilter-persistent` - -### 2. Ephemeral Build Directory -- Fresh `git clone --depth 1` into `/tmp/nextwks-build/` every time -- No persistent `/opt/NextWks` repo (eliminates git permission issues) -- Binary + configs built as user, then `sudo cp` to `/opt/nextworkspace/` - -### 3. File Ownership -All files in `/opt/nextworkspace/` and `/opt/backup/` are `chown -R` to the non-root user after every deploy. - -### 4. Config Templates with Placeholder Substitution -Config files live in git with `{PLACEHOLDER}` syntax. The script substitutes values at deploy time: - -| File | Placeholders | -|------|-------------| -| `config/caddy/Caddyfile` | `{DOMAIN}`, `{TLS_EMAIL}` | -| `config/authelia/configuration.yml` | `{DOMAIN}`, `{JWT_SECRET}`, `{SESSION_SECRET}`, `{STORAGE_ENCRYPTION_KEY}`, `{SMTP_HOST}`, `{SMTP_PORT}`, `{SMTP_USER}`, `{SMTP_PASS}` | -| `config/authelia/users_database.yml` | `{ADMIN_PASSWORD_HASH}`, `{TLS_EMAIL}` | -| `compose/stack.yaml` | `{AUTHELIA_SECRET}` (= `SESSION_SECRET`) | - -Configs are regenerated on **every** run (install, update, destroy) from templates + saved secrets. - -### 5. Secrets Vault (`/opt/backup/.env`) -All values are single-quoted to protect `$` signs (bcrypt hashes). Survives `--destroy`. Sourced with `set +u` to avoid errors from `$` in values. - -### 6. Container Lifecycle -Containers are stopped **before** copying the new binary (avoids "Text file busy"). Restarted after deploy. - ---- - -## Network Architecture - -``` -Internet :443 → [iptables REDIRECT] → host :8443 → [Caddy container :443] -Internet :80 → [iptables REDIRECT] → host :8080 → [Caddy container :80] - -Caddy (rootless, nextwks-net) - ├── auth.{DOMAIN} → Authelia :9091 (internal, no host port) - ├── app.{DOMAIN} → Launcher :9000 (internal, forward auth via Authelia) - └── www.{DOMAIN} → static files -``` - -All three containers on `nextwks-net` (rootless podman bridge). -Only Caddy has host ports (8080, 8443). Authelia and Launcher are internal-only. - -## Firewall (`tools/firewall-routing.sh`) -- NAT redirects: 80→8080, 443→8443 -- INPUT rules: allow lo, established, SSH (22), Caddy ports (8080, 8443), app ports (8000) -- Global DROP at end for all other unsolicited inbound -- Persisted: `netfilter-persistent save` (runs on every deploy mode) - -## Versioning -- Format: `MILESTONE.FEATURE.PATCH.BUILD` (e.g., `0.1.0.0032`) -- Bump VERSION, update CHANGELOG.md, `git tag v$(cat VERSION)` on every change - -## Workflow -1. Edit code in dev clone -2. `git commit -m "msg" && git tag v$(cat VERSION) && git push origin main --tags` -3. On server: `./nextwks.sh --update` - -## Health Check -The script polls `podman exec launcher curl -sf http://127.0.0.1:9000/health` up to 15×3s. Launcher container uses `alpine:latest` with `curl` installed at startup via `apk add --no-cache curl`.