feat: multi-tenant SaaS + distributed-tracing spans - #5
Merged
Conversation
- Pass NEXT_PUBLIC_* as build args so they're inlined into the frontend image - Add .dockerignore (keeps node_modules/.next/.env out of the build context) - Rename middleware.ts -> proxy.ts (Next.js 16 renamed the middleware file) - Wrap the sign-in form in <Suspense> (useSearchParams build requirement)
- Migrations: organizations + memberships + projects.org_id (003); api_keys table with rotation/revocation (004); Row-Level Security via a restricted dashboard_reader role scoped by app.project_id GUC (005, fails closed) - Internal endpoints (BFF-only): provision user+org+project on login, list access, self-serve project/key create/rotate/delete, realtime-token mint - Auth planes: authenticate_internal + resolve_project_id (internal token + project id, or x-api-key); scoped_conn runs dashboard reads under RLS - Realtime: Socket.io + SSE accept short-lived project-scoped tokens - Per-project ingest rate limit; CORS locked to configured origins - Fix: get_timeseries interval param (timedelta); /v1/traces JSONB tags parse
- BFF routes: /api/projects (list/create/delete/rotate), /api/lb/* read proxy (session -> membership check -> backend with internal token), /api/realtime-token - Provision user+org on login (auth.ts); session carries allowed project ids - ProjectProvider now backend-backed (real projects, no localStorage mock); create dialog + switcher use the real key lifecycle - Dashboard reads go through the BFF scoped to the active project (no browser key) - Realtime (metrics/incidents/live-log) use short-lived project-scoped tokens - Honesty: a real project never shows synthetic/mock data — real values or an empty state (stat cards, traces, and all useApiQuery-backed panels) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SpanBuffer + sendSpans() POST to /v1/spans (batched, fire-and-forget) - Express middleware emits a root span per request (service/operation/ duration/status/tags) and propagates x-trace-id + x-parent-span-id so downstream instrumented services can attach child spans - New config: `service` name and `tracing` toggle (default on)
ryzrr
force-pushed
the
feat/multi-tenancy
branch
from
July 18, 2026 17:31
7d52b77 to
a505ce2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Turns Liveboard into a hosted, isolated multi-tenant SaaS, and makes traces real.
Multi-tenancy (all live-tested)
dashboard_readerrole (migration 005) — cross-tenant reads return 0 rows, fail-closedHonesty fixes
Distributed tracing (Option A)
x-trace-id,x-parent-span-id) ready for multi-service traces (v2)Fixes
get_timeseriesinterval param (timedelta);/v1/tracesJSONB tags parse; 204 responsesStatic-checked (tsc, eslint --max-warnings 0, py_compile) and exercised end-to-end with a real SDK-instrumented demo service.