From cc35750817687d1191a06a1ba6dc0badac607918 Mon Sep 17 00:00:00 2001 From: Bryan Deng Date: Sun, 5 Jul 2026 19:15:21 -0400 Subject: [PATCH] docs: retire monolithic SPEC-backend.md delete dev-docs/SPEC-backend.md now that its content lives in co-located AGENTS.md files. drop the eager @import from root AGENTS.md (that import loaded all 728 lines into every session), point the root at the co-located docs, and rewrite the maintenance rule to update the nearest AGENTS.md rather than one monolithic spec. fix the README link too. Co-Authored-By: Claude Opus 4.8 --- AGENTS.md | 8 +- README.md | 2 +- dev-docs/SPEC-backend.md | 728 --------------------------------------- 3 files changed, 6 insertions(+), 732 deletions(-) delete mode 100644 dev-docs/SPEC-backend.md diff --git a/AGENTS.md b/AGENTS.md index 8dabc92..1dcd3d1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,6 @@ -See @dev-docs/SPEC-backend.md for the backend spec and @dev-docs/SPEC-frontend.md for the frontend spec. +Backend docs are co-located with the code as `AGENTS.md` files under `builders/server` (start at `builders/server/AGENTS.md` for the layer map; each subpackage has its own). Claude Code auto-loads the one nearest to what you are working on, so they are not imported here. Authoring datasets/builders is documented in `builders/scripts/AGENTS.md`, and infra in `infra/AGENTS.md`. + +See @dev-docs/SPEC-frontend.md for the frontend spec. ## Before writing code @@ -34,7 +36,7 @@ Structure stacks by architectural layer, bottom-up: 1. Schema / data model changes 2. Business logic / service layer 3. API / interface layer -4. If there is a significant spec change: update the relevant spec file (`SPEC-backend.md` or `SPEC-frontend.md`) as the final PR +4. If there is a significant doc change: update the co-located `AGENTS.md` for the affected backend layer (or `SPEC-frontend.md` for the frontend) as the final PR Each PR in a stack should make one logical change. It is acceptable — and sometimes desirable — for a later PR to overwrite or refine what an earlier PR did. This is intentional: the stack shows the logical progression to the final state, not just the diff. @@ -72,7 +74,7 @@ Repeat steps 2–6 for each PR in the stack. See the `git-town` skill for full c ## Tech specifications -After EVERY SET of updates to the code, update the relevant spec file (@dev-docs/SPEC-backend.md or @dev-docs/SPEC-frontend.md) with what has been changed in the code. +After EVERY SET of updates to the code, update the docs for what changed. For the backend, update the nearest co-located `AGENTS.md` to the code you changed (start at `builders/server/AGENTS.md` for the layer map, but only touch it when layer boundaries change). For the frontend, update @dev-docs/SPEC-frontend.md. Keep each doc lean: per-function mechanics belong in docstrings, cross-cutting "why" belongs in the co-located `AGENTS.md`. ## Pull requests diff --git a/README.md b/README.md index b9eaffa..7080017 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A service to serve and build financial time-series data. Datastream implements a **builder server** (Python/FastAPI), a **Postgres database**, a **Caddy reverse proxy** with automatic HTTPS, example builder scripts, and a **Svelte frontend**. -See the full technical specs: [backend](dev-docs/SPEC-backend.md) | [frontend](dev-docs/SPEC-frontend.md) | [sdk](dev-docs/SPEC-sdk.md) +Technical docs: backend docs live in co-located `AGENTS.md` files under [`builders/server`](builders/server/AGENTS.md); [frontend](dev-docs/SPEC-frontend.md) | [sdk](dev-docs/SPEC-sdk.md) ## Prerequisites diff --git a/dev-docs/SPEC-backend.md b/dev-docs/SPEC-backend.md deleted file mode 100644 index db6bdfb..0000000 --- a/dev-docs/SPEC-backend.md +++ /dev/null @@ -1,728 +0,0 @@ -# Datastream Backend - -A service to serve and build financial time-series data. - -## Service - -A single Python FastAPI service handles both public API traffic and builds. It listens on port 3000. - -### Public endpoints - -``` -GET /status -``` - -``` -GET /datasets -``` - -``` -POST /build/{dataset_name}/{dataset_version}?start=&end=&dry-run= -``` - -``` -GET /data/{dataset_name}/{dataset_version}?start=&end=&build-data= -``` - -### API authentication - -Every endpoint except `GET /status` requires a valid API key sent as a bearer token: - -``` -Authorization: Bearer -``` - -`/status` is intentionally unauthenticated so the Docker healthcheck (which hits `/api/v1/status`) keeps working. - -**Key storage.** Valid keys live in the `API_KEYS` env var as comma-separated `label:sha256hex` pairs, e.g. `default:ab12…,team-a:cd34…`. Only the **sha256 hash** of each key is stored, so a leaked config or log never reveals a working key. The `label` identifies the caller (a team) and is bound to the structlog context as `team` on every authenticated request. The env format is already a `label -> hash` map, so extending from one shared key to a key-per-team is just another entry — no format change. - -**Verification.** The `verify_api_key` dependency (`core/auth.py`) hashes the presented token and looks it up in the key map. Comparing the hash (not the raw key) is timing-safe: the compared value is already a sha256 of the secret, so timing cannot recover the key. Enforcement is wired once in `main.py`: `public_router` (carrying `/status`) mounts open, and `router` (everything else) mounts with `dependencies=[Depends(verify_api_key)]`. - -**Fail-closed startup.** The `lifespan` handler loads `API_KEYS` and raises if it is empty, so the service refuses to start unauthenticated on a public network. There is no "auth disabled" flag. - -**Key generation.** Mint a key and its env line with: - -``` -cd builders/server && uv run python -m core.auth generate