A database writeback publishes a node’s output table into an external Postgres-compatible database you control — an application database, a Supabase project, a warehouse landing schema. It is the governed alternative to writing df.to_sql(...) in a Python node: deliveries are authorized per invocation, frozen against the exact data version you approved, committed in a single destination transaction, safe to retry, and receipted.

When to use what

Native delivery is the default bulk path. Python writebacks are the extensibility escape hatch, not a replacement for it.

The contract

A writeback node declares everything up front, and the platform enforces it:
  • secret_key references a stored tenant secret whose value is the postgres:// connection string. Credentials are resolved inside the worker at delivery time — they never appear in the definition, in receipts, or in error messages.
  • Exactly one table input. The writeback delivers one node’s output; shape it upstream.
  • mode — how rows land (below). upsert requires merge_keys; append and replace forbid them.
  • schema_policystrict fails the delivery on any column drift; additive adds new upstream columns to the destination (inside the same transaction) and tolerates destination-only columns.

Delivery modes

append inserts every delivered row. upsert updates destination rows matching the declared merge_keys and inserts the rest — deterministic: a staged batch with NULL or duplicate merge keys fails whole, never partially. replace makes the delivered input the complete destination state. Replace is destructive by nature, so it is explicitly guarded: a replace-mode writeback must declare requires_permission — a server-enforced permission checked against whoever invokes it (and re-checked on retries). You cannot author an ungated replace. All three modes commit in one destination transaction — including first-delivery table creation and additive column adds. A failure of any kind rolls the destination back to exactly its prior state. The table object is never dropped or swapped: your indexes, constraints, defaults, triggers, and RLS policies survive every delivery, including replace.

Frozen inputs and safe retries

At invocation time the delivery freezes an execution envelope: the exact source artifact (pinned to its DuckLake snapshot version), the definition, and the invoking principal. Retries redeliver that — never a newer version of the data, never a re-resolved definition. On the destination side an idempotency ledger (_panels_staging._panels_writeback_ledger) commits atomically with your rows, so a retry after an uncertain outcome reports the recorded result instead of applying a duplicate effect.

Receipts

Every terminal delivery produces a receipt visible in run history and via the core_list_writeback_outcomes MCP tool: mode, destination (schema.table — never connection details), rows attempted / inserted / updated / deleted / committed, columns added by schema evolution, the frozen source version, and — on failure — a structured category (connection, permission, constraint, schema, data, config, source) with a sanitized message. The full per-attempt audit trail is sealed in an effect-log artifact linked from the receipt.

Invoking a delivery

Writebacks are invoked, never run: they don’t participate in compute cascades, and Run affordances deliberately reject them. Invocation paths:
  • a threshold trigger on the upstream node (e.g. artifact.row_count > 0) firing the writeback as a service principal,
  • an action chain step,
  • an agent calling the core_invoke_writeback MCP tool.

Supabase specifics

Supabase is Postgres — ordinary table publishing needs nothing Supabase-specific. Two practical notes:
  • Use the direct connection string (or a session-mode pooler) as the secret value. Transaction-mode poolers (port 6543 by default) break single-transaction semantics.
  • Deliveries connect as the database role in your connection string and are not subject to your anon-key RLS policies. Keep RLS enabled for your app’s client-side access; the writeback writes through the direct role.