# CLAUDE.md ## Overview MCQ (Metacircular Document Queue) is a reading queue service. Documents (raw markdown) are pushed via API from inside the infrastructure, then read through a mobile-friendly web UI from anywhere. MCIAS authenticates all access — any user (including guest) can read, any user (including system accounts) can push. ## Build Commands ```bash make all # vet → lint → test → build make mcq # build binary with version injection make build # go build ./... make test # go test ./... make vet # go vet ./... make lint # golangci-lint run ./... make proto # regenerate gRPC code from .proto files make proto-lint # buf lint + buf breaking make devserver # build and run locally against srv/mcq.toml make clean # remove binaries ``` Run a single test: ```bash go test ./internal/db/ -run TestPutDocument ``` ## Architecture Single binary, three concerns: - **REST API** (`/v1/*`) — CRUD for documents, MCIAS Bearer token auth - **gRPC API** (`:9443`) — same operations, MCIAS interceptor auth - **Web UI** (`/`, `/d/{slug}`, `/login`) — goldmark-rendered reader, MCIAS session cookies Documents keyed by slug (unique). PUT upserts — same slug replaces content. ## Project Structure ``` cmd/mcq/ CLI entry point (server subcommand) internal/ db/ SQLite schema, migrations, document CRUD server/ REST API routes and handlers grpcserver/ gRPC server, interceptors, service handlers webserver/ Web UI routes, templates, session management render/ goldmark markdown-to-HTML renderer proto/mcq/v1/ Protobuf definitions gen/mcq/v1/ Generated Go code (do not edit) web/ Embedded templates + static files deploy/ systemd, examples ``` ## Shared Library MCQ uses `mcdsl` (git.wntrmute.dev/mc/mcdsl) for: auth, db, config, httpserver, grpcserver, csrf, web (session cookies, auth middleware, template rendering). ## Critical Rules 1. **REST/gRPC sync**: Every REST endpoint has a corresponding gRPC RPC. 2. **gRPC interceptor maps**: New RPCs must be added to the correct map. 3. **No test frameworks**: stdlib `testing` only, real SQLite in t.TempDir(). 4. **CSRF on all web mutations**: double-submit cookie pattern. 5. **Session cookies**: HttpOnly, Secure, SameSite=Strict. ## SSO (public vs internal MCIAS URLs) MCQ is reachable publicly (`mcq.metacircular.net`), so its SSO uses **two** MCIAS URLs (via `mcdsl/sso` ≥ v1.9.0): - `[mcias].server_url` — the **internal** address (`https://mcias.svc.mcp.metacircular.net:8443`) used for the server-to-server authorization-code exchange. Efficient and does not depend on the public edge. - `[sso].public_url` — the **public, browser-facing** MCIAS base URL (`https://mcias.metacircular.net`) used to build the authorize redirect, so end-user browsers (which can't resolve the internal name) can reach it. - `[sso].redirect_uri` must be the **public** callback (`https://mcq.metacircular.net/sso/callback`) and must match the `redirect_uri` registered for the `mcq` SSO client in MCIAS (`mciasctl sso update --client-id mcq --redirect-uri ...`). If `public_url` is empty the authorize redirect falls back to `server_url` (tailnet-only SSO). The startup log prints both `authorize_url` and `exchange_url` so you can confirm the split. ## Deployment / runtime - **Containers run rootless under MCP.** Dockerfiles must NOT declare `VOLUME /srv/mcq`, pre-create/chown the data dir, or set `USER` — MCP bind-mounts `/srv/mcq` and runs `--user 0:0`. See `../engineering-standards.md` → Containerization. - **Not unikernel-eligible (yet).** MCQ writes a SQLite DB to `/srv/mcq`; the unikernel runtime currently bakes config/certs read-only and has no writable host mount, so MCQ stays a container until 9p/virtio-blk storage lands. See `docs/unikernels.md` in the workspace root.