Files
Stalwart-webui/DEVELOPMENT.md
T
Steven RYDELL adca6d8730 feat(dev): switch dev tokens to a real admin account with configurable duration
Requested: dev-token.sh/.ps1 tokens should last 3h by default, with an
argument to override the duration.

Server-verified finding: STALWART_RECOVERY_ADMIN (the break-glass
account docker-compose.yml and the scripts used) always issues OAuth
tokens with a fixed 1h expiry, regardless of the server's configured
accessTokenExpiry — confirmed against the live container, including
after changing the setting and restarting. Confirmed x:ApiKey objects,
by contrast, support an arbitrary expiresAt set per request, and their
secret works directly as a bearer token.

Add scripts/dev-server-init.sh (+ .ps1): a one-time, idempotent setup
step that completes the server's bootstrap wizard (default domain, no
TLS certificate request), creates a real "devadmin" admin account, and
sets the server's default OAuth token lifetime to 3h.

Rework dev-token.sh/.ps1 to authenticate as devadmin and create an
x:ApiKey with a caller-supplied expiry (`dev-token.sh 1800` for 30
minutes, defaults to 10800s/3h) instead of running the OAuth PKCE flow
against the recovery account. Verified end-to-end against a fresh
container, including a real browser session against the running WebUI.

Also includes an incidental package-lock.json sync (was still pinned to
v1.0.8 / stale dependency ranges from before the upstream merge).
2026-08-01 18:32:13 +02:00

139 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Development
How to run a local Stalwart test server and develop this WebUI against it.
This file is meant to be read by humans and AI coding agents alike — see
[AGENTS.md](AGENTS.md) for the rules that also apply while doing this.
## Prerequisites
- Node.js 18+
- Docker (with the `docker compose` CLI plugin)
## 1. Start a local test server
```bash
npm run dev:server
```
This runs `docker compose up -d`, which starts a disposable Stalwart
instance ([`docker-compose.yml`](docker-compose.yml)) with:
- The management/JMAP HTTP API on `http://localhost:8080` (the only port
the WebUI dev proxy needs — see `server.proxy` in
[`vite.config.ts`](vite.config.ts)).
- Mail protocol ports (SMTP/IMAP/POP3/ManageSieve) exposed too, only
needed if you're testing actual mail flows, not just admin UI screens.
- A fixed admin account baked in via `STALWART_RECOVERY_ADMIN`:
`admin@example.org` / `c8321iEscHDy0GWV`. **Disposable dev credentials
only — never reuse them for anything real.**
- Named Docker volumes (`stalwart-etc`, `stalwart-data`) so data survives
a restart. Data persists until you tear the volumes down.
Useful companions:
```bash
npm run dev:server:logs # tail the container's logs
npm run dev:server:down # stop it (add `-v` via `docker compose down -v` to also wipe data)
```
The server takes a couple of seconds to come up; `docker compose logs stalwart`
will show `Network listener started ... localPort = 8080` once it's ready.
## 2. Initialize the server (first time only)
The container starts in Stalwart's bootstrap mode, which only allows
signing in as the break-glass `STALWART_RECOVERY_ADMIN` account — real
accounts and most settings aren't usable yet. Run once per fresh volume:
```bash
# Windows / PowerShell
pwsh ./scripts/dev-server-init.ps1
# Linux / macOS / any POSIX shell (including most AI agent sandboxes)
bash ./scripts/dev-server-init.sh
```
This completes the bootstrap wizard (default domain `example.org`, no TLS
certificate request — safe for local/offline use), creates a real
`devadmin@example.org` admin account, and sets the server's default OAuth
access token lifetime to 3 hours. It's idempotent — safe to re-run, it
no-ops once the server is already bootstrapped. You only need to re-run it
after `docker compose down -v` (which wipes the volumes).
## 3. Get an access token
For local development it's simpler to skip interactive login and use a
bearer token directly via `VITE_ACCESS_TOKEN` (see `.env.development`).
```bash
# Windows / PowerShell
pwsh ./scripts/dev-token.ps1 # 3 hour token (server default)
pwsh ./scripts/dev-token.ps1 -DurationSeconds 1800 # custom duration (30 min)
# Linux / macOS / any POSIX shell (including most AI agent sandboxes)
bash ./scripts/dev-token.sh # 3 hour token
bash ./scripts/dev-token.sh 1800 # custom duration (30 min)
```
Both scripts authenticate as the `devadmin` account created in step 2 and
create a Stalwart API key with the requested expiry (default 3 hours,
overridable per invocation — this is a genuine per-request duration, not
a global setting), then write its secret to `.env.development.local`
(gitignored, never committed) as `VITE_ACCESS_TOKEN`. Re-run the script
and restart `npm run dev` once the token expires (the UI starts returning
401s).
The `STALWART_RECOVERY_ADMIN` account is intentionally not used here: it's
a break-glass credential and its tokens always expire in a fixed 1 hour
regardless of server configuration, so it can't honor a custom duration.
## 4. Run the WebUI
```bash
npm install # first time only
npm run dev
```
Open `http://localhost:5173`. You should land directly in the admin panel
(no login screen) since `VITE_ACCESS_TOKEN` is set.
## 5. Verify your change
```bash
npm run typecheck
npm run lint
npm test
npm run build
```
For UI changes, actually look at the running app (browser or a browser
automation tool) — passing typecheck/lint/tests proves the code compiles
and existing behavior didn't regress, it doesn't prove the new UI works.
## Resetting the test server
To start from a completely clean server (e.g. to re-test first-run
behavior):
```bash
npm run dev:server:down
docker compose down -v # also removes the stalwart-etc/stalwart-data volumes
npm run dev:server
bash ./scripts/dev-server-init.sh # re-run: fresh volume needs bootstrapping again
```
## Notes for AI agents
- This whole workflow (steps 14) is scriptable end-to-end without a
browser: `npm run dev:server`, then `bash scripts/dev-server-init.sh`
(first time only), then `bash scripts/dev-token.sh`, then the app is
reachable at `http://localhost:5173` with `VITE_ACCESS_TOKEN` already
set. Verify backend connectivity directly with `curl`, e.g.
`curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/jmap/session`.
- Read [AGENTS.md](AGENTS.md) before touching anything under `src/`
the schema-fidelity rule applies to all development, local test server
or not.
- Don't commit `.env.development.local` (it holds a live token) or leave
the dev container running unexpectedly — `npm run dev:server:down` when
you're done.