Governance architecture

What stops the assistant from binding a risk.

This page is written for the person who has to sign off on the tool, not for the person choosing it. It describes the mechanisms exactly, says where they sit in the stack, and states plainly what they do not cover.

Enforcement layer
PostgreSQL
Independent mechanisms
Three
Verified
Every test run

Why architecture rather than policy

The usual answer to AI risk in a regulated workflow is procedural: a policy document, a review step, training, an attestation. Those controls depend on everyone involved continuing to follow them, and they fail quietly — you find out a control was skipped when you go looking for why something went wrong.

Slipstream’s controls are in the database, underneath the application. They do not depend on the application being correct, on a prompt being well-written, or on a model behaving. An assistant that tried to finalize a term would have the statement refused by Postgres before any of our code had an opinion about it.

There are three mechanisms and they are independent. Each one alone would stop the failure; all three would have to be removed deliberately, in three separate places, for an assistant to commit anything.

The three mechanisms

Mechanism 01 — Insert trigger

A proposal is born pending, whatever it asked for.

Every proposed term is written through slip_proposals. A BEFORE INSERT trigger rewrites the row on its way in: status is forced to proposed, and the two columns that record a decision are blanked.

This runs for every caller, including the service role the assistants use. An assistant can construct an insert asking for a term to arrive already approved, with a decider and a timestamp attached. What lands in the table is a pending row with those fields empty.

The trigger is BEFORE INSERT rather than a check constraint deliberately: a constraint would reject the write and surface an error the caller might handle, retry, or route around. This accepts the write and corrects it.

supabase/schema.sql — insert trigger
create or replace function slip_proposals_force_pending()
returns trigger
language plpgsql
set search_path = pg_temp
as $$
begin
  new.status     := 'proposed';
  new.decided_by := null;
  new.decided_at := null;
  return new;
end;
$$;

create trigger slip_proposals_force_pending_trg
  before insert on slip_proposals
  for each row
  execute function slip_proposals_force_pending();

Mechanism 02 — Withheld privilege

Nobody holds the grant. Not the assistants, not us.

No role has UPDATE on slip_proposals. Not the assistants, not a signed-in underwriter, not an administrator. The full set of tables a signed-in session may modify in place is two: their own profile, and documents they uploaded.

This is a privilege, not a runtime check. Code cannot forget it, a migration cannot skip it, and a bug cannot route around it — without the grant, Postgres refuses the statement before it executes.

The explicit revoke matters as much as the grant. Supabase’s default privileges hand broad access to new tables in public, so “we never granted UPDATE” is not the same statement as “UPDATE is not held”. We found that gap on a live project and closed it.

supabase/schema.sql — grant layer
-- Strip Supabase's defaults from both roles first,
-- then grant back exactly what is wanted.
revoke all on slip_proposals
  from anon, authenticated;

grant select, insert on slip_proposals
  to authenticated;

-- Deliberately absent: UPDATE, DELETE.
-- The only table a session may modify in place
-- is profiles — its own display name.

Mechanism 03 — Gated function

One way out, and it checks who is asking.

Because there is no UPDATE grant, a single SECURITY DEFINER function is the only path out of pending. It refuses an unauthenticated caller outright, and it refuses anyone whose organization is not the one the term was drafted for.

That second check is what stops a counterparty approving on your behalf. A term drafted for the broking side can only be decided by someone seated on the broking side of that specific room.

The decision and its audit row are written in the same transaction, so a decided term without a trail is not a state the database can hold. A reviewer may also adjust the value before approving; that is recorded as an amendment against their name, distinct from an approval, with the original proposal preserved beside it.

decide_slip_proposal — the only exit
create or replace function decide_slip_proposal(
  p_proposal_id uuid,
  p_decision    text,
  p_value       jsonb
)
returns slip_proposals
language plpgsql
security definer
set search_path = public, pg_temp
as $$
begin
  if v_uid is null then
    raise exception 'not authenticated';
  end if;

  if v_org <> v_prop.proposer_org_id then
    raise exception
      'only a member of the proposing organization '
      'can decide this proposal';
  end if;

  -- Decision and audit row, one transaction.
end;
$$;

We cannot bypass this either. The mechanisms sit beneath the application and they refuse our own administrative credentials. There is no founder override and no support tool that quietly writes the column.

The audit trail, and what it refuses

Every state change writes one row to an append-only log. Each row carries the actor, whether that actor was a person or an assistant, the firm they acted for, and a hash chained to the row before it.

Update and delete are refused by trigger, not only by policy — which matters because the assistants write using a service role that bypasses row-level security. A trigger does not care which role you are.

A consequence worth stating, because it surprises people: a placement room cannot be deleted at all. Every room-scoped table cascades from the room, so a delete reaches the audit trail, the trigger refuses, and the whole statement fails. That is a property of the design rather than a feature of it, and it means a firm whose policy requires deletion on demand cannot use Slipstream as it stands.

audit_events — immutability
create trigger audit_events_no_update_trg
  before update on audit_events
  for each row execute function audit_events_immutable();

create trigger audit_events_no_delete_trg
  before delete on audit_events
  for each row execute function audit_events_immutable();

-- Triggers rather than policies: the service role
-- the assistants use bypasses row-level security.

Limits

What these guarantees do not cover.

A control that is described only by what it stops is not a control anybody can review. These are the edges.

They do not make the drafting correct

Nothing here stops an assistant proposing a term that is badly worded, commercially wrong, or inappropriate for the risk. It stops that proposal becoming binding without a person accepting it. Review is still review.

They are not a substitute for your own controls

Delegated authority, referral thresholds and sign-off limits live in your firm, not in this product. Slipstream records who decided what; it does not know whether that person was authorised to.

They do not cover the model provider

The mechanisms govern what reaches your data. They say nothing about how a model provider handles a prompt — that is a contractual question, answered separately. Slipstream sends submissions to Anthropic’s API, whose terms do not use prompts or completions to train models. Which provider and model read a given submission is recorded on that submission’s audit row, so the answer is in the trail rather than in a policy document.

Deletion is not available

The append-only trail cannot be edited or removed, and neither can a room. If your retention policy requires deletion on request, the honest form would be redaction — tombstoning values while leaving the chain intact — and that is a design change, not a setting.

Questions we get

From security and compliance reviewers.

Does the AI know our internal underwriting rules?
Not today. The assistants draft against general insurance language conventions and what the submission itself states. They do not hold your appetite, your referral thresholds, or your carrier-specific rules, and they should not be treated as though they do — your underwriters remain the authoritative source for all of that. On the roadmap is a private retrieval layer, where a carrier uploads its guidelines into an isolated store that the assistant can query at drafting time and that is never used to train a model. We are building that with design partners rather than ahead of them, because the useful shape of it depends on how firms actually keep those rules.
Where does our data sit?
Postgres, object storage and authentication are Supabase on AWS us-east-1. The application runs on Vercel. Live collaboration state runs through Liveblocks. Document reading and the assistants use the Anthropic API on a paid workspace, whose terms do not train on submitted data; which model read a given submission is recorded in that placement’s trail. There is no EU-resident deployment, which matters if data cannot leave the EEA — say so early and we will tell you plainly that we are not ready for that.
Can one firm see another firm’s placement?
No. Access is granted per room, never per organization. Every room-scoped table carries a policy checking membership of that specific placement, and a room you were not invited to returns nothing rather than an error — indistinguishable from one that does not exist, so a URL cannot be used to confirm a placement exists.
What happens if the application has a bug?
The three mechanisms are below the application. A bug in our code, a mistake in a prompt, or a compromised application session still cannot move a proposal out of pending without an authenticated human in the right organization, because the privilege to do so does not exist and the only function that can is gated.
How do we verify any of this ourselves?
Ask us for a walkthrough against a live database — we will run the statements in front of you and show you the refusals. The mechanisms are also asserted on every test run against a real Postgres instance, including tests that deliberately break each one to confirm the checks still fail.
Can we get our data out?
Yes, from inside the room, without asking us. Either firm placing the risk exports the placement as one self-contained file: the slip, every move on every term, every quote including superseded ones, every condition and how it was released, every document with a SHA-256 computed from the stored object at that moment, every assistant proposal with the human decision on it, and the full trail with each event’s hash. It opens in any browser with no network and no account, and it reports whether the hash chain verifies as you take it — while stating plainly that the chain is an internal integrity check, not a cryptographic guarantee against someone with control of the database. The export is itself recorded, so who took a copy and when is answerable. What we do not yet offer self-serve is a machine-readable extract for an archive system; that is the same data through a different renderer, and small.
Is it SOC 2 certified?
No. SOC 2 Type II is planned before general availability and no audit has begun. We would rather say that than imply a control environment we have not been assessed against.