v0.1.0.0033: update AGENT.md/README.md/CHANGELOG with final architecture docs
This commit is contained in:
parent
7303c94a3c
commit
9f6c85ce60
4 changed files with 100 additions and 97 deletions
141
AGENT.md
141
AGENT.md
|
|
@ -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`)
|
||||
- Bump VERSION file on every change before commit.
|
||||
- Every commit must be tagged with the version: `git tag v$(cat VERSION)`
|
||||
Single unified script: **`tools/nextwks.sh`** (replaces old `deploy.sh`/`install.sh`).
|
||||
|
||||
## 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.
|
||||
The old `deploy.sh` and `install.sh` are deprecated.
|
||||
**Run WITHOUT sudo.** The script invokes `sudo` only where needed (apt, writing to `/opt/`, iptables).
|
||||
|
||||
**Key principles:**
|
||||
- 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.
|
||||
## Key Architecture Decisions
|
||||
|
||||
### 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 |
|
||||
|------|-------------|
|
||||
| `--install` | First-time setup on a bare VM: installs deps, prompts for config, builds binary, generates configs, deploys stack |
|
||||
| `--update` | Smart update: clones fresh, rebuilds binary, copies to target, bounces containers |
|
||||
| `--destroy` | Full greenfield redeploy: tears down containers, wipes `/opt/nextworkspace/`, rebuilds from scratch using saved secrets from `/opt/backup/.env` |
|
||||
### 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/`
|
||||
|
||||
### 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.
|
||||
2. Test locally (e.g., `go build && go run .`).
|
||||
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:
|
||||
### 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}` (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
|
||||
TLS_EMAIL=admin@nextwks.eu
|
||||
ADMIN_USERNAME=master
|
||||
ADMIN_PASSWORD=<generated>
|
||||
SMTP_HOST=smtp.openxchange.eu
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=post@nextwks.eu
|
||||
SMTP_PASS=<prompted>
|
||||
JWT_SECRET=<auto-generated>
|
||||
SESSION_SECRET=<auto-generated>
|
||||
STORAGE_ENCRYPTION_KEY=<auto-generated>
|
||||
ADMIN_PASSWORD_HASH=<bcrypt hash>
|
||||
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
|
||||
|
||||
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
|
||||
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`.
|
||||
|
|
|
|||
24
CHANGELOG.md
24
CHANGELOG.md
|
|
@ -1,5 +1,29 @@
|
|||
# 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
|
||||
|
||||
### Added
|
||||
|
|
|
|||
30
README.md
30
README.md
|
|
@ -5,24 +5,22 @@ A self-hosted productivity suite for startups. One binary + Caddy + Authelia.
|
|||
## Architecture
|
||||
|
||||
```
|
||||
app.nextwks.eu :443 auth.nextwks.eu :443
|
||||
│ │
|
||||
Caddy (TLS + forward auth) Caddy → Authelia :9091
|
||||
│ │
|
||||
├── /home/ → launcher page └── authelia-api :8080
|
||||
├── /drive/* → OpenCloud :9100
|
||||
├── /office/* → Euro Office :9200
|
||||
├── /erp/* → ERPNext :9300
|
||||
├── /chat/* → Matrix :9400
|
||||
├── /meet/* → Jitsi :9500
|
||||
├── /mail/* → Alps :9600
|
||||
├── /ai/* → Open WebUI :9700
|
||||
└── /admin/* → Portainer :9800
|
||||
Internet :443 ──iptables──> :8443 ──> Caddy container :443
|
||||
Internet :80 ──iptables──> :8080 ──> Caddy container :80
|
||||
|
||||
Caddy (rootless podman, nextwks-net)
|
||||
├── auth.{DOMAIN} ──> Authelia :9091 (internal)
|
||||
├── app.{DOMAIN} ──> Launcher :9000 (forward auth via Authelia)
|
||||
└── www.{DOMAIN} ──> static files
|
||||
|
||||
Authelia :9091 ──> api :8080 (internal)
|
||||
Launcher :9000 ──> /config, /people, /settings, /health
|
||||
```
|
||||
|
||||
- **Caddy**: Reverse proxy, TLS (auto LE), subdomain routing, forward auth to Authelia
|
||||
- **Authelia**: OIDC provider, 2FA, identity store
|
||||
- **Binary**: Go launcher + path-based reverse proxy to upstream apps
|
||||
- **Caddy**: TLS termination (ZeroSSL/LE), subdomain routing, forward auth to Authelia
|
||||
- **Authelia**: OIDC provider, 2FA, identity store, user management API
|
||||
- **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)
|
||||
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.1.0.0032
|
||||
0.1.0.0033
|
||||
|
|
|
|||
Loading…
Reference in a new issue