v0.1.0.0033: update AGENT.md/README.md/CHANGELOG with final architecture docs

This commit is contained in:
Claus Lohmar 2026-07-11 09:20:29 +01:00
parent 7303c94a3c
commit 9f6c85ce60
4 changed files with 100 additions and 97 deletions

141
AGENT.md
View file

@ -1,101 +1,82 @@
# NextWorkspace — Agent Workflow Instructions # NextWorkspace — Architecture & Workflow
## Versioning ## Deployment Model
- Format: `MILESTONE.FEATURE.PATCH.BUILD` (e.g., `0.1.0.0032`) Single unified script: **`tools/nextwks.sh`** (replaces old `deploy.sh`/`install.sh`).
- Bump VERSION file on every change before commit.
- Every commit must be tagged with the version: `git tag v$(cat VERSION)`
## Deployment Model (Unified Script) | 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`) |
The project uses a single unified script at `tools/nextwks.sh` for all operations. **Run WITHOUT sudo.** The script invokes `sudo` only where needed (apt, writing to `/opt/`, iptables).
The old `deploy.sh` and `install.sh` are deprecated.
**Key principles:** ## Key Architecture Decisions
- Source of truth is **git remote only**. No local `/opt/NextWks` repo.
- Building happens in `/tmp/nextwks-build/` via `git clone --depth 1` (fresh every time).
- Runtime goes to `/opt/nextworkspace/` (configs, compose, binary, lng).
- Secrets persist in `/opt/backup/.env` and survive `--destroy`.
- Stack runs on a shared `nextwks-net` podman bridge network.
### Script Flags ### 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`
| Flag | What it does | ### 2. Ephemeral Build Directory
|------|-------------| - Fresh `git clone --depth 1` into `/tmp/nextwks-build/` every time
| `--install` | First-time setup on a bare VM: installs deps, prompts for config, builds binary, generates configs, deploys stack | - No persistent `/opt/NextWks` repo (eliminates git permission issues)
| `--update` | Smart update: clones fresh, rebuilds binary, copies to target, bounces containers | - Binary + configs built as user, then `sudo cp` to `/opt/nextworkspace/`
| `--destroy` | Full greenfield redeploy: tears down containers, wipes `/opt/nextworkspace/`, rebuilds from scratch using saved secrets from `/opt/backup/.env` |
### Workflow for Making Changes ### 3. File Ownership
All files in `/opt/nextworkspace/` and `/opt/backup/` are `chown -R` to the non-root user after every deploy.
1. Edit code in the development clone. ### 4. Config Templates with Placeholder Substitution
2. Test locally (e.g., `go build && go run .`). Config files live in git with `{PLACEHOLDER}` syntax. The script substitutes values at deploy time:
3. Bump `VERSION` (increment BUILD).
4. Update `CHANGELOG.md`.
5. Commit: `git add -A && git commit -m "description"`
6. Tag: `git tag v$(cat VERSION)`
7. Push: `git push origin main --tags`
8. Deploy: `./nextwks.sh --update` (no sudo — script handles it per-command)
> **Note:** The deploy script is downloaded by users via `curl` from the repo. For production deployment, users run:
> ```bash
> curl -sL https://git.lohmar.co.uk/lexton-it/NextWks/raw/branch/main/tools/nextwks.sh | sudo bash -s -- --install
> ```
## Build Process
- Static Go binary: `CGO_ENABLED=0 go build -o nextworkspace .`
- Must run on Alpine in the container (no glibc dependency).
- Binary runs as PID 1 in the `launcher` container.
## Architecture
```
Caddy (:80/:443, host ports)
├── auth.{DOMAIN} → Authelia :9091 (internal)
├── app.{DOMAIN} → Launcher :9000 (internal) with forward auth
└── www.{DOMAIN} → static files
All three containers on nextwks-net (podman bridge).
Only Caddy exposes ports to host.
```
## Config Template System
Config files use `{PLACEHOLDER}` syntax. The script substitutes values at deploy time:
| File | Placeholders | | File | Placeholders |
|------|-------------| |------|-------------|
| `config/caddy/Caddyfile` | `{DOMAIN}`, `{TLS_EMAIL}` | | `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/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}` | | `config/authelia/users_database.yml` | `{ADMIN_PASSWORD_HASH}`, `{TLS_EMAIL}` |
| `compose/stack.yaml` | `{AUTHELIA_SECRET}` (same as `SESSION_SECRET`) | | `compose/stack.yaml` | `{AUTHELIA_SECRET}` (= `SESSION_SECRET`) |
## Env Vault (`/opt/backup/.env`) 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
Persisted secrets across destroys:
``` ```
DOMAIN=nextwks.eu Internet :443 → [iptables REDIRECT] → host :8443 → [Caddy container :443]
TLS_EMAIL=admin@nextwks.eu Internet :80 → [iptables REDIRECT] → host :8080 → [Caddy container :80]
ADMIN_USERNAME=master
ADMIN_PASSWORD=<generated> Caddy (rootless, nextwks-net)
SMTP_HOST=smtp.openxchange.eu ├── auth.{DOMAIN} → Authelia :9091 (internal, no host port)
SMTP_PORT=587 ├── app.{DOMAIN} → Launcher :9000 (internal, forward auth via Authelia)
SMTP_USER=post@nextwks.eu └── www.{DOMAIN} → static files
SMTP_PASS=<prompted>
JWT_SECRET=<auto-generated>
SESSION_SECRET=<auto-generated>
STORAGE_ENCRYPTION_KEY=<auto-generated>
ADMIN_PASSWORD_HASH=<bcrypt hash>
``` ```
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 ## 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`.
After deploy, the script polls `podman exec launcher wget -qO- http://127.0.0.1:9000/health`
up to 10 times (2s interval). Expected response: `OK`.
## Commit Message Style
- Imperative mood ("Add", "Fix", "Update", "Bump")
- Reference the component if relevant ("launcher: add health endpoint")
- Keep under 72 chars for the first line

View file

@ -1,5 +1,29 @@
# Changelog # Changelog
## 0.1.0.0033 — 2026-07-11
### Added
- `tools/firewall-routing.sh` — iptables redirects + VM firewall
- `storage.encryption_key` to Authelia config (required by v4.38+)
- Auto-detection of existing install in `--install` mode
### Changed
- **Rootless Podman**: all container commands run without sudo
- **Ports**: Caddy binds to 8080/8443, iptables redirects 80/443
- `.gitignore`: `/nextworkspace` (root-scoped) to track `config/nextworkspace/`
- Configs regenerated on every mode (install/update/destroy)
- `.env` values single-quoted, written via `tee -a` to preserve `$` in bcrypt hashes
- Admin password: now 24 mixed-case alphanumeric chars (base64)
- Containers stopped before binary copy to avoid "Text file busy"
- Firewall rules persisted via `netfilter-persistent save`
- Docs: AGENT.md, README.md fully updated
### Fixed
- `SSL_ERROR_INTERNAL_ERROR_ALERT` — Authelia now starts with proper config
- Password hash corruption — `$2a$...` no longer mangled by `bash -c`
- "Text file busy" during `--update` — containers stopped before copy
- `--update` skipped config regeneration (now always regenerates)
## 0.1.0.0032 — 2026-07-11 ## 0.1.0.0032 — 2026-07-11
### Added ### Added

View file

@ -5,24 +5,22 @@ A self-hosted productivity suite for startups. One binary + Caddy + Authelia.
## Architecture ## Architecture
``` ```
app.nextwks.eu :443 auth.nextwks.eu :443 Internet :443 ──iptables──> :8443 ──> Caddy container :443
│ │ Internet :80 ──iptables──> :8080 ──> Caddy container :80
Caddy (TLS + forward auth) Caddy → Authelia :9091
│ │ Caddy (rootless podman, nextwks-net)
├── /home/ → launcher page └── authelia-api :8080 ├── auth.{DOMAIN} ──> Authelia :9091 (internal)
├── /drive/* → OpenCloud :9100 ├── app.{DOMAIN} ──> Launcher :9000 (forward auth via Authelia)
├── /office/* → Euro Office :9200 └── www.{DOMAIN} ──> static files
├── /erp/* → ERPNext :9300
├── /chat/* → Matrix :9400 Authelia :9091 ──> api :8080 (internal)
├── /meet/* → Jitsi :9500 Launcher :9000 ──> /config, /people, /settings, /health
├── /mail/* → Alps :9600
├── /ai/* → Open WebUI :9700
└── /admin/* → Portainer :9800
``` ```
- **Caddy**: Reverse proxy, TLS (auto LE), subdomain routing, forward auth to Authelia - **Caddy**: TLS termination (ZeroSSL/LE), subdomain routing, forward auth to Authelia
- **Authelia**: OIDC provider, 2FA, identity store - **Authelia**: OIDC provider, 2FA, identity store, user management API
- **Binary**: Go launcher + path-based reverse proxy to upstream apps - **Launcher**: Go binary — app dashboard, people directory, admin panel, settings
- **iptables**: Redirects 80→8080 and 443→8443 so Caddy can run rootless
## Quick Start (Bare VM) ## Quick Start (Bare VM)

View file

@ -1 +1 @@
0.1.0.0032 0.1.0.0033