Companion to
M13_FRONTEND_SPEC.md. This is the cross-milestone plan; per-milestone detail lives inM13a_PLAN.md,M13b_PLAN.md,M13c_PLAN.md.
Status: planning (post-M11, parallel to M12)
Goal: ship a v1 multi-tenant admin UI that replaces dlq.html
and adds CRUD + live-tail + audit + auth surfaces, in a single
admind Go binary that embeds a Vite+React 19 SPA.
The original M13 plan (workstream-as-primary-axis, 9 workstreams) assumed one engineer ships end-to-end. In practice you stop and review between surfaces — and you want each stop to be a shippable artifact, not "we have some auth working but no UI yet".
The milestone split lets you:
| Stop after | What you have | Why stop here |
|---|---|---|
| M13a | authd + web skeleton + login works | "Is the design right? Is the auth model right? Is the SPA architecture right?" — review before committing to feature work. |
| M13b | Companies + Sources + Telegram CRUD | ~80% of operator value. The customer can self-onboard, set up sources, configure telegram. Live tail + DLQ + audit can wait. |
| M13c | Live tail + DLQ + Audit + K8s + E2E | Full v1. Replaces dlq.html, ships Playwright gate, K8s-ready. |
Each milestone flips one SPEC.md row. Each has its own
M13x_PLAN.md with workstreams, estimates, and per-milestone DoD.
┌──────────────┐
│ M13.0 spec │ (this PR — no code)
│ + API │
└──────┬───────┘
│
▼
┌──────────────┐
│ M13a │ ~5-6 days
│ authd + │
│ web shell │
│ + JWT gate │
└──────┬───────┘
│
▼
┌──────────────┐
│ M13b │ ~7-8 days
│ CRUD: │
│ Companies │
│ Sources │
│ Telegram │
└──────┬───────┘
│
▼
┌──────────────┐
│ M13c │ ~7-9 days
│ Live tail │
│ DLQ │
│ Audit │
│ K8s │
│ Playwright │
└──────────────┘
Total estimate: ~19-23 days with one engineer. Can be parallelized with a second engineer at M13b (one on Companies, one on Sources).
M13_FRONTEND_SPEC.mdM13_API_CONTRACT.mdcmd/authd/ — JWT IdP service on :8804, all 7 endpoints.web/ — Vite + React 19 + shadcn/ui + TanStack Query + RHF + Zod.
Login page works end-to-end. Other routes render "coming soon".admind modified to require JWT on all /v1/* endpoints (except
/health, /metrics).M13a_PLAN.md.M13b_PLAN.md.dlq.html.admind and authd (carry from M12 W1).M13c_PLAN.md.These live in the spec but are operationally tested in M13c:
admind embeds the SPA via embed.FS. Target
size: < 60 MB.admind itself. authd is same-origin
via service discovery.pnpm run gen:api in CI on every Go API
change. Type drift fails the PR.WHERE company_id = $1 for
tenant-admins. Tested in Playwright with a cross-tenant 403 check.audit_log. The viewer is read-only.| From | What | Status | Affects |
|---|---|---|---|
| M11 | gRPC ingest stable, F2 publish counter | ✅ done | M13c (live tail source) |
| M12 W1 | K8s manifests for admind and authd |
⏳ in flight | M13c W7 (K8s manifests) |
| Postgres | auth schema + tables |
to be created in M13a W1 | M13a |
| Redis | rate-limit counters for authd login |
✅ already in stack | M13a |
| NATS | tailhub for in-memory SSE feed (M13c) |
✅ already exists | M13c |
| SMTP (optional) | magic-link email | NOT required for v1 (psql fallback) | M13a (best-effort) |
| When | What runs in parallel |
|---|---|
| M13a W1-W2 | M12 W1 (K8s manifests) — independent |
| M13b W1-W3 | M12 W2 (NATS cluster) — independent |
| M13c W7 | M12 W3-W4 (Helm + 50k/s bench) — blocks K8s manifest merge |
M13 does not block M12. M12 W5 (M11 prod gate on K8s) is independent of M13. The two milestones converge at "single PR that ships v1 with K8s + frontend" but the workstream branches don't have to.
cmd/authd/ exists, builds, runs, healthy on :8804.cmd/admind/ builds with embedded SPA, < 60 MB.M13_VERIFICATION.md published with: screenshots of the 6
modules, the Playwright run log, the tenant-isolation test,
the binary size, and the bundle breakdown.M13a, M13b, M13c rows flipped to ✅ shipped
YYYY-MM-DD.cmd/admind/ui/dlq.html removed.Next step: commit the 3 docs from M13.0, then start M13a W1
(authd).