Development¶
Stack¶
| Part | Technology |
|---|---|
| API | Go 1.26, chi router, pgx (PostgreSQL), yaml.v3, go-oidc, x/crypto (Argon2id), x/net/html |
| UI | SvelteKit (Svelte 5 runes), Tailwind CSS, CodeMirror 6, lucide icons; built as a static SPA |
| Database | PostgreSQL 17 |
| Tests | Go unit and engine tests (fake host/DNS/Authentik), Python API end-to-end checks, browser checks with headless Chromium (browserless) |
Repository layout¶
See Architecture → Code map. Two rules shape the code:
- Everything that touches the server goes through
host.Host, which has a real implementation and a fake. The same is true for DNS (ionos.DNS) and Authentik (authentik.API). - Every operation is a run started with
Engine.start(app, kind, user, fn, after): one at a time, a log (r.Step,r.Info,r.OK,r.Warn), undo functions registered withs.onFail(name, fn).
Fake mode¶
DEPLOYER_FAKE=1 replaces the host, DNS and Authentik with in-memory fakes: you can run the full wizard, deploy,
update and delete without touching any server. Docker Hub lookups stay real (read-only).
Local development¶
The repository ships helper scripts for a development container that has Go, Node/pnpm and a PostgreSQL service; adapt the container names to your setup.
scripts/dev.sh up # API on :8081 (fake mode, database deployer_dev) + Vite on :5175
scripts/dev.sh seed # admin dev@test.local / devpassword123 with TOTP enabled
scripts/dev.sh totp # current TOTP code for the seeded admin
scripts/dev.sh status # processes and last log lines
scripts/dev.sh test # gofmt, go vet, go test (scratch database deployer_test)
scripts/dev.sh down # stop everything; reset = down + drop the dev databases
Without the scripts:
# API (fake mode)
cd api
DEPLOYER_FAKE=1 DEPLOYER_DEV=1 DEPLOYER_LISTEN=:8081 DEPLOYER_PUBLIC_URL=http://localhost:8081 \
DEPLOYER_DATABASE_URL=postgres://dev:dev@localhost:5432/deployer_dev?sslmode=disable \
DEPLOYER_MASTER_KEY_FILE=/tmp/dp/master.key DEPLOYER_SETUP_TOKEN_FILE=/tmp/dp/setup_token \
go run ./cmd/deployer
# UI (proxies /api to :8081)
cd web && pnpm install && pnpm exec vite dev --port 5175
Tests¶
| Suite | Command | What it covers |
|---|---|---|
| Go | scripts/dev.sh test or cd api && go test ./... (needs DEPLOYER_TEST_DATABASE_URL) |
compose and nginx render/parse round trips, lint, catalog builds, every SSO recipe applies and is undone exactly, auto-login form parsing, SSO detection, the engine (deploy, rollback, updates, delete, account changes) against fakes |
| API end-to-end | python3 scripts/api_e2e.py against a fake-mode server |
about 100 checks: auth, CSRF, MFA, roles, catalog, Docker Hub, drafts, lint, nginx validation, deploy, updates, rollback, delete, SSO choices, detection |
| UI | scripts/dev.sh browser && scripts/dev.sh ui scripts/ui_e2e.mjs |
the full wizard in Chromium, phone width, dark mode, screenshots |
| Type check | cd web && pnpm exec svelte-check |
Svelte and JS types |
scripts/build.sh runs the type check, gofmt, go vet and the Go tests before producing the binary.
The real-login lab¶
Recipes are verified with real logins against a staging Deployer, never the production one:
- Create a database
deployer_staging; run the built binary withDEPLOYER_LISTEN=127.0.0.1:8151,DEPLOYER_DEV=1, another port range (8950-8999) and subnet range (230-250), and the same master key; copy theauthentiksettings row from the production database. ssolab create dp-lab-user dp-lab-group(refuses non-staging databases) creates a throwaway Authentik user and group.- Start a headless Chromium:
docker run -d -p 127.0.0.1:3300:3000 ghcr.io/browserless/chromium. LAB_DIR=… scripts/sso_lab.py run <template|image> [recipe|oidc|saml|header|none|basic|autologin|own]deploysssolab.<domain>, signs in through Authentik with the lab user, prints who the app thinks you are (probes such as/api/user), saves a screenshot and deletes the app (DNS record and certificate are kept between runs).LAB_DRAFT='{"sharedUser": …}'passes inputs.- Clean up:
sso_lab.py clean(also DNS and certificate),ssolab purgeandssolab delete, stop the staging service and the browser, drop the database.
Keep the number of logins small and watch Authentik's database connections while testing.
Refreshing the Authentik guides index¶
python3 scripts/kb_authentik.py # rebuilds api/internal/ssodetect/authentik_guides.json from the Authentik repository
Conventions¶
- Go:
gofmt,go vetclean; errors are messages a person can act on; API responses never containnulllists ([]instead). - UI: Svelte 5 runes, components in
src/lib/components, API calls through#lib/api.js(adds the CSRF header). - Every new server operation: add it to
host.Host, implement it inReal(validated arguments, no shell) and inFake, and test it through the engine. - Every new catalog app: a template in
templates.json; if it supports single sign-on, a recipe tested in the lab.