m2-market/specs/002-market-web/plan.md

62 lines
2.8 KiB
Markdown

# Implementation Plan: M2 Market Web
**Branch**: `002-market-web` | **Date**: 2026-07-02 | **Spec**: [spec.md](./spec.md)
## Summary
One Coolify-deployed container: FastAPI backend that proxies the three live rails
(memory-api catalog, m2-ledger, Forgejo registry) with keys server-side, serving a
React/Vite SPA. Read-only + install handoff. Rides the proven CI/CD (push → deploy),
http-scheme domain market.machinemachine.ai.
## Technical Context
**Language**: Python 3.12 (FastAPI, httpx) + TypeScript React 18/Vite
**Storage**: none (stateless proxy; session cookie is signed, in-memory secret)
**Testing**: pytest for the backend proxy (httpx MockTransport per upstream, secrets-
leak test asserting no key strings in any response); vitest optional for components
**Deploy**: `web/Dockerfile` multi-stage (node build → python runtime), Coolify app
`m2-market-web`, env: FLEET_PASSCODE, SESSION_SECRET, MEMORY_API_URL/KEY,
LEDGER_URL/LEDGER_SERVICE_KEY, FORGEJO_URL/FORGEJO_TOKEN, REGISTRY_REPO
**Constraints**: constitution I (assembly: existing APIs only, no new rails), II (web is
a surface, never truth), IV (tenant filter server-side, same semantics as CLI reader), VI
(no secrets in image; runtime env)
## Constitution Check
Passes: no new services beyond the (sanctioned-as-surface) web app itself; all data
derived from registry/ledger/catalog; keys never leave the backend; read-only economy.
Deviation: none.
## Project Structure
```text
web/
├── backend/
│ ├── src/m2_market_web/
│ │ ├── main.py # FastAPI app: static serve + /api/* + /health
│ │ ├── auth.py # FLEET_PASSCODE login -> signed session cookie
│ │ ├── catalog.py # /api/search, /api/listing/{id} (memory-api proxy,
│ │ │ # tenant filter — mirror cli catalog.py semantics)
│ │ ├── ledger.py # /api/wallet/{op}, /api/economy (balances+audit links)
│ │ └── registry.py # /api/governance (open PRs+labels, listings+status)
│ ├── tests/
│ └── pyproject.toml # uv workspace member
├── frontend/ # Vite + React + TS, dark navy/cyan (mm brand)
│ ├── src/{pages,components,api.ts}
│ └── package.json
└── Dockerfile
```
## Sequencing
1. Backend proxy + tests [P with 2] → 2. Frontend SPA [P with 1] (against a typed
/api contract defined in contracts/web-api.md) → 3. Dockerfile + Coolify app + domain +
webhook → 4. Live verification vs SC-101..105.
## Contract
`contracts/web-api.md` (written with tasks): /api/login {passcode}; /api/search?q=&limit=;
/api/listing/{id}; /api/wallet/{operator}; /api/economy; /api/governance; /health.
Every /api/* except /health and /login requires the session cookie. Responses are
minimal DTOs — never raw upstream bodies (leak surface).