11---
22title : Dashboard
3- description : " Serve the bundled web dashboard and its REST API from a Java process ."
3+ description : " Serve the bundled web dashboard and its REST API — session auth, OAuth/SSO, and legacy token mode ."
44---
55
66The jar bundles the web dashboard SPA; ` DashboardServer ` serves it plus a JSON
77REST API over the queue — no separate service, no asset build step.
88
9+ ## Starting the dashboard
10+
11+ <Tabs items = { [" Programmatic" , " Taskito.dashboard()" , " CLI" ]} >
12+ <Tab value = " Programmatic" >
13+
914``` java
1015try (Taskito taskito = Taskito . builder(). sqlite(" taskito.db" ). open();
1116 DashboardServer server = DashboardServer . start(taskito, 8080 )) {
@@ -14,60 +19,178 @@ try (Taskito taskito = Taskito.builder().sqlite("taskito.db").open();
1419}
1520```
1621
17- Or from the [ CLI] ( /java/guides/operations/cli ) :
22+ ` DashboardServer.start(queue, port) ` opens in [ session-auth
23+ mode] ( #session-auth-default ) . ` start(queue, port, token) ` switches to
24+ [ legacy shared-token mode] ( #legacy-shared-token-mode ) ; four- and five-argument
25+ overloads add an unpacked SPA directory and a ` secureCookies ` flag.
26+
27+ </Tab >
28+ <Tab value = " Taskito.dashboard()" >
29+
30+ ``` java
31+ try (Taskito taskito = Taskito . builder(). sqlite(" taskito.db" ). open();
32+ DashboardServer server = taskito. dashboard(8080 )) {
33+ // ...
34+ }
35+
36+ // Legacy shared-token mode, gating /api/* as a fixed admin identity:
37+ taskito. dashboard(8080 , System . getenv(" DASH_TOKEN" ));
38+ ```
39+
40+ ` Taskito.dashboard(port) ` / ` dashboard(port, token) ` are convenience defaults
41+ over ` DashboardServer.start(...) ` for the common case — one fewer import.
42+
43+ </Tab >
44+ <Tab value = " CLI" >
1845
1946``` bash
2047taskito --url taskito.db dashboard --port 8080
2148```
2249
50+ | Flag | Default | Description |
51+ | ---| ---| ---|
52+ | ` --port ` | ` 8080 ` | Bind port (` 0 ` for ephemeral) |
53+ | ` --token ` | none | Legacy shared token gating ` /api/* ` — disables session auth and OAuth |
54+ | ` --static ` | bundled SPA | Directory of a prebuilt SPA, overriding the jar's extracted copy |
55+ | ` --insecure-cookies ` | off | Drop the ` Secure ` cookie attribute — for local HTTP development |
56+
57+ </Tab >
58+ </Tabs >
59+
2360Pass ` 0 ` as the port for an ephemeral one (` server.port() ` reports what was
2461bound). The SPA is extracted from the jar to a per-user, content-addressed
2562directory on first use; ` -Dtaskito.dashboard.dir=/path ` (or the ` staticDir `
26- argument) overrides it with an unpacked build. Without bundled assets only
27- ` /api/* ` responds.
63+ argument / ` --static ` ) overrides it with an unpacked build. Without bundled
64+ assets only ` /api/* ` responds.
2865
29- ## REST API
66+ <Callout type = " info" >
67+ Running Spring Boot? ` taskito-spring ` can auto-start a ` DashboardServer ` bean
68+ from ` taskito.dashboard.* ` properties — see
69+ [ Spring Boot: Dashboard auto-configuration] ( /java/guides/integrations/spring#dashboard-auto-configuration ) .
70+ </Callout >
71+
72+ ## Auth
73+
74+ Two modes, chosen by whether a ` token ` is passed to ` start(...) ` /
75+ ` dashboard(...) ` .
3076
31- Everything is JSON, fields in snake_case, timestamps in Unix milliseconds.
77+ ### Session auth (default)
3278
33- | Method · Path | Effect |
79+ With no token, the dashboard runs password sign-in (and optionally
80+ [ OAuth/OIDC] ( /java/guides/operations/sso ) ) with server-side sessions. Users
81+ and sessions live in the queue's settings key/value store — no dedicated
82+ tables — so the model is identical across SQLite, PostgreSQL, and Redis.
83+
84+ - ** First-run setup.** On a fresh database every route except the public set
85+ (` /api/auth/status ` , ` /api/auth/login ` , ` /api/auth/setup ` ,
86+ ` /api/auth/providers ` , ` /health ` , ` /readiness ` , ` /metrics ` ) returns
87+ ` 503 setup_required ` until an admin exists. ` POST /api/auth/setup ` creates
88+ it (and signs it in); the route locks itself after the first user.
89+ - ** Env-admin bootstrap.** Set both ` TASKITO_DASHBOARD_ADMIN_USER ` and
90+ ` TASKITO_DASHBOARD_ADMIN_PASSWORD ` before starting the process to seed the
91+ first admin without visiting a browser — useful for containers. It's
92+ idempotent: once a user with that name exists, later restarts skip
93+ creation.
94+
95+ ``` bash
96+ export TASKITO_DASHBOARD_ADMIN_USER=admin
97+ export TASKITO_DASHBOARD_ADMIN_PASSWORD=' change-me-on-first-login'
98+ taskito --url taskito.db dashboard --port 8080
99+ ```
100+
101+ <Callout type = " warning" >
102+ Unlike a scripting-language runtime, the JVM cannot scrub a variable out
103+ of its own process environment once it has been read — the password
104+ stays visible to anything that can inspect the process (` /proc ` , a
105+ debugger, a core dump) for the process's lifetime. Prefer first-run setup
106+ through the SPA where that matters; treat the env var as a one-time
107+ recovery path and rotate the password after logging in.
108+ </Callout >
109+
110+ - ** Passwords** are hashed with PBKDF2-HMAC-SHA256 — 600,000 iterations, a
111+ 16-byte random salt — no third-party crypto dependency.
112+ - ** Sessions** are opaque tokens with a 24-hour TTL, carried in an ` HttpOnly ` ,
113+ ` SameSite=Strict ` ` taskito_session ` cookie (plus ` Secure ` unless disabled —
114+ see below).
115+ - ** CSRF** uses the double-submit pattern: a non-HttpOnly ` taskito_csrf `
116+ cookie must match both the token bound to the session and the
117+ ` X-CSRF-Token ` header on every state-changing request
118+ (` POST ` /` PUT ` /` DELETE ` /` PATCH ` ). ` /api/auth/login ` and ` /api/auth/setup ` are
119+ exempt — there is no session yet to bind to.
120+ - ** ` --insecure-cookies ` ** (or ` secureCookies=false ` on ` DashboardServer.start ` ,
121+ or ` taskito.dashboard.secure-cookies=false ` in Spring) drops the ` Secure `
122+ cookie attribute for local HTTP development. Keep it on — the default — for
123+ anything served over HTTPS.
124+
125+ ### Roles
126+
127+ RBAC is enforced server-side and is deliberately simple: every state-changing
128+ route is admin-only except two self-service routes; all reads are open to any
129+ authenticated user.
130+
131+ | Role | Access |
34132| ---| ---|
35- | ` GET /api/stats ` | Counts by status across all queues. |
36- | ` GET /api/stats/queues ` | Counts per queue. |
37- | ` GET /api/queues/paused ` | Names of paused queues. |
38- | ` GET /api/jobs ` | Job list — ` ?status=&queue=&task=&limit=&offset= ` . |
39- | ` GET /api/jobs/{id} ` | A single job. |
40- | ` GET /api/dead-letters ` | Dead-letter entries — ` ?limit=&offset= ` . |
41- | ` GET /api/metrics ` | Per-execution metrics — ` ?task=&since= ` (ms window). |
42- | ` GET /api/workers ` | Registered workers + heartbeats. |
43- | ` GET /api/auth/status ` | ` { "auth_required": true\|false } ` — never needs a token. |
44- | ` POST /api/jobs/{id}/cancel ` | Cancel a job. |
45- | ` POST /api/dead-letters/{id}/retry ` | Re-enqueue a dead-letter entry. |
46- | ` POST /api/queues/{name}/pause ` · ` /resume ` | Pause / resume a queue. |
133+ | ` admin ` | Full access — cancel/replay jobs, purge dead letters, pause/resume queues, manage webhooks, edit settings, edit task/queue overrides. |
134+ | ` viewer ` | Read-only, plus their own ` POST /api/auth/logout ` and ` POST /api/auth/change-password ` . Any other mutating route returns ` 403 forbidden ` . |
47135
48- ## Auth
136+ The first user — created via setup or env bootstrap — is always ` admin ` .
137+
138+ ### Legacy shared-token mode
49139
50- Auth runs ** open ** by default. Pass a token to require it on every ` /api/* `
51- request (except ` /api/ auth/status ` ):
140+ Pass a ` token ` to gate ` /api/* ` behind a single fixed credential — no users,
141+ no sessions, no RBAC. Kept for back-compat with the pre- auth dashboard.
52142
53143``` java
54144DashboardServer . start(taskito, 8080 , System . getenv(" DASH_TOKEN" ));
55145```
56146
57147Requests authenticate with ` ?token=<token> ` ; opening ` /?token=<token> ` once
58148sets an httpOnly ` taskito_token ` cookie so the SPA works for the rest of the
59- session. This is a single shared token — no per-user login, RBAC, or SSO. For
60- those, put the server behind a reverse proxy that handles auth.
149+ session. OAuth has no login UI in this mode, so it's disabled automatically —
150+ ` start(queue, port, token, ...) ` never builds an OAuth flow when ` token ` is
151+ non-null.
61152
62153<Callout type = " warning" >
63154 ` ?token= ` puts the secret in the URL, where it can leak via browser history,
64155 ` Referer ` headers, and proxy or access logs. Use it only over HTTPS, redact
65- query strings from logs, and rely on the cookie afterwards — once the first
66- request sets it, the token never needs to appear in a URL again.
156+ query strings from logs, and rely on the cookie afterwards.
67157</Callout >
68158
159+ ## Metrics and health probes
160+
161+ Three routes sit outside ` /api/* ` and outside the session/token auth gate:
162+
163+ | Route | Access | What it does |
164+ | ---| ---| ---|
165+ | ` GET /health ` | Always public | Liveness — always ` {"status": "ok"} ` |
166+ | ` GET /readiness ` | Public unless ` TASKITO_DASHBOARD_METRICS_TOKEN ` is set | Storage/worker/resource readiness |
167+ | ` GET /metrics ` | Public unless ` TASKITO_DASHBOARD_METRICS_TOKEN ` is set | Prometheus text exposition |
168+
169+ Set ` TASKITO_DASHBOARD_METRICS_TOKEN ` to require an
170+ ` Authorization: Bearer <token> ` header (checked in constant time) on
171+ ` /readiness ` and ` /metrics ` . ` /health ` always stays open for liveness probes.
172+
173+ ## REST API
174+
175+ Everything is JSON, fields in snake_case, timestamps in Unix milliseconds —
176+ the same contract the bundled SPA consumes. All paths below are relative to
177+ ` /api/ ` .
178+
179+ | Group | Routes |
180+ | ---| ---|
181+ | Auth | ` auth/status ` , ` /setup ` , ` /login ` , ` /logout ` , ` /whoami ` , ` /change-password ` , ` /providers ` , ` /oauth/start/{slot} ` , ` /oauth/callback/{slot} ` — see [ SSO] ( /java/guides/operations/sso ) |
182+ | Stats & jobs | ` stats ` , ` stats/queues ` , ` queues/paused ` , ` jobs ` (+ ` /{id} ` , ` /{id}/logs ` , ` /{id}/replay-history ` , ` /{id}/dag ` , ` /{id}/cancel ` , ` /{id}/replay ` ) |
183+ | Dead letters | ` dead-letters ` (+ ` /{id}/retry ` ) |
184+ | Metrics & logs | ` metrics ` , ` metrics/timeseries ` , ` logs ` |
185+ | Infrastructure | ` workers ` , ` circuit-breakers ` , ` resources ` , ` scaler ` , ` event-types ` |
186+ | Queue control | ` queues/{name}/pause ` , ` queues/{name}/resume ` |
187+ | Task/queue overrides | ` tasks ` , ` tasks/{name}/override ` , ` queues ` , ` queues/{name}/override ` — runtime rate limit, concurrency, retries, timeout, priority, and pause, without redeploying |
188+ | Webhooks | ` webhooks ` (+ ` /{id} ` , ` /{id}/test ` , ` /{id}/rotate-secret ` , ` /{id}/deliveries ` , ` /{id}/deliveries/{deliveryId} ` , ` /{id}/deliveries/{deliveryId}/replay ` ) — see [ Webhooks: Dashboard management] ( /java/guides/extensibility/webhooks#dashboard-management ) |
189+ | Workflows | ` workflows/runs ` (+ ` /{id} ` , ` /{id}/dag ` , ` /{id}/children ` ) |
190+ | Settings | ` settings ` , ` settings/{key} ` |
191+
69192<Callout type = " warning" >
70- Open mode means anyone who can reach the port has full control. Bind it to
71- localhost, front it with your own auth, or at minimum set a token — see
72- [ Security ] ( /java/guides/operations/security ) .
193+ Every route above is auth-gated: session mode requires a valid session (plus
194+ CSRF on writes) except the public auth routes; legacy mode requires the
195+ matching token. See [ Auth ] ( #auth ) .
73196</Callout >
0 commit comments