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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.14"]
python-version: ["3.12"]

steps:
- name: Checkout
Expand Down Expand Up @@ -84,7 +84,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.14"]
python-version: ["3.12"]

steps:
- name: Checkout
Expand All @@ -110,7 +110,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.14"]
python-version: ["3.12"]

steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.14
3.12
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Fallback (activated **venv** or global tools): the same steps with **`python -m

On **Windows**, use `py -3` in place of `python` if that is how your environment is set up. If pytest temp dirs fail with permissions, see **`DEVELOPMENT.md`** / **`tests/conftest.py`**.

**CI bar** (mirrors **`.github/workflows/ci.yml`** on **CPython 3.14**): see the workflow for the exact sequence; includes **`uv sync --frozen --extra dev`**, **`web/`** **`npm ci`** + **`npm run build`** + **`git diff --exit-code`** on **`static/`**, Playwright **`npm run test:e2e`**, **ruff**, **pytest**, schema drift check, **`flightdeck-quickstart-verify`**, **`flightdeck --help`**. When you change **`pyproject.toml`** optional extras (including **`flightdeck.integrations`** extras), run **`uv lock`** and commit **`uv.lock`**. The workflow may include a separate **integrations** job that **`uv sync`**s **`dev`** plus selected integration extras and runs targeted tests.
**CI bar** (mirrors **`.github/workflows/ci.yml`** on the **`.python-version`** interpreter): see the workflow for the exact sequence; includes **`uv sync --frozen --extra dev`**, **`web/`** **`npm ci`** + **`npm run build`** + **`git diff --exit-code`** on **`static/`**, Playwright **`npm run test:e2e`**, **ruff**, **pytest**, schema drift check, **`flightdeck-quickstart-verify`**, **`flightdeck --help`**. When you change **`pyproject.toml`** optional extras (including **`flightdeck.integrations`** extras), run **`uv lock`** and commit **`uv.lock`**. The workflow may include a separate **integrations** job that **`uv sync`**s **`dev`** plus selected integration extras and runs targeted tests.

Use a repo-local temp directory if the OS temp directory is restricted.

Expand Down
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ This project follows [Semantic Versioning](https://semver.org/). From **v1.0.0**

## Unreleased

### Breaking

- **`POST /v1/events`:** uses the same **`FLIGHTDECK_LOCAL_API_TOKEN`** / loopback policy as promotion and rollback. Remote unauthenticated ingest is no longer accepted; set the env var and send **`Authorization: Bearer`** (Python SDK **`api_token=`**, or **`--api-token`** / env in **[examples/integration/emit_sample_events.node.mjs](examples/integration/emit_sample_events.node.mjs)**).
- **Python:** **`requires-python`** is **`>=3.11,<4`** (replaces **`>=3.14,<3.15`**). **`[tool.ruff] target-version`** is **`py311`**. CI follows **`.python-version`** (currently **3.12**).

### Changed

- **Docs / examples:** **`DEVELOPMENT.md`**, **`AGENTS.md`**, **`docs/sdk.md`**, **`docs/troubleshooting.md`**, **`examples/integration/README.md`**, **`examples/integration/adoption/README.md`**, **`examples/deploy/README.md`** — align with the Python range and ledger-write ingest model.

### Added

- **Experimental `flightdeck.integrations`:** optional extras **`integrations-langchain`**, **`integrations-temporal`**, **`integrations-openai-agents`**, and meta **`integrations-ci`** (CI job); thin mappers from OpenAI chat completions, Anthropic messages, OpenAI Agents–style results, LangChain callbacks, CrewAI-style manual totals, and Temporal-oriented **`labels`**. Docs: **`docs/sdk-integrations.md`**; examples: **`examples/integration/adoption/`**. Contributor policy updates in **`AGENTS.md`** / **`CLAUDE.md`**.
Expand Down Expand Up @@ -149,7 +158,7 @@ This project follows [Semantic Versioning](https://semver.org/). From **v1.0.0**
- **Slim distribution:** this repository ships a focused in-tree **`docs/`** tree (CLI, HTTP API, SDK, operations/policy, release artifact, web UI references); org mirror scripts and **`verify-repo-standards`** wrappers are not included. Extended maintainer runbooks and the canonical README live on **[github.com/flightdeckdev/flightdeck](https://github.com/flightdeckdev/flightdeck)**; in-repo links now point there where applicable.
- **`pyproject.toml`:** OpenTelemetry packages are **optional** only (**`telemetry`** / **`all`** extras); the default install matches the **1.0.0** dependency story (core does not import OpenTelemetry).
- **`.pre-commit-config.yaml`:** **ruff** replaces **black** / **isort**; **`ruff-pre-commit`** pinned to **v0.15.12** to match **`dev`** (**`ruff==0.15.12`**).
- **CI:** Python **3.13** and **3.14** added to the Ubuntu and Windows matrices (superseded by **3.14**-only policy as of **1.0.2**).
- **CI:** Python **3.13** and **3.14** added to the Ubuntu and Windows matrices (superseded by **3.14**-only policy as of **1.0.2**, then relaxed to **3.11+** as of **1.2.0**).
- **`pyproject.toml`:** default **`pytest --basetemp=.tmp/pytest`** so local runs avoid Windows **`PermissionError`** on **`%TEMP%\pytest-of-*`**.
- **`pre-commit-hooks`:** bumped to **v5.0.0**.

Expand Down
2 changes: 1 addition & 1 deletion DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Requirements

- **CPython 3.14.x** only (`requires-python` in **`pyproject.toml`**; **`.python-version`** pins **3.14** for **uv**). CI runs **3.14** on Ubuntu and Windows.
- **CPython 3.11+** (`requires-python` in **`pyproject.toml`** is **`>=3.11,<4`**; **`.python-version`** pins a contributor default for **uv**, currently **3.12**). CI runs that pinned release on Ubuntu and Windows.
- Git
- **[uv](https://docs.astral.sh/uv/)** (recommended): single tool for venvs, installs, and **`uv run`** ([installation](https://docs.astral.sh/uv/getting-started/installation/)). On Windows you can use `py -3 -m pip install uv` if you do not use the standalone installer.

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Shipped locally:
### Local HTTP API

With **`flightdeck serve`** (default bind **127.0.0.1**), the app exposes **`GET /health`**, **`GET /v1/workspace`**
(read-only workspace flags for scripts and the bundled UI), **`GET /v1/metrics`**, **`GET /v1/releases`**, **`GET /v1/promoted`**, **`GET /v1/actions`**, **`GET /v1/promotion-requests`**, **`GET /v1/runs`**, **`POST /v1/events`**, **`POST /v1/diff`**, **`POST /v1/promote`**, **`POST /v1/promote/request`**, **`POST /v1/promote/confirm`**, and **`POST /v1/rollback`**. **`POST /v1/promote`**, **`POST /v1/promote/request`**, **`POST /v1/promote/confirm`**, and **`POST /v1/rollback`** accept requests only from loopback clients unless **`FLIGHTDECK_LOCAL_API_TOKEN`** is set, in which case callers must send **`Authorization: Bearer <token>`** (same behavior as the **`web/`** dev UI via **`VITE_FLIGHTDECK_LOCAL_API_TOKEN`**). See **[docs/http-api.md](docs/http-api.md)** and **[SECURITY.md](SECURITY.md)**.
(read-only workspace flags for scripts and the bundled UI), **`GET /v1/metrics`**, **`GET /v1/releases`**, **`GET /v1/promoted`**, **`GET /v1/actions`**, **`GET /v1/promotion-requests`**, **`GET /v1/runs`**, **`POST /v1/events`**, **`POST /v1/diff`**, **`POST /v1/promote`**, **`POST /v1/promote/request`**, **`POST /v1/promote/confirm`**, and **`POST /v1/rollback`**. **`POST /v1/promote`**, **`POST /v1/promote/request`**, **`POST /v1/promote/confirm`**, **`POST /v1/rollback`**, and **`POST /v1/events`** accept requests only from loopback clients unless **`FLIGHTDECK_LOCAL_API_TOKEN`** is set, in which case callers must send **`Authorization: Bearer <token>`** (same behavior as the **`web/`** dev UI via **`VITE_FLIGHTDECK_LOCAL_API_TOKEN`**). **`POST /v1/diff`** stays read-only and does not use that gate. See **[docs/http-api.md](docs/http-api.md)** and **[SECURITY.md](SECURITY.md)**.

## Quickstart

Expand Down
12 changes: 11 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,21 @@ Narrative docs (including the CLI reference) are maintained on **[github.com/fli

Patch-line documentation: optional **`flightdeck.integrations`** mappers behind **`integrations-*`**
extras (see **`pyproject.toml`**, **`docs/sdk-integrations.md`**, **`examples/integration/adoption/`**).
**Stable contracts unchanged:** **`RunEvent`** JSON / **`POST /v1/events`**. **`AGENTS.md`** clarifies
**Stable payload contract:** **`RunEvent`** JSON for **`POST /v1/events`** (shape unchanged; **v1.2.0** tightens **HTTP access** for ingest — see **v1.2.0** notes below). **`AGENTS.md`** clarifies
that these adapters are adoption glue, not in-product orchestration or a plugin registry. CI adds
an **`integrations`** job (**`uv sync --frozen --extra dev --extra integrations-ci`**) for LangChain
callback coverage.

## v1.2.0 — Python 3.11+ floor and protected event ingest

Minor release (see **[CHANGELOG.md](CHANGELOG.md)**): **`requires-python`** is **`>=3.11,<4`** so
installs work on common production interpreters (**3.11–3.14**). **`POST /v1/events`** is a **ledger
write** and now matches the promote/rollback access model: **loopback-only** when
**`FLIGHTDECK_LOCAL_API_TOKEN`** is unset; **Bearer required** when it is set (covers Docker **`--host
0.0.0.0`** and private LANs). **`POST /v1/diff`** remains read-only and ungated. Migration: remote
emitters must send **`Authorization: Bearer`** whenever the server uses a local API token; loopback
scripts without a token are unchanged.

## v1.1.2 — Forensics filters, JSONL export, productization closure slice

Patch release (see **[CHANGELOG.md](CHANGELOG.md)**): optional **`trace_id`** filter on **`GET /v1/runs`**, **`flightdeck runs list --trace-id`**, and SDK **`list_runs(trace_id=…)`** (exact match on **`RunEvent.request.trace_id`**); **`flightdeck runs export`** writes the same filtered slice as JSONL (stdout or **`-o`**, **`--limit`** up to **500**, stderr warning when truncated). **Stable contracts:** additive HTTP query param and CLI command only.
Expand Down
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ See **[CONTRIBUTING.md](CONTRIBUTING.md)** for a pre-push checklist aligned with

## Local HTTP API (`flightdeck serve`)

The bundled server is intended for **local development and demos**. **`POST /v1/promote`**, **`POST /v1/promote/request`**, **`POST /v1/promote/confirm`**, and **`POST /v1/rollback`** are gated in server code so that, with no token configured, only **loopback** clients (`127.0.0.1`, `::1`, `localhost`) can invoke them. If you set **`FLIGHTDECK_LOCAL_API_TOKEN`**, every mutation request must include **`Authorization: Bearer <that value>`**; use a strong random value and treat it like a local secret.
The bundled server is intended for **local development and demos**. **`POST /v1/promote`**, **`POST /v1/promote/request`**, **`POST /v1/promote/confirm`**, **`POST /v1/rollback`**, and **`POST /v1/events`** (run event ingest) share one **ledger-write** access model in server code: with no token configured, only **loopback** clients (`127.0.0.1`, `::1`, `localhost`, and the Starlette test client) may call them. If you set **`FLIGHTDECK_LOCAL_API_TOKEN`**, every such request must include **`Authorization: Bearer <that value>`**; use a strong random value and treat it like a local secret. Remote emitters (agents, sidecars) must use the Bearer path when the server listens beyond loopback.

**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.
**`POST /v1/diff`** is intentionally unauthenticated (read-only computation on stored evidence). When `flightdeck serve` binds to `127.0.0.1` (the default), callers are constrained by network topology; if you use **`--host 0.0.0.0`**, protect read routes at the network layer if exposure is a concern.

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)**.
22 changes: 12 additions & 10 deletions docs/http-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,18 @@ Two access tiers:
|-------|--------------------|---------------------------------|
| `GET /health` | open | open |
| `GET /v1/*` (reads, including `GET /v1/workspace`, `GET /v1/metrics`, `GET /v1/runs`, `GET /v1/runs/export`, `GET /v1/promotion-requests`) | open | open |
| `POST /v1/events` | open† | open (no Bearer required) |
| `POST /v1/events` | loopback only | `Authorization: Bearer <token>` required |
| `POST /v1/diff` | open | open |
| `POST /v1/promote` | loopback only | `Authorization: Bearer <token>` required |
| `POST /v1/promote/request`, `POST /v1/promote/confirm` | loopback only | `Authorization: Bearer <token>` required |
| `POST /v1/rollback` | loopback only | `Authorization: Bearer <token>` required |

†`POST /v1/events` has **no server-side loopback or token gate** in code
(`server/routes/ingest.py`). Only `POST /v1/promote`, `POST /v1/promote/request`,
`POST /v1/promote/confirm`, and `POST /v1/rollback` call
`_require_mutation_access`. When the server binds to `127.0.0.1` (the default), ingest is
effectively local-only by network topology, not by application enforcement. If you bind
`--host 0.0.0.0`, event ingest becomes reachable from any host. Protect it at the network
layer (firewall / reverse proxy) if that is a concern.
`POST /v1/events` uses the **same** loopback / Bearer gate as promote and rollback
(`require_ledger_write_access` in `server/mutation_access.py`). Remote agents must set
`FLIGHTDECK_LOCAL_API_TOKEN` on the server and send matching `Authorization: Bearer` headers
(including the Python SDK’s `api_token=`). When no token is configured, only loopback
callers (`127.0.0.1`, `::1`, `localhost`) may append run events, so binding `--host 0.0.0.0`
does not leave ingest open to arbitrary clients on the network.

```bash
export FLIGHTDECK_LOCAL_API_TOKEN="$(openssl rand -hex 32)"
Expand All @@ -62,8 +61,8 @@ Health probe. Always returns HTTP 200 while the server is up.

`mutation_auth` is always present on current servers:

- `"loopback"` — `FLIGHTDECK_LOCAL_API_TOKEN` is not set; promote and rollback are allowed only from loopback clients (no Bearer gate).
- `"bearer"` — `FLIGHTDECK_LOCAL_API_TOKEN` is set; promote and rollback require `Authorization: Bearer <that value>`.
- `"loopback"` — `FLIGHTDECK_LOCAL_API_TOKEN` is not set; ledger writes (including **`POST /v1/events`**) are allowed only from loopback clients (no Bearer gate).
- `"bearer"` — `FLIGHTDECK_LOCAL_API_TOKEN` is set; ledger writes require `Authorization: Bearer <that value>` from any client host.

This field never includes secret material.

Expand Down Expand Up @@ -295,6 +294,9 @@ Response headers (non-secret hints for clients):

Ingest `RunEvent` records (runtime evidence for diff and policy evaluation).

**Auth:** Same as promote/rollback — loopback-only when `FLIGHTDECK_LOCAL_API_TOKEN` is unset;
otherwise `Authorization: Bearer <token>` required (see [Authentication and access control](#authentication-and-access-control)).

**Request body**
```json
{
Expand Down
13 changes: 8 additions & 5 deletions docs/sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,12 @@ method is a coroutine. Call `await client.aclose()` instead of `client.close()`.

## Authentication

When `flightdeck serve` is started with `FLIGHTDECK_LOCAL_API_TOKEN` set, every mutation
request (`POST /v1/promote`, `POST /v1/rollback`) requires the matching Bearer token. Read
routes and event ingest do **not** require the token in the default local setup.
When `flightdeck serve` is started with `FLIGHTDECK_LOCAL_API_TOKEN` set, every **ledger write**
— **`POST /v1/promote`**, **`POST /v1/promote/request`**, **`POST /v1/promote/confirm`**,
**`POST /v1/rollback`**, and **`POST /v1/events`** (`ingest_run_events`) — requires the
matching Bearer token on the `FlightdeckClient` (`api_token=…`). With no token configured
on the server, those routes accept only **loopback** callers; read routes and **`POST /v1/diff`**
stay open.

```python
client = FlightdeckClient(
Expand All @@ -93,7 +96,7 @@ See [SECURITY.md](../SECURITY.md) for the full access model.

### `health() -> dict`

`GET /health` — returns `{"status": "ok", "mutation_auth": "loopback"|"bearer"}` when the server is up (`mutation_auth` describes promote/rollback auth; see **HTTP API**).
`GET /health` — returns `{"status": "ok", "mutation_auth": "loopback"|"bearer"}` when the server is up (`mutation_auth` describes ledger-write auth, including event ingest; see **HTTP API**).

### `get_workspace() -> dict`

Expand Down Expand Up @@ -272,7 +275,7 @@ a no-op for the injected client.

## Constraints

- The SDK targets the same CPython version as the CLI (`>=3.14` from v1.0).
- The SDK targets the same CPython version range as the CLI (`>=3.11,<4` from **v1.2**).
- `httpx` is a required dependency of `flightdeck-ai`; it is not optional.
- `RunEvent` instances must have `api_version = "v1"` (the default). The server rejects
other values with HTTP 400.
Expand Down
10 changes: 5 additions & 5 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ interpreting `doctor` failures) see [operations-and-policy.md](operations-and-po

## Developer environment

### `uv sync` or `pip install` fails on CPython 3.13 or earlier
### `uv sync` or `pip install` fails on CPython 3.10 or earlier

FlightDeck requires **CPython 3.14**. The `requires-python` constraint in `pyproject.toml`
and the `.python-version` pin for `uv` both enforce this.
FlightDeck requires **CPython 3.11+** (see `requires-python` in `pyproject.toml` and the
`.python-version` pin for `uv`).

```
ERROR: Package 'flightdeck-ai' requires a different Python: 3.13.x not in '>=3.14'
ERROR: Package 'flightdeck-ai' requires a different Python: 3.10.x not in '>=3.11'
```

Install CPython 3.14 using `uv python install 3.14` (if using uv) or from
Install a supported CPython (for example `uv python install 3.12`) or from
[python.org](https://www.python.org/downloads/). Confirm the active interpreter:

```bash
Expand Down
2 changes: 1 addition & 1 deletion examples/ci/github-actions/policy-gate-monorepo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
python-version: "3.14"
python-version: "3.12"

- name: Sync Python env
run: uv sync --frozen --extra dev
Expand Down
2 changes: 1 addition & 1 deletion examples/ci/github-actions/policy-gate-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:

- uses: actions/setup-python@v5
with:
python-version: "3.14"
python-version: "3.12"

- name: Install FlightDeck CLI
run: pip install "flightdeck-ai${FLIGHTDECK_AI_SPEC}"
Expand Down
2 changes: 1 addition & 1 deletion examples/deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Inside the Compose stack, **`exec`** into the running container with **`/workspa

### Optional mutation token

Set **`FLIGHTDECK_LOCAL_API_TOKEN`** in your environment before `docker compose up` (or in an `.env` file beside `docker-compose.yml`). Clients must send **`Authorization: Bearer …`** for **`POST /v1/promote`** and **`POST /v1/rollback`**. Ingest and diff are **not** behind this Bearer gate by default—treat network placement accordingly.
Set **`FLIGHTDECK_LOCAL_API_TOKEN`** in your environment before `docker compose up` (or in an `.env` file beside `docker-compose.yml`). Clients must send **`Authorization: Bearer …`** for **ledger writes**: **`POST /v1/promote*`**, **`POST /v1/rollback`**, and **`POST /v1/events`**. With no token configured, those routes accept only **loopback** callers. **`POST /v1/diff`** stays unauthenticated (read-only); still treat network placement as a trust boundary.

## Helm (optional single-replica chart)

Expand Down
Loading
Loading