feat(dashboard): bearer-token auth with browser persistence + MIT license

Pre-release polish. Two related changes bundled because they both land the
project on public-release footing:

Dashboard auth
- fn dashboard now gates the HTTP API + terminal/badge WebSockets behind a
  bearer token by default. Token resolution order: --token flag,
  FUSION_DASHBOARD_TOKEN env, FUSION_DAEMON_TOKEN env (back-compat), or an
  auto-generated fn_<32 hex>. --no-auth disables. The startup banner prints
  a click-to-open URL with ?token=<token> embedded.
- Auth middleware now also accepts fn_token=<token> as a query-string
  fallback so EventSource and WebSocket clients (which can't set custom
  headers) still authenticate.
- setupTerminalWebSocket / setupBadgeWebSocket now refuse unauthenticated
  upgrades with a proper 401 + socket close.
- Frontend: new auth.ts module captures ?token= off the URL into
  localStorage (key fn.authToken), strips it from the visible URL via
  replaceState, and installs a window.fetch wrapper that injects
  Authorization: Bearer <token> on every same-origin /api/* request.
  EventSource/WebSocket URL builders (api.ts, sse-bus.ts, useTerminal,
  useBadgeWebSocket) route through appendTokenQuery().

MIT license
- LICENSE file at repo root.
- license: "MIT" on root package.json and every packages/*/package.json,
  plus description/bugs metadata on the CLI package.

Docs
- docs/cli-reference.md documents --token / --no-auth / FUSION_DASHBOARD_TOKEN
  and the click-to-open auth flow.
- docs/getting-started.md, docs/docker.md, README.md point at the new flow
  and the CLI reference section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-21 18:01:20 -07:00
parent 2c16620eff
commit c0f9260998
24 changed files with 496 additions and 68 deletions

View File

@@ -40,16 +40,68 @@ fn init --name my-project --path /absolute/path/to/project
## `fn dashboard`
Start the web dashboard (default port `4040`).
Start the web dashboard (default port `4040`, bound to `127.0.0.1`).
```bash
fn dashboard
fn dashboard --port 5050
fn dashboard --host 0.0.0.0 # expose on LAN (use with care)
fn dashboard --token fn_yourStaticToken # reuse a fixed token
fn dashboard --no-auth # disable bearer auth (local only)
fn dashboard --interactive
fn dashboard --paused
fn dashboard --dev
```
| Option | Description |
|---|---|
| `--port`, `-p` | Dashboard HTTP port (default `4040`). |
| `--host` | Host to bind (default `127.0.0.1`, localhost only). Pass `0.0.0.0` to expose on all interfaces. |
| `--token <token>` | Bearer token to use. Default: `$FUSION_DASHBOARD_TOKEN``$FUSION_DAEMON_TOKEN` → auto-generated. |
| `--no-auth` | Disable bearer-token auth. Not recommended when binding to `0.0.0.0`. |
| `--paused` | Start with the engine paused (automation disabled). |
| `--interactive` | Interactive port selection. |
| `--dev` | Start dashboard only (no AI engine, no triage/scheduler). |
### Authentication
Unless `--no-auth` is passed, the dashboard API (including the terminal
WebSocket) is protected by a bearer token. On startup, Fusion prints both the
raw token and a click-to-open URL that embeds `?token=<token>`:
```
fn board
────────────────────────
→ http://localhost:4040
Auth: bearer token required
Token: fn_8f3a...
Open: http://localhost:4040/?token=fn_8f3a...
(the browser stores the token so you only need to click once)
```
On first visit the dashboard captures the token from the URL into
`localStorage` (key `fn.authToken`) and strips it from the visible URL so the
secret does not end up in browser history. Subsequent loads (including
closing and reopening the tab) reuse the stored token.
Precedence when resolving the token:
1. `--token <token>` flag
2. `FUSION_DASHBOARD_TOKEN` environment variable
3. `FUSION_DAEMON_TOKEN` environment variable (back-compat with `fn daemon`)
4. Random `fn_<32 hex>` generated per run
To reuse a stable token across runs, export one of the env vars:
```bash
export FUSION_DASHBOARD_TOKEN=fn_my_stable_token
fn dashboard
```
If you ever need to revoke access, either restart `fn dashboard` (which
rotates the auto-generated token) or clear the `fn.authToken` entry from
each client's `localStorage`.
---
## `fn serve`

View File

@@ -32,10 +32,20 @@ Pass provider credentials and integrations with `-e` flags:
-e ANTHROPIC_API_KEY=...
-e OPENAI_API_KEY=...
-e GITHUB_TOKEN=...
-e FUSION_DASHBOARD_TOKEN=fn_your_stable_token # optional; persists across restarts
```
Add any other provider keys your setup requires (for example `OPENROUTER_API_KEY`).
### Dashboard authentication
The dashboard is bearer-token protected by default. In a container the
auto-generated token appears in `docker logs` on startup — copy it, or set
`FUSION_DASHBOARD_TOKEN` (or the back-compat `FUSION_DAEMON_TOKEN`) to a
stable value so the token survives restarts. See
[CLI reference → fn dashboard → Authentication](./cli-reference.md#fn-dashboard)
for the full flow.
## Pass additional CLI flags
You can append normal CLI arguments after the image name:

View File

@@ -72,12 +72,25 @@ fn dashboard --paused # start with automation paused
fn dashboard --dev # run UI only (no engine)
```
Open: `http://localhost:4040` (or your custom port).
On startup, Fusion prints a click-to-open URL that includes a bearer token:
```
→ http://localhost:4040
Token: fn_8f3a...
Open: http://localhost:4040/?token=fn_8f3a...
```
Click the **Open** link. Your browser captures the token into `localStorage`,
strips it from the visible URL, and reuses it automatically on later loads.
See [CLI reference → fn dashboard → Authentication](./cli-reference.md#fn-dashboard)
for details, including how to set a stable token via `FUSION_DASHBOARD_TOKEN`
or disable auth with `--no-auth` for strictly-local setups.
Other launch modes:
```bash
fn serve --port 5050 --host 0.0.0.0 # headless node (API + engine, no web UI)
fn dashboard --host 0.0.0.0 # expose on LAN (auth stays on by default)
fn serve --port 5050 --host 0.0.0.0 # headless node (API + engine, no web UI)
fn daemon --port 5050 # daemon mode with token auth support
fn desktop # launch Electron desktop app
```