Ask Brian (engineering desk)
Ask SABLE is the product's own chat for its users. Ask Brian is this separate, admin-only engineering desk, named for its persona (SABLE_ASK_PERSONA_NAME).
web/ask.html, backed by /api/ask-engineering. A separate, narrower chat surface than
Ask SABLE — this one answers questions about SABLE's own
documentation, code, and database schema, for engineers debugging or extending the app, not
about a client's projects.
The gate: admin AND an explicit allowlist
Reaching this screen at all requires both conditions: the viewer's role must be admin, and
their identity must appear in SABLE_ASK_ALLOWLIST. Unset, that allowlist means nobody — there is
no fallback to "any admin." whoami's canAskEngineering field decides server-side whether the
nav link (data-nav-access="ask") even shows.
Three layers protect the database access, so no single mistake can write or over-read
- A
NOLOGINPostgres role (sable_engineering_reader) grantedSELECTon the tables an engineer may see — and explicitly nothing on secrets, the env control plane, private mail and chat, or feedback attachments. New tables are unreadable by this role until a migration grants them, deliberately with noalter default privileges— a future secret-shaped table is safe by default rather than readable until someone remembers to deny it. - A permissive
SELECTRLS policy for that role on every RLS-protected table it may see — necessary because the role isn't the table owner, and the normal org-scoped policies needauth.uid(), which a service-role call doesn't carry. - One
SECURITY DEFINERfunction that switches to the reader role, pins a 5-second statement timeout, marks the transaction read-only, wraps the caller'sSELECTin a row cap, and returns JSON. Onlyservice_rolemay call it — the web route checks the caller's role and allowlist membership before it ever reaches this function.
Nothing here grants anything to anon or authenticated — the entire path is service-role only,
gated at the web layer first.
What it can do besides query the database
Read-only SQL through ask_engineering_query, connector and Trigger run status, and a search
over the documentation/code corpus (searchDocs, searchCode, readFile, readDoc) — the
model has tools, but every one of them reads, none writes.
No persisted memory, on purpose
Unlike /api/chat-stream, nothing here lands in chat_sessions/chat_messages. The page itself
keeps the conversation in memory for the session; closing the tab loses it. This is a deliberate
difference from the client-facing chat surfaces, which persist multi-turn history.
Where the code lives
sable-agents-demo/web/ask.htmlsable-agents-demo/web/api/ask-engineering.js,_ask_engineering.jssable-agents-demo/web/api/_ask_corpus.generated.jssable-agents-demo/supabase/migrations/20260909220000_ask_engineering_readonly.sqlsable-agents-demo/scripts/ask/bundle-corpus.mjs,scripts/ask/rollback-ask-engineering-readonly.sqlsable-agents-demo/docs/superpowers/specs/2026-09-09-ask-brian-engineering-desk.md