ADR-0044: Supabase as a module component, Alpha Vantage as a manifest¶
Date: 2026-10-12. Status: accepted. Builds on ADR-0029 (manifests), ADR-0031 (module permissions) and ADR-0035 (module retry).
Context¶
KAN-1861 adds two first-party components. The card described Supabase as a pure-HTTP manifest and Alpha
Vantage as a preset over http_request v2. Writing the Supabase manifest showed that the rest interpreter
cannot express four things PostgREST needs:
- a request body that is a JSON array (insert and upsert send
[{...}, {...}]; abody.jsontemplate is an object or an array of templates, never one input value); - a query string whose parameter names come from the input (a filter is
status=eq.open, the column is the name), wherequeryhas fixed names; - reading the body of a non-2xx answer (PostgREST puts
code,message,detailsandhintthere; the interpreter reports the status line only); - a project URL taken from a secret slot (the interpreter's configurable host comes from
config, which akind: modulecannot read).
Decision¶
kampong/supabaseis akind: module, still pure HTTP: it only callsctx.fetch. Extending the interpreter for one connector would put PostgREST-shaped features (array roots, dynamic query names, error bodies) into a contract every manifest shares. GitHub (KAN-1872) made the same call for the same reason.- Two slots, as the card asks:
url(SUPABASE_URL, the project URL) andkey(SUPABASE_KEY), both bound to*.supabase.co. The module accepts onlyhttps://<ref>.supabase.cowith no port, so a wrong value cannot send the key to another host even inside the wildcard. The wildcard is wider than one project; that is the price of reading the URL from a slot, and the module's own check closes it. The key is sent as theapikeyheader only: the newer publishable and secret keys are not JWTs, so a Bearer header would be refused. - Preferences:
return=minimalby default (returning: representationto get rows), and upsert addsresolution=merge-duplicates(orignore-duplicates) withon_conflictin the query string. - Error mapping: the body's code, message, details and hint are reported with the status, plus a short
piece of advice for the codes people meet most (unique violation, row-level security, missing table or
function). Retry follows ADR-0035: a 429 for any op, 408 and 5xx for reads only,
Retry-Afterhonoured. A write is never repeated after a 5xx, since it may have been applied. - The probe is
GET /auth/v1/settings, not a PostgREST route: it answers 401 for a bad key whatever the key's role and whatever the tables' policies are, whereas the REST root is refused to non-service keys. kampong/alpha-vantagestays akind: restmanifest (the thin preset the card describes):failure_whenrules turn the 200-OK bodies (Information,Note,Error Message, and any body withoutTime Series (Daily)) into failures, withpace: { rps: 1 }and a retry policy. Only the burst-limitInformationis marked retryable; a spent daily quota, a bad symbol or a bad key will not be fixed by trying again, and each retry would spend more of a small quota. Its probe is a real daily-prices call forIBM(Alpha Vantage has no free account endpoint), and onlyapikey is invalid/invalid or missingcount as a refused key.
Consequences¶
- The canvas gets a full form for Alpha Vantage. For the Supabase table ops, the free-form
rows,filterandargsproperties have no field kind, so those ops use the existing YAMLwitheditor (the documented fallback);check_keyneeds no input. Teaching the form planner free-form objects is a separate change. - Alpha Vantage's response keys contain spaces, which an output schema cannot name, so its output is an untyped object.
- A change to either component changes its digest: run
npm run generate:registry-indexinpackages/engine.