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

2.8 KiB

Implementation Plan: M2 Market Web

Branch: 002-market-web | Date: 2026-07-02 | Spec: 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

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).