feat(observer): Data Observer Phase 1 — secure RBAC foundation (observer-api + Supabase + observer-web) - #27
Merged
Merged
Conversation
added 10 commits
June 30, 2026 07:32
create_app() read ObserverSettings() at import time, requiring OBSERVER_JWT_SECRET even when only importing the module. Replace the settings call with a direct os.environ read for OBSERVER_CORS_ORIGINS, matching the pattern in energex.service.readapi. JWT secret validation still happens at request time via auth.py (unchanged). Add regression test: importing energex.observer.app with no OBSERVER_JWT_SECRET set must succeed, and protected routes still 401.
Wrap each lib.read(s) call in a try/except so a corrupt or unreadable
symbol only increments an `unreadable` counter rather than crashing the
entire /catalog response. Each library entry now includes {"name",
"symbols", "rows", "unreadable"}.
Add test_catalog_resilient_to_bad_symbol to verify HTTP 200 is returned,
good symbols are counted, and unreadable >= 1 when one symbol raises.
profiles/audit_log/saved_views/issue_acks with RLS; observer_current_role() helper; handle_new_user() trigger seeds a viewer profile; custom_access_token_hook injects profiles.role into the JWT as the user_role claim. Includes the supabase_auth_admin read policy so the hook can resolve roles under RLS.
…althcheck + test script
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.
Summary
Phase 1 (Foundation) of the internal Data Observer — a secure, role-managed app to explore the platform's bitemporal data. This lands the auth/RBAC spine and the app shell end-to-end. The 4V explorer, quality (Veracity) view, geo map, and Neo4j graph views follow in later phases.
What's included
src/energex/observer/) — FastAPI read service. JWT RBAC (viewer < operator < admin) verified against the Supabase HS256 secret (audienceauthenticated;exp+audrequired). Endpoints:GET /catalog(ArcticDB library/symbol/row inventory, viewer+),GET /me,GET /admin/ping(admin-only), openGET /healthz. Per-symbol read errors are isolated so one unreadable symbol can't fail the whole catalog.supabase/migrations/0001_observer_rbac.sql) —profiles/audit_log/saved_views/issue_ackswith RLS. Role is the source of truth inprofiles.roleand is injected into the JWT as theuser_roleclaim bycustom_access_token_hook. RLS blocks role self-escalation (admin-only writes); a dedicatedsupabase_auth_adminSELECT policy lets the hook resolve roles under RLS. Verified end-to-end — a real minted JWT carries the role.observer-web/) — Next.js 16 + Tailwind v4 "Graphite" dark theme. Supabase SSR auth (proxy.tsgate viagetUser()), role-aware nav rail, and an Overview page reading/catalog. Vitest unit tests + a deferred Playwright@smoke.docker-compose.ymladdsobserver-api(:8090) andobserver-web(:8080) under thefull+observerprofiles;observer-web/Dockerfile(multi-stage,NEXT_PUBLIC_*as build args);.env.examplekeys; CI runs observer-api tests via--all-extrasand adds anobserver-webbuild + Vitest job.Security model (defense-in-depth)
observer-api is the real authorization boundary: it re-verifies every JWT and enforces min-role per endpoint. The frontend only does UX gating and carries the bearer token — the client is never trusted. No secrets are committed (
.envis gitignored; the Supabase service key never reaches the browser bundle).Testing & review
uv run --all-extras pytest); ruff check + format clean; observer-webnpm run build+ Vitest green; bothdocker compose --profile full|observer configvalidate.Notes
[auth.hook.custom_access_token]in the shared Supabase project'sconfig.toml(a separate repo) and restarting it; data was preserved.NEXT_PUBLIC_SUPABASE_URLmust be reachable from both the browser and the web container — documented indocker-compose.ymland.env.example.docs/superpowers/(gitignored by existing repo convention).