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).
139 lines
5.1 KiB
Markdown
139 lines
5.1 KiB
Markdown
# 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 1–4) 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.
|