For Snowflake · Cortex Agents · Snowflake Intelligence · Horizon

Every Cortex agent action,
independently judged before it executes.

Three agent actions below are shaped exactly as they leave Snowflake — a Cortex Agents custom tool, a write through a governed stored procedure, an MCP tool call — and each one goes through the live Veridect system on this page. On your side the integration is a Python function in your own schema, a stored procedure, or a URL change. Behind it is the whole control plane: consensus from frontier models across independent vendors, deterministic policy classes, risk routing, a hash-chained ledger, and an Ed25519-signed decision record you can verify right here. And on this page the judgment is also made from inside a Snowflake account — a function in a schema, allowed out through an External Access Integration — with the signed decision landing as a row in a table in that same account, live.

Open the full live tour
3 seams
a custom tool, a governed procedure, or one MCP URL — your agents, semantic views and models stay exactly as they are
1 judgment
POST /pre-action — behind every path (the MCP gateway makes that call for you); the answer is a verdict plus a signed record
1 row back
the signed decision lands in a table in your own account — ordinary Snowflake data, queryable beside what it governed, inside Horizon’s reach
0 trust required
every decision re-derives from its recorded inputs and verifies against a published key — try it below
Where it sits

Between the agent’s plan and the write.

Snowflake runs the agent: Cortex Agents plan and orchestrate across Cortex Analyst, Cortex Search and custom tools; Snowflake Intelligence puts them in front of business users; Horizon Catalog governs the data they touch with role-based access, masking and lineage. Veridect sits at the one moment none of those were built to own: the instant before an action with consequences executes — a write, a grant, a share, a task re-run — when an independent judgment — not the model that proposed the action, not the platform that hosts it — decides whether it goes, waits for a person, or never happens, and signs the record either way. Then the record lands where your governance already lives: a row in your account.

Your agent on Snowflake

Proposes an action

  • Cortex Agents calling custom tools — Python or SQL functions and stored procedures
  • Snowflake Intelligence agents acting for business users
  • Any Cortex agent reaching an external MCP server, or any external agent reaching the Snowflake-managed MCP server
Veridect · live control plane

Judges it before it runs

  • Deterministic scope check against the agent’s configured authority
  • Policy classes in code — money thresholds, protected data, regulated data, cumulative exposure
  • Multi-vendor model consensus with calibrated confidence and a dissent register
  • Risk routing: greenlight, escalate to a named human queue, or block
  • Hash-chained, Ed25519-signed record — written whether it passes or not
Outcome

Three doors, one record

greenlightthe procedure does its work or the task runs; the verdict and record id travel with it
escalateheld for a person in the queue the policy names — and the decision row is in your account’s table either way
blocknever executes — and the refusal is on the ledger too
Three thin paths — and the way back

The integration is thin. The system behind it is not.

Each path maps the call your agent was already making onto one judgment — POST /api/ai/consensus/pre-action, made by your function, your procedure, or the MCP gateway on your behalf: the verb, the target object, the fields, which agent is asking, and what that agent is authorized to do. Nothing about the agent, the model, or the semantic layer changes. The verdict decides whether the original action proceeds — and the signed decision comes back as a row beside the data it governed.

PATH 01 · CORTEX AGENTS · CUSTOM TOOL

A pre-action call inside the tool itself

Cortex Agents call custom tools that are Snowflake functions or stored procedures. Make the Veridect call the first thing the tool does: a Python function with an External Access Integration asks Veridect, and anything other than a greenlight returns the verdict and the record id instead of doing the work — the agent sees why, and so does the ledger.

~25 lines · one UDF, one network rule, one secret · no change to the agent or its model

Fail-closed by construction: the function returns {"verdict":"unavailable"} on a non-200, a timeout or a malformed body, and the procedure that calls it does its work only on a parsed greenlight. Egress is pinned by the network rule to one host; the API key lives in a Snowflake SECRET bound to the integration, never in code. This is the function Live 04 runs, from inside an account, below.

-- 1) once (ACCOUNTADMIN): egress to one host, the key as a secret, one integration binding them
CREATE NETWORK RULE governance.veridect_egress MODE = EGRESS TYPE = HOST_PORT VALUE_LIST = ('<your-veridect-host>:443');
CREATE SECRET governance.veridect_key TYPE = GENERIC_STRING SECRET_STRING = '<VERIDECT_API_KEY>';
CREATE EXTERNAL ACCESS INTEGRATION veridect_eai
  ALLOWED_NETWORK_RULES = (governance.veridect_egress) ALLOWED_AUTHENTICATION_SECRETS = (governance.veridect_key) ENABLED = TRUE;

-- 2) the judgment, as a function in your own schema
CREATE OR REPLACE FUNCTION governance.veridect_pre_action(payload VARIANT)
RETURNS VARIANT LANGUAGE PYTHON RUNTIME_VERSION = '3.11' HANDLER = 'judge'
EXTERNAL_ACCESS_INTEGRATIONS = (veridect_eai) SECRETS = ('key' = governance.veridect_key)
PACKAGES = ('requests')                                  -- carries its own CA bundle; stdlib urllib cannot verify TLS in the UDF sandbox
AS $$
import requests, _snowflake
def judge(payload):
    try:
        r = requests.post('https://<your-veridect-host>/api/ai/consensus/pre-action', json=payload, timeout=60,
                          headers={'Authorization': 'Bearer ' + _snowflake.get_generic_secret_string('key')})
        d = r.json() if r.status_code == 200 else {}
    except Exception:
        d = {}                                              # network, timeout, parse: all stay closed
    return d if d.get('verdict') else {'verdict': 'unavailable'}   # closed until proven open
$$;
PATH 02 · THE PLATFORM · GOVERNED WRITE PROCEDURE

Agents never hold DML — one procedure does the writing

Give agent roles no direct INSERT, UPDATE, GRANT or ALTER. The only way an agent changes anything is a stored procedure that asks Veridect first and runs its fixed statement only on a greenlight, with the verdict and the record id in its return value. Every agent, every tool, every agent you add next quarter is covered at the data layer; people and ordinary pipelines never wait.

~20 lines · one procedure per governed write · humans untouched

Where the authority lives: the scope string is the agent’s configured authority, kept in a small table keyed by the agent’s own Snowflake user — read with CURRENT_USER() inside the procedure, so no caller can name a more privileged agent. A read-only analyst agent that proposes to create a share is a categorical no-authority action — the deterministic layer blocks it before any model is asked to weigh in. Watch it below. The policy classes work over field names; send values only for the fields your policy needs. The same shape fits the Snowflake-managed MCP server: keep its query tool read-only and route every write through a procedure like this one.

-- the agent's role can CALL this procedure; it cannot UPDATE the table itself
CREATE OR REPLACE PROCEDURE governance.update_customer_contact(customer_id STRING, changes VARIANT)
RETURNS VARIANT LANGUAGE SQL EXECUTE AS OWNER
AS $$
DECLARE agent STRING; scope STRING; req VARIANT; d VARIANT;
BEGIN
  agent := CURRENT_USER();                                                 -- the session's user: the agent's own service user, not a parameter it can set
  scope := (SELECT scope FROM governance.agent_scopes WHERE agent_user = :agent);   -- its configured authority, in words
  IF (scope IS NULL) THEN RETURN OBJECT_CONSTRUCT('written', FALSE, 'verdict', 'unknown_agent'); END IF;   -- no configured authority: never asked, never run
  req := OBJECT_CONSTRUCT(
         'action', 'update_customer_record', 'target', 'snowflake:crm.customer_master',
         'parameters', OBJECT_INSERT(:changes, 'customer_id', :customer_id),
         'callingAgent', :agent, 'callingAgentScope', :scope, 'department', 'data_platform');
  d := governance.veridect_pre_action(:req);
  IF (GET(d, 'verdict')::STRING IS DISTINCT FROM 'greenlight') THEN             -- "unavailable" and NULL are not a greenlight
    RETURN OBJECT_CONSTRUCT('written', FALSE, 'verdict', GET(d, 'verdict'), 'record_id', GET(d, 'auditBundleId'));
  END IF;
  UPDATE crm.customer_master                                               -- a fixed statement; nothing is interpolated
     SET email_address = COALESCE(GET(:changes, 'email_address')::STRING, email_address),
         phone_number  = COALESCE(GET(:changes, 'phone_number')::STRING,  phone_number)
   WHERE customer_id = :customer_id;
  RETURN OBJECT_CONSTRUCT('written', TRUE, 'verdict', 'greenlight', 'record_id', GET(d, 'auditBundleId'));
END;
$$;
PATH 03 · MCP · BOTH DIRECTIONS

One MCP URL, whichever way the call flows

Cortex Agents can reach external tool servers over MCP, and external agents can reach Snowflake through the Snowflake-managed MCP server. Either way, register the real server as a Veridect upstream once and point the client at Veridect instead. Every tools/call is judged in flight; tools/list, initialize and everything else pass straight through. A refused call is never forwarded.

0 lines of agent code · one registration, one URL

Identity on the wire: the agent’s identity, workflow id and business context ride in params._meta["veridect/…"] — unsigned, that is the caller’s own claim, and the decision record marks it as such; a tenant can require a signed Ed25519 identity envelope on every call, and an unsigned call is then refused inside the protocol. Both outcomes run live below.

# 1) once — register the real server as an upstream (admin call)
POST /api/ai/consensus/mcp/upstreams/{tenant}
{ "upstreamId": "cortex-tools-prod", "url": "https://<your-tool-server>/mcp", "enabled": true }

# 2a) a Cortex agent reaching an external tool server — the MCP connector URL
before:  https://<your-tool-server>/mcp
after:   https://<your-veridect-host>/api/mcp/gateway/cortex-tools-prod

# 2b) an external agent reaching Snowflake — the Snowflake-managed MCP server
before:  https://<account>.snowflakecomputing.com/api/v2/databases/<db>/schemas/<schema>/mcp-servers/<server>
after:   https://<your-veridect-host>/api/mcp/gateway/snowflake-mcp-prod
HEADERS = {
  "Authorization":            "Bearer <VERIDECT_API_KEY>",       # your Veridect tenant
  "X-Upstream-Authorization": "Bearer <Snowflake PAT or OAuth token>",   # forwarded to Snowflake as Authorization
}

# 3) what the agent sees on a greenlight: the upstream's own result, plus
#    result._meta["veridect/verdict"] and result._meta["veridect/bundleId"]
#    on escalate / block: a JSON-RPC error (-32003 / -32004) carrying the
#    verdict and the record id — and the server never receives the call.
THE WAY BACK · A ROW IN YOUR ACCOUNT

The decision lands beside the data it governed

Every verdict returns a signed decision record. Write it into a DECISIONS table in your own schema — bundle id, verdict, confidence, policy classes, bundle hash, signature key id, request and response as VARIANT. From then on it is ordinary Snowflake data: queryable, maskable, lineage-tracked under Horizon, shareable with your auditor. Nothing on the Snowflake side is installed.

1 INSERT · one table · VARIANT columns

Tamper-evident on arrival: the row carries the bundle hash and the Ed25519 key id; the public verify endpoint — or your own offline check against the published key — re-derives the verdict from the recorded inputs at any later date. Demonstrated end-to-end inside a live Snowflake account below — demonstrated wiring, not a Native App, a Marketplace listing or an endorsed integration.

CREATE TABLE IF NOT EXISTS governance.decisions (
  recorded_at      TIMESTAMP_LTZ DEFAULT CURRENT_TIMESTAMP(),
  bundle_id        STRING,  verdict STRING,  confidence NUMBER(6,2),
  policy_classes   VARIANT, escalation_route STRING,
  bundle_hash      STRING,  signature_alg STRING,  key_id STRING,
  request          VARIANT, response VARIANT );

-- inside update_customer_contact, right after d := governance.veridect_pre_action(:req) — written whether it passed or not
INSERT INTO governance.decisions
  (bundle_id, verdict, confidence, policy_classes, escalation_route, bundle_hash, signature_alg, key_id, request, response)
SELECT GET(d, 'auditBundleId'), GET(d, 'verdict'), GET(d, 'confidence'),
       GET(GET(d, 'auditBundle'), 'policyTriggers'), GET(d, 'escalationRoute'),
       GET(GET(d, 'auditBundle'), 'bundleHash'),
       GET(GET(GET(d, 'auditBundle'), 'attestation'), 'alg'),
       GET(GET(GET(d, 'auditBundle'), 'attestation'), 'keyId'),
       :req, :d;

-- later, for the auditor: every escalated action that touched protected fields this quarter
SELECT recorded_at, bundle_id, escalation_route, request:action::STRING
FROM governance.decisions
WHERE verdict = 'escalate' AND ARRAY_CONTAINS('protected_personal_data_modification'::VARIANT, policy_classes);
Run it live

Five Snowflake-shaped actions, through the live system, now.

Each card sends the exact request the custom tool, the governed procedure, or the MCP connection would send, to the live Veridect system on a shared sandbox tenant. The verdicts are real multi-model decisions made while you wait; the records are written to the ledger and signed; the Verify button re-derives the verdict from the recorded inputs and checks the signature against the published key. Live 04 then makes the Live 03 judgment again — this time from inside a Snowflake account — and records it as a row there. Expected outcomes are stated in advance — the engine decides.

Ready. Each decision takes a few seconds while the model roster deliberates.
LIVE 01 · PATH 01 · CORTEX AGENTS CUSTOM TOOL

Re-run a failed revenue roll-up task

A Cortex pipeline-operations agent calls its rerun_task tool on ANALYTICS.FINANCE.DAILY_REVENUE_ROLLUP after a transient failure. Its configured authority: “may read pipeline state and may re-run failed scheduled tasks in the ANALYTICS database.”

Expected: greenlight — inside authority, no policy class fires, the roster agrees. The procedure runs EXECUTE TASK and returns the record id with its result.

Waiting. Request shape: the custom tool’s payload, verbatim.
LIVE 02 · PATH 02 · GOVERNED WRITE PROCEDURE

A read-only analyst tries to share a customer table

A Snowflake Intelligence analyst agent proposes create_share: a new share of SALES.CRM.CONTACTS (SELECT) to an external partner account. Its configured authority: “read-only: may query semantic views and Cortex Search services to answer questions.”

Expected: block — a read-only agent proposing to create a share is a categorical no-authority action; the deterministic scope check decides, the models are consulted for the record, and the procedure returns the verdict instead of creating the share.

Waiting. Request shape: the governed procedure’s payload, mapped from its arguments.
LIVE 03 · PATH 01 · CORTEX AGENTS CUSTOM TOOL

Correct contact fields on a customer master record

A customer-data steward agent, inside its authority (“may look up customer master records and may update contact fields on customer master records”), calls update_customer_record with new email_address, phone_number and home_address values after a dedupe pass.

Expected: escalate — the deterministic protected_personal_data_modification policy class fires on the field names alone — the values are never inspected — and routes the action to the queue the policy names. Authorized but consequential actions escalate to a person; they are never silently blocked and never silently allowed. Live 04 makes this same judgment from inside a Snowflake account.

Waiting. Request shape: the custom tool’s payload, verbatim.
LIVE 04 · THE WAY BACK · INSIDE A SNOWFLAKE ACCOUNT

The same judgment, made from inside a Snowflake account — and recorded there

The Live 03 request is handed to a Python function in the GOVERNANCE schema of a live Snowflake account, through the Snowflake SQL API. The function calls the live Veridect system through an External Access Integration; the signed decision is then written as a row in that account’s VERIDECT.GOVERNANCE.DECISIONS table. What you see is Snowflake’s own reply: the user and role the statements ran as, the query ids, and the row count after the insert.

Expected: one new row — the same verdict and policy class as Live 03, plus the query id that judged it and the query id that recorded it. A suspended warehouse resumes first, so the first run of the day can take a minute; that latency is the warehouse’s, not the engine’s. If Snowflake does not answer, the card says so — nothing is simulated.

Waiting. This card runs the Live 03 request from inside the account; Live 03 itself need not run first.
LIVE 05 · PATH 03 · MCP

The same tools/call, two tenants, two outcomes

A JSON-RPC tools/call for lookup_policy_limit goes through the live Veridect MCP gateway to a sample tool server that counts what it actually receives. First on the shared sandbox tenant; then on a tenant that requires a signed identity envelope, sent without one.

Expected: first call forwarded, the tool’s own hit counter goes up by one and the result carries _meta["veridect/bundleId"]; second call refused inside the protocol (-32004 identity_not_presented), counter unchanged, refusal on the ledger.

Waiting. Request shape: JSON-RPC 2.0, verbatim.

What on this page is live, and what is illustrative

Live: every verdict, confidence, dissent register, policy class, escalation route, ledger write, hash chain, Ed25519 signature, MCP forwarding decision and verification result above comes from the production Veridect system on a shared sandbox tenant — the same endpoints an integrator calls. The consensus roster is four frontier providers from independent vendors with a decisive-verdict early stop, so a decision on this surface typically records three; the models are named in each record’s dissent register. The protected-data and money thresholds are this sandbox tenant’s policy; in your tenant every threshold is yours.

Live, in a Snowflake account Veridect controls: the function, the table and the row in Live 04 exist in a small Snowflake account Veridect pays for. The statements run through the Snowflake SQL API as a SERVICE user with a programmatic access token whose role can reach only the VERIDECT database; the user, role, warehouse, query ids and row count shown are Snowflake’s reply, and the function’s egress is limited by a network rule to the live system’s host. Only fixed, server-held requests ever enter that account — nothing typed by a visitor reaches a SQL statement. In your deployment the account, the schema and the credentials are yours; the integration is demonstrated wiring, not a Snowflake Native App, a Marketplace listing or an endorsed integration. A daily allowance keeps visitors from filling the table; when it is used up the card says so.

Illustrative: the agent side. No Cortex agent, Snowflake Intelligence session or stored procedure executes from this page; each card sends the exact request the custom tool, the governed procedure or the MCP connection would send, so what you watch is the decision those integrations would receive. The MCP calls in Live 05 leave this browser as a standard MCP client; a Cortex MCP connector and the Snowflake-managed MCP server were not exercised here. The code above is written against documented Snowflake features — Python UDFs with External Access Integrations and secrets, Snowflake Scripting procedures with EXECUTE AS OWNER, the managed MCP server endpoint — and the tool-server URL is a placeholder for your own.

Verification proves that the enforcement decision re-derives from the recorded inputs and that the record is unmodified and signed by the published key. It does not re-run the models: the consensus summary is attested through the recorded dissent register and verifier-text hash, and disclosed as such in the proof itself.

Snowflake, the Snowflake logo, Cortex, Snowflake Intelligence, Horizon and other Snowflake marks are trademarks or registered trademarks of Snowflake Inc. in the United States and other countries. Veridect is an independent company and is not affiliated with or endorsed by Snowflake.

Five questions, answered for a Snowflake team

The short version.

How does this help?

It lets Cortex agents into the actions that carry consequences — writes, grants, shares, task re-runs, customer and patient records — because an independent judgment stands in front of each action and a signed record stands behind it. The model that proposes the action is never the one that approves it, and the record lands as a row beside the data it governed.

How do I integrate it?

One of the three paths above: a function at the top of the tool, a governed write procedure, or one MCP URL. Your agents, semantic views and models stay as they are. The way back is one INSERT into a table in your schema. It runs where you say, on your keys.

How long does it take?

The system is live today. A private sandbox on your own policy thresholds in a day once you say go; production typically about two weeks, with the pace set mostly on your side — security review, keys, access. Ours is ready on day one.

How does this make money?

Three levers, none of them invented here: revenue held back because agents could not be trusted with the consequential step; cost stopped on human review of actions a signed record now settles; losses not taken on the actions that should never have executed. Your own number comes out of the sandbox.

Why you and not a big well-known company?

Because the judgment has to be independent of the model and of the platform to be worth anything to an auditor. Horizon governs what data an agent may reach; a platform grading its own agents’ actions is self-governance. Veridect is a separate control plane with its own ledger and keys, validated by an external red team on scope-violation semantics, and verifiable offline. Try to forge a record on the live tour; the engine refuses it.

Test-drive it yourself.

Run the five actions above, verify the records, note the query ids, then take the full live tour — identity envelopes, delegation chains, cross-agent exposure, the fleet layer, and the forgery the engine refuses. Seven minutes, open access.

Prefer to see it on your own scenarios? Paste one on the pre-action console — or write to lrussell@veridect.ai and a scoped production sandbox on your own thresholds and your own account follows in a day.

The same control plane, built the same way for Microsoft, AWS, ServiceNow, CrowdStrike, Rubrik, Salesforce, Databricks, Okta and Google Cloud — one engine behind all ten pages, each run live. All ten platforms →