Shared Primitives — Blast-Radius Reference¶
Version: 1.0 Status: Pre-launch reference Last Updated: April 2026 Owner: Protocol Raw Operations
Why this document exists¶
A small number of database functions sit at the centre of Protocol Raw's automation fabric. Every scheduled monitor, every deferred HTTP call from a trigger, every Edge Function invocation originating inside Postgres passes through one of them. A single regression in any of these primitives silences or corrupts a wide fan-out of callers at once.
The 2026-03-23 fn_invoke_edge_function outage is the reference incident: a Vault secret name drifted, one primitive began returning NULL, and every dependent cron path stopped firing silently. Nothing alerted because the alerting path itself depended on the same primitive.
This document is the index of what those primitives are, who calls them, and what must be checked before they change.
Hibernation-state note. Pre-launch, only 1 of 21
fn_invoke_edge_functioncron callers is active. The wake-up script activates the other 20 simultaneously. Any blast-radius review run during hibernation must account for the latent caller set, not just the active one. The live views below return both active and inactive callers; theactivecolumn is the gate, not a filter.
The shared primitive set¶
Three categories, derived live from pg_proc by raw_ops.v_shared_primitive_caller_map:
| Category | Count | What it is |
|---|---|---|
| Named primitives | 3 | fn_invoke_edge_function, fn_invoke_monitor, fn_canary_system_health_v1 |
| Other direct Vault readers | 14 | raw_ops.* functions that read vault.decrypted_secrets directly instead of delegating to fn_invoke_edge_function |
| Monitor checks | 29 | fn_check_*_v2 functions; 28 routed through run-monitor + 1 known orphan |
All three named primitives read vault.decrypted_secrets directly. The "other direct Vault readers" are architectural siblings rather than callers — they re-implement the same Vault-read pattern rather than going through fn_invoke_edge_function. The view surfaces all 16 (14 others + 2 named primitives) under relationship = 'vault_pattern_duplicator' because each is a fan-out point for a Vault key rename.
There are no inter-primitive calls: fn_invoke_monitor and fn_canary_system_health_v1 duplicate fn_invoke_edge_function's Vault-read pattern rather than delegating to it, by design. This keeps the meta-monitors on a disjoint failure path from the primary monitoring fabric — see the 2026-04-14 Slack-silence incident for why that matters.
Named primitives¶
raw_ops.fn_invoke_edge_function(p_function_name, p_payload)¶
Purpose. Generic invoker for any Supabase Edge Function from inside Postgres. Reads service_role_key and anon_key from Vault, constructs the pg_net HTTP POST, returns the request id.
Callers. 23 direct callers — 2 function bodies, 21 pg_cron jobs (1 active, 20 hibernated). 16 additional functions duplicate its Vault-read pattern.
Change constraints.
- Signature is load-bearing across every caller. Adding a parameter requires simultaneous update to every caller.
- Vault key names (service_role_key, anon_key) appear as string literals in the body. A rename must fan out to every duplicator (see Vault-key rename checklist below).
- The function is the most-called primitive in the system. Latency regressions here amplify across every cron wake-up.
raw_ops.fn_invoke_monitor(p_check_name)¶
Purpose. Specialised invoker for run-monitor. Reads Vault, constructs the pg_net POST to /functions/v1/run-monitor with {"check": p_check_name}, logs a monitoring_runs heartbeat.
Callers. 30 pg_cron jobs (2 active pre-launch: monitor-acquisition-capture, monitor-daily-snapshot; 28 hibernated).
Change constraints.
- Payload shape {"check": ...} is mirrored in supabase/functions/run-monitor/index.ts. Any rename of the check key must land in both places in the same deploy window.
- Does not call fn_invoke_edge_function. Duplicates the Vault-read pattern deliberately so a regression in fn_invoke_edge_function does not take out the monitoring fabric.
raw_ops.fn_canary_system_health_v1()¶
Purpose. Meta-monitor. Checks that the monitoring fabric itself is alive by posting to run-monitor via an independent Vault read and Slack channel (slack_canary_webhook).
Callers. 1 pg_cron job (canary-system-health).
Change constraints.
- Must not share a failure mode with fn_invoke_monitor or the monitor-check set. Duplicating the Vault read and using a disjoint Slack webhook is the enforcement mechanism.
- If any of the 29 fn_check functions starts firing through the canary for convenience, the canary's meta-monitor guarantee is lost.
Vault-key rename checklist¶
Renaming or rotating any vault.decrypted_secrets key requires updating every reader listed by raw_ops.v_vault_key_readers. Reader counts (authoritative, from the live view):
| Vault key | Readers | Notes |
|---|---|---|
service_role_key |
9 | fn_invoke_edge_function is one; 8 others read it directly |
anon_key |
5 | includes fn_invoke_edge_function, fn_invoke_monitor, fn_canary_system_health_v1, notify_courier_exception, plus one more |
slack_canary_webhook |
1 | fn_canary_system_health_v1 only |
transcript_signing_key |
2 | fn_generate_transcript_token_v1, fn_validate_transcript_token_v1 |
sync_internal_secret |
1 | fn_sync_acquisition_source_to_cio |
17 distinct reader functions overall (a function reading two keys appears twice in the table, once per key).
16 vault_pattern_duplicator rows in the caller map — this is the union of all direct Vault readers except fn_invoke_edge_function itself (the 2 named sibling primitives + 14 other direct readers). All 16 are fan-out points for any Vault key rename that touches their key.
Before a rename:
SELECT * FROM raw_ops.v_vault_key_readers WHERE vault_key_name = '<old_name>';- For each reader, grep the function body to confirm the key literal appears only where expected (the view's matching is substring-based on the quoted key name, which is tight but not perfect).
- Stage the rename: add the new key to Vault alongside the old, update each reader to read the new key, confirm behaviour on a canary path, remove the old key.
- Re-run the view — every reader should now point at the new key.
Monitor checks — fn_check_*_v2¶
Every raw_ops.fn_check_*_v2 is a primitive in the sense that the run-monitor Edge Function's MONITOR_FUNCTIONS map treats them as a closed set: adding a new monitor requires simultaneous edits to the SQL function, the map, and a pg_cron schedule (see the monitor template in CLAUDE.md).
28 of 29 are routed through run-monitor. The 29th — fn_check_support_queue_v2 — has zero callers and zero MONITOR_FUNCTIONS entry; it is tracked as a pre-launch follow-up (decide whether to drop or wire in).
Change constraints for the fn_check set.
- Return shape is a JSONB object with success, run_id, check_name, duration_ms, and an optional alert_config sub-object. The run-monitor → ops-alerter chain parses these keys by name.
- The check_name string in the return value is also the key in MONITOR_FUNCTIONS. Renaming one requires renaming the other in the same deploy.
- Each must INSERT and UPDATE raw_ops.monitoring_runs so the monitoring heartbeat stays accurate. Skipping this makes the monitoring_heartbeat check believe the system is silent.
When to consult this document¶
- Before renaming a Vault key.
- Before changing the signature of
fn_invoke_edge_function,fn_invoke_monitor, orfn_canary_system_health_v1. - Before adding, renaming, or removing an
fn_check_*_v2function. - Before modifying
supabase/functions/run-monitor/index.ts— specifically theMONITOR_FUNCTIONSmap. - Before enabling the hibernated cron fleet at launch — confirm the latent caller set against the view.
- During any incident where a cron-driven path has gone quiet and the silence is not obviously scoped to one endpoint.
Pre-deploy checklist for primitive changes¶
Run through before any change to a function in the shared primitive set:
- Identify the blast radius. Query the caller map and Vault-key reader view (below). Enumerate every caller and every duplicator.
- Verify hibernation state. If the change is safe only for active callers, confirm that hibernated callers will also accept it when activated. A function rename that works for 2 active cron jobs but breaks 28 hibernated ones is a latent launch-day outage.
- Plan the deploy order. Primitive-change-then-caller-change is safer than the reverse if the primitive is forgiving of old callers. For signature-breaking changes, deploy in a single transaction or a scripted sequence — not across two sessions.
- Add ANALYZE +
NOTIFY pgrst 'reload schema'ifpublic-schema functions are touched. See "Migration Checklist (DDL Hygiene)" inCLAUDE.md. - Audit boundaries. Apply the boundary-audit rule from
CLAUDE.md— every CHECK constraint, enum, allow-list, API validator, and downstream consumer that could reject the change. - Log to
ops_events. The primitive's call site should emit INFO on work and WARNING on failure so a post-deploy tail ofops_eventscan confirm the change landed without silent regression.
Live source of truth¶
Both views are SECURITY INVOKER — no privileges required beyond read access to raw_ops.
Query the caller map:
SELECT *
FROM raw_ops.v_shared_primitive_caller_map
ORDER BY primitive_name, relationship, caller_type, caller_name;
Query the Vault-key fan-out:
View definitions live in the database (CREATE OR REPLACE VIEW raw_ops.v_shared_primitive_caller_map …) and are self-describing via COMMENT ON VIEW. They derive the primitive set from pg_proc scans, so new Vault readers and new fn_check_*_v2 functions surface automatically. Two facts are encoded statically because they are not visible in pg_catalog: the MONITOR_FUNCTIONS map (which lives in the run-monitor Edge Function source) and the known-orphan list (currently fn_check_support_queue_v2). Both are small, single-edit points inside the view definitions.
Related references¶
- SOP-MON-01 — monitoring and alerting architecture. The canonical pattern for any new
fn_check_*_v2. CLAUDE.md— "Migration Checklist (DDL Hygiene)" and "Boundary audits" sections. Always applicable before primitive changes.TODO-followups.md— open follow-ups including thefn_check_support_queue_v2orphan and the 2026-04-19 GUC sweep close-out.
Metabase¶
Metabase dashboard wrappers for both views are deferred until capital lands. View DDL is stable — both views can be surfaced as Metabase questions at that point with no changes needed. Until then, the SQL queries above are the reference; anyone with Supabase Management API access or a psql session to raw_ops can run them directly.