Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
- name: Playwright E2E (approval workspace)
env:
FD_E2E_FORCE_APPROVAL: "1"
PW_WEBSERVER_APPROVAL: "1"
run: |
cd web
npx playwright install chromium
Expand Down Expand Up @@ -126,6 +127,7 @@ jobs:
shell: bash
env:
FD_E2E_FORCE_APPROVAL: "1"
PW_WEBSERVER_APPROVAL: "1"
run: |
cd web
npx playwright install chromium
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ This project follows [Semantic Versioning](https://semver.org/). From **v1.0.0**
- **Web Runs:** forensics UX — empty / offset / truncation messaging, export copy aligned to server limits, trace band rows, **View** drawer with structured fields and full event JSON, extra table columns (trace, status).
- **Web Diff:** scannable sections (policy, evidence window, pricing/catalog/hints, rollups), pre-query hint, `evaluated_at` when present; **examples** index and **integration** README link **`/#/diff`** and **`POST /v1/diff`** to the end-to-end loop.
- **Web Actions:** workspace loading skeleton; numbered steps when approval is on; pending table **Use for confirm** and **Refresh list**; clearer browser confirm copy and approval-reason placeholder.
- **Web shell / Overview:** skeleton loading instead of plain “Loading…”; **Refresh** disabled while loading; ledger metrics line with links to **Diff** and **Runs**; Diff query card **`aria-busy`** while computing.
- **Web shell / Overview:** skeleton loading instead of plain “Loading…”; **Refresh** disabled while loading; ledger metrics line with links to **Diff** and **Runs**; per-metric hint lines; Diff query card **`aria-busy`** while computing.
- **Web Runs:** optional **Group by trace_id** (collapsible `<details>` per trace); **View** uses **`aria-haspopup="dialog"`**.
- **Web Diff:** warn when imported **pricing table versions** or **providers** differ between baseline and candidate (same `pricing` block as before).
- **Web security strip:** loading state with **`aria-busy`** while **`/health`** is fetched.
- **Web Actions:** **Rollback** uses danger-styled button (still same confirm + API).
- **Examples / deploy / SECURITY:** [examples/README.md](examples/README.md) step 7 and readiness row mention **`/#/runs`** grouping; [examples/deploy/README.md](examples/deploy/README.md) operator checklist; Compose **`restart: unless-stopped`** on the reference service; **[SECURITY.md](SECURITY.md)** links the deploy guide for operational hardening.
- **Playwright:** `e2e-server.mjs` enables **`promotion_requires_approval`** only when **`PW_FORCE_APPROVAL_WORKSPACE=1`** (set from the CLI target or **`PW_WEBSERVER_APPROVAL`**); default suite no longer breaks on a stray **`FD_E2E_FORCE_APPROVAL`** shell export; **`reuseExistingServer: false`** for a clean workspace each run; **[web/README.md](web/README.md)** documents approval vs default runs.

### Added

Expand Down
2 changes: 2 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,5 @@ The bundled server is intended for **local development and demos**. **`POST /v1/
**Human approval** (`promotion_requires_approval: true` in `flightdeck.yaml`) adds a **second actor step** before a promote is applied: **`POST /v1/promote/request`** creates a pending row; **`POST /v1/promote/confirm`** completes it. **Policy still runs on confirm** — approval is not a bypass; a request that fails policy remains blocked with the same HTTP **409** outcome as a direct promote. **`GET /v1/workspace`**, **`GET /v1/promotion-requests`**, and other read-only **`GET /v1/*`** routes stay on the read tier (no Bearer required unless you add external controls).

**`POST /v1/events`** and **`POST /v1/diff`** have **no server-side host or token check** in `server/routes/ingest.py` and `server/routes/actions.py`. They are open to any caller that can reach the server. When `flightdeck serve` binds to `127.0.0.1` (the default), this is safe by network topology. If you use `--host 0.0.0.0` or bind to a non-loopback address, event ingest and diff become reachable from any client. Protect them at the network layer (firewall / reverse proxy) if the server is exposed on a shared or public network.

For **Compose healthchecks**, **SQLite backup** scheduling, and an **operator checklist** (logs, restarts, one writer per workspace file), see **[examples/deploy/README.md](examples/deploy/README.md)**.
9 changes: 5 additions & 4 deletions docs/web-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ The app uses **HashRouter** (`react-router-dom`) so all navigation stays within

| Hash path | Component | HTTP calls | Notes |
|-----------|-----------|-----------|-------|
| `#/` | `OverviewPage` | `GET /v1/releases`, `GET /v1/promoted`, `GET /v1/actions`, `GET /v1/metrics` (parallel where applicable) | Ledger metrics (read-only); skeleton while loading; links to Diff/Runs |
| `#/diff` | `DiffPage` | `POST /v1/diff` | Sections: policy gate (incl. `evaluated_at`), evidence window, pricing/catalog/hints, per-1k prices when present, cost/quality rollups; raw JSON panel |
| `#/runs` | `RunsPage` | `GET /v1/releases` (for datalist), `GET /v1/runs`, `GET /v1/runs/export` | Forensics: filters, table (trace/status, trace band rows), **View** drawer, empty/offset/truncation hints, NDJSON download |
| `#/actions` | `ActionsPage` | `GET /v1/workspace`, `GET /v1/promotion-requests` (when `promotion_requires_approval`), `POST /v1/promote` **or** `POST /v1/promote/request` + `POST /v1/promote/confirm`, `POST /v1/rollback` | Workspace skeleton then strip; approval path: numbered steps, pending **Refresh list** / **Use for confirm**; see **ActionsPage** below |
| `#/` | `OverviewPage` | `GET /v1/releases`, `GET /v1/promoted`, `GET /v1/actions`, `GET /v1/metrics` (parallel where applicable) | Ledger metrics (read-only); short per-counter hints; skeleton while loading; links to Diff/Runs |
| `#/diff` | `DiffPage` | `POST /v1/diff` | Sections: policy gate (incl. `evaluated_at`), evidence window, pricing/catalog/hints (incl. provider/version skew callout when sides differ), per-1k prices when present, cost/quality rollups; raw JSON panel |
| `#/runs` | `RunsPage` | `GET /v1/releases` (for datalist), `GET /v1/runs`, `GET /v1/runs/export` | Forensics: filters, table (trace/status, trace band rows or **Group by trace_id**), **View** drawer, empty/offset/truncation hints, NDJSON download |
| `#/actions` | `ActionsPage` | `GET /v1/workspace`, `GET /v1/promotion-requests` (when `promotion_requires_approval`), `POST /v1/promote` **or** `POST /v1/promote/request` + `POST /v1/promote/confirm`, `POST /v1/rollback` | Workspace skeleton then strip; approval path: numbered steps, pending **Refresh list** / **Use for confirm**; **Rollback** danger-styled; see **ActionsPage** below |
| `#/*` (any other) | — | Redirects to `#/` | |

`App.tsx` declares the route tree. `AppShell` is the layout wrapper rendered for all routes.
Expand Down Expand Up @@ -112,6 +112,7 @@ warning strip:
| Condition | What is shown |
|-----------|---------------|
| `UI_READ_ONLY=true` | Info banner: "Read-only UI: navigation to promote and rollback is disabled." |
| `/health` in flight | Muted line + skeleton; **`aria-busy="true"`** on the strip |
| `/health` fetch failed | Warning banner: "Could not load server security mode." (with error detail) |
| `mutation_auth === null` (unknown value) | Nothing (renders `null`) |
| Server `"bearer"` + client has no token | **Warning**: token mismatch — promote/rollback will be rejected until the UI token matches the server |
Expand Down
4 changes: 2 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This folder holds **copy-pasteable** references for wiring FlightDeck into a rea
4. **Diff and gate** in CI: `flightdeck release diff …` with **`--fail-on-policy`** when you want a non-zero exit without mutating promotion — see [ci/](ci/README.md) and `ledger_gate.py` / GitHub Actions templates. Optional **`pricing_catalog_path`** in `flightdeck.yaml` adds **`pricing.catalog`** / **`pricing.hints`** on diffs (see [docs/pricing-catalog.md](../docs/pricing-catalog.md)). **Same contract in the browser or HTTP:** with `flightdeck serve`, open **`/#/diff`** for structured policy / pricing / rollup sections, or call **`POST /v1/diff`** (matches **`flightdeck release diff --output json`**). Details: [docs/web-ui.md](../docs/web-ui.md), [docs/http-api.md](../docs/http-api.md).
5. **Promote or rollback** via CLI (`flightdeck release promote` / `rollback`) or HTTP `POST /v1/promote` and `POST /v1/rollback` (token + loopback rules apply). When **`promotion_requires_approval: true`**, use **`release promote-request`** / **`promote-confirm`** or **`POST /v1/promote/request`** then **`POST /v1/promote/confirm`** — see [ci/promote_with_approval.sh](ci/promote_with_approval.sh) and [ci/README.md](ci/README.md) (GitHub Actions patterns).
6. **Run the server** in a container or compose stack — see [deploy/](deploy/README.md). The bundled UI calls **`GET /v1/workspace`** to choose direct promote vs request/confirm.
7. **Triage runs** with **`flightdeck runs list`** / **`runs export`** or **`GET /v1/runs`**, and **observe** aggregate ledger size with **`GET /v1/metrics`** (JSON counters; read-only, same access tier as other `GET /v1/*` routes).
7. **Triage runs** with **`flightdeck runs list`** / **`runs export`** or **`GET /v1/runs`**, and **observe** aggregate ledger size with **`GET /v1/metrics`** (JSON counters; read-only, same access tier as other `GET /v1/*` routes). With **`flightdeck serve`**, **`/#/runs`** adds optional **Group by trace_id** (collapsible sections) on top of the same API slice.

## Readiness checklist (quick pass)

Expand All @@ -20,7 +20,7 @@ Use this as a **discoverability** pass for the **[ROADMAP.md](../ROADMAP.md)** s
|--------|----------------|
| **Approval-gated promote in CI** | [ci/promote_with_approval.sh](ci/promote_with_approval.sh), [ci/README.md](ci/README.md), [ci/github-actions/promote-approval-twostep.yml](ci/github-actions/promote-approval-twostep.yml) |
| **Two-provider (or catalog) pricing on a diff** | [docs/pricing-catalog.md](../docs/pricing-catalog.md); tests **`test_diff_cross_provider_releases`** and **`test_catalog_comparable_cost_on_cross_provider_diff`** in `tests/` |
| **Operate `flightdeck serve` with deployment guidance** | [deploy/README.md](deploy/README.md) (**Compose healthcheck**, **`FLIGHTDECK_LOCAL_API_TOKEN`**, **SQLite backup** via **`flightdeck doctor --backup`**); optional **Helm** under [deploy/chart/flightdeck/](deploy/chart/flightdeck/) |
| **Operate `flightdeck serve` with deployment guidance** | [deploy/README.md](deploy/README.md) (**Compose healthcheck**, **`restart: unless-stopped`**, **`FLIGHTDECK_LOCAL_API_TOKEN`**, **SQLite backup** via **`flightdeck doctor --backup`**, operator checklist); optional **Helm** under [deploy/chart/flightdeck/](deploy/chart/flightdeck/) |

## Subfolders

Expand Down
8 changes: 7 additions & 1 deletion examples/deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,13 @@ Use an absolute path on Linux/macOS; on Windows Docker Desktop, use a path Docke

## Process supervision

Compose sets a **`healthcheck`** on **`/health`** plus restart policies; for systemd/Kubernetes, reuse the same image and run **`/entrypoint.sh`** (or invoke **`flightdeck serve`** directly with a prepared workspace directory).
Compose sets a **`healthcheck`** on **`/health`** plus **`restart: unless-stopped`** on the service; for systemd/Kubernetes, reuse the same image and run **`/entrypoint.sh`** (or invoke **`flightdeck serve`** directly with a prepared workspace directory).

## Operator checklist

- **Logs:** `docker compose logs -f flightdeck` (or your platform log stream) when debugging ingest or policy failures.
- **State:** one **`flightdeck serve`** instance per workspace SQLite file; do not run two writers against the same volume.
- **Upgrades:** rebuild the image on semver bumps; keep **`/workspace`** mounted so the ledger survives container recreation.

## Related

Expand Down
1 change: 1 addition & 0 deletions examples/deploy/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ services:
flightdeck:
build: .
init: true
restart: unless-stopped
volumes:
- fd_workspace:/workspace
ports:
Expand Down
Loading
Loading