Airlock Stored Procedure Reference
Generated from the exact upstream stored procedure Markdown contract used by agents and bundled Airlock documentation.
After install or upgrade, fetch the bundled contract with CALL airlock.agent.documentation();. Use CONTENT_MODE => 'PROCEDURES' for structured procedure discovery.
For agent tooling, start with the public Airlock MCP repo and the local Airlock MCP guide. Use Airlock Tools for AI Agents for deeper procedure, records JSON, tool-schema, and delegation guidance. Use OKF Knowledge Bundles when working with governed Markdown bundle metadata.
This document defines the v1 contract for the airlock Snowflake stored procedures, covering both:
airlock.admin.*– admin‑only control plane, maintenance, and billing-emission APIsairlock.observe.*– read-only governance and billing observability APIsairlock.agent.*– agent data access procedures
The goal is to provide a stable, predictable surface for app integrators, BI users, and tooling (e.g. Renaissance CLI).
Examples assume the installed app object is named airlock. If an account uses a different app name, substitute that name. For example, CALL airlock.agent.describe_spec(...) becomes CALL MY_AIRLOCK_APP.AGENT.DESCRIBE_SPEC(...) when the installed app object is named MY_AIRLOCK_APP; CALL airlock.agent.list_my_roles() becomes CALL MY_AIRLOCK_APP.AGENT.LIST_MY_ROLES().
Business agents should normally run with the app's app_agent application role and call AGENT.* procedures. Reserve app_admin and ADMIN.* procedures for authorized administrators and setup automation. A demo agent such as csmith may have an Airlock role named agent; that does not imply Snowflake app_admin.
If an agent gets Unknown user-defined function AIRLOCK.ADMIN... or AIRLOCK.AGENT..., first resolve the installed application object name; do not guess new procedure names. Use the app name the human provided, inspect the connection's existing worksheets/scripts, or run an account discovery query such as SHOW APPLICATIONS LIKE 'AIRLOCK%'. Once the app object is known, start agent discovery in the agent schema:
CALL <APP_NAME>.AGENT.LIST_MY_ROLES();
CALL <APP_NAME>.AGENT.LIST_MY_SPECS('<airlock_role>', TRUE);
CALL <APP_NAME>.AGENT.DESCRIBE_SPEC('<spec_name>', '<airlock_role>', TRUE);
Use OBSERVE.SPEC_CONFIG or OBSERVE.SPEC when the active Snowflake role has the app's app_observer or app_admin application role and the task is read-only governance observation rather than agent discovery. app_observer opens the read-only procedure family; Airlock assignments and each spec's observer_access rules determine the caller's actual spec scope.
Every mutating human or automated agent should connect as an attributable Snowflake principal. Direct work uses that principal's Airlock assignments; work for another principal uses explicit Airlock delegation. Snowflake user type, workspace roles, channel membership, reactions, and chat approval do not create Airlock authority. A channel may use a dedicated scoped observer principal, but that principal must not be reused for mutation. See Principal And Workspace Interoperability for the authority matrix, polling contract, and portable commitment receipt. See External Agent Provenance for the bounded workspace envelope, query-tag convention, and two-principal Native MCP proof.
#Guidance for AI agents and automation
This block is for agents and integrators calling procedures from SQL (Snowflake CLI, Snowsight worksheets, drivers, CI, and MCP tools). The same text is bundled with the Native App and returned in-SQL:
-- Default: TABLE(PAYLOAD VARIANT) — one row per heading (TOC); each PAYLOAD is structured JSON.
CALL airlock.agent.documentation();
-- Full bundled markdown in one row: PAYLOAD.kind = 'full', PAYLOAD.body (UTF-8).
CALL airlock.agent.documentation(CONTENT_MODE => 'FULL');
-- Procedure registry: one structured PAYLOAD row per stored-procedure signature.
CALL airlock.agent.documentation(CONTENT_MODE => 'PROCEDURES');
-- Slice by numbered section ids (matches ### N. and #### N.M in the bundled doc).
CALL airlock.agent.documentation('markdown', 'api_v1', 'SECTION', '4.8');
-- Chunk fallback for clients that truncate very large FULL result rows.
CALL airlock.agent.documentation('markdown', 'api_v1', 'CHUNK', '1-3');
Parameters are (output_format, doc_key, content_mode, section_ids) — all optional with defaults. Default content_mode is TOC: the first PAYLOAD is kind: documentation_manifest with content_hash / etag and agent_instructions, followed by heading rows where PAYLOAD is a small object (e.g. kind: toc_entry, level, section_id, title). Every heading has a section_id: numbered headings use ids such as 4.8, while unnumbered guidance headings use stable slug ids. Use CONTENT_MODE => 'PROCEDURES' for a compact structured procedure registry: first row is kind: procedure_index_manifest, followed by kind: procedure_entry rows with procedure_name, schema_name, short_name, signature, arguments, and section_id. Use CONTENT_MODE => 'FULL' for the entire document as markdown inside PAYLOAD.body. Use CONTENT_MODE => 'SECTION' with section_ids (e.g. '3', '4.8', 'guidance-for-ai-agents-and-automation') for kind: section_bundle and a sections array of {section_id, body} objects. Use CONTENT_MODE => 'CHUNK' to receive smaller kind: "chunk" rows; section_ids may be blank for all chunks, '1', '1-3', or 'offset=0,limit=12000'.
After each app upgrade, pull the contract with CALL airlock.agent.documentation(); and read PAYLOAD from each row (or use FULL / SECTION when you need prose bodies).
#Procedure mental model
Airlock procedures are meant to be guessable by family. Prefer named arguments whenever a procedure has optional parameters; positional calls are acceptable only for short discovery calls. Omit optional arguments you are not using instead of passing placeholder NULL values or typed casts. Named CALL syntax lets agents set only the fields that matter and avoids Snowflake argument-shape ambiguity.
- Discovery: User-scope discovery procedures may still use
list_*,describe_*, andget_*names. Admin read-only inventory and detail procedures have moved toobserve, which uses noun-style read-only names: plural nouns return collections (observe.specs), singular nouns return detail (observe.spec), and cross-cutting concepts use domain nouns (observe.activity). Optional filters come after the primary object name. User-scope discovery commonly acceptsin_app_roleandinclude_managed_rolesso agents can choose an Airlock role lens without confusing it with a Snowflake role. - Declarative configuration:
create_*andalter_*admin procedures usually accept one descriptorVARIANTplusvalidate_only. Usevalidate_only, notdry_run, when validating a desired configuration without writing it. - Operational previews: destructive operational procedures use
dry_runwhen the result is a concrete preview of rows/files/events that would be changed. Drop-style configuration procedures useforcewhen the caller is overriding dependency checks. - Data submission:
validate_dataandload_datause exactly one source:pathfor an already-staged file, orfile_contentfor inline CSV. For inline loads, passfilename. Agents and generated apps may author payloads as records JSON in their tool layer, but the installed procedure contract still receives CSVfile_content; do not pass ad hocrecords_jsonarguments. Records JSON payloads should contain only business records plusspec_name/filename; keeppath_scope,in_app_role,on_behalf_of_user,delegation_id, workflow target, and attachment content as explicit procedure/tool context. Use a tool-side size guard before inline calls, and stage a file when the generated CSV is too large. OKF-style knowledge bundles are not CSV submissions; for specs that declarecore_config.payload_adapter = "okf_knowledge_bundle", useadmin.load_okf_bundlewith base64 bundle bytes and parsed concept metadata. For shared-folder direct writes, passpath_scope. For delegated writes, passon_behalf_of_userand let Airlock resolve the principal user's path. Do not passpath => NULL; simply omitpath.load_datalands in the spec's configured initial workflow step. If that step is Draft and the caller wants Submitted, load first, pin any required evidence withadd_file_reference, and then calledit_file_workflow; do not add workflow-state columns to the payload.load_dataevaluates active Expectations before writing the manifest row: strict unmet expectations returnEXPECTATION_BLOCKED; non-strict unmet expectations returnEXPECTATION_WARNINGwhile allowing the load. - Payload discipline: build submitted files from the spec's
column_config,file_rules, andattachment_policy. Do not invent ids or add Airlock lifecycle fields such asworkflow_status,workflow_step,approval_status,approved_by, orapproved_atunless those fields are explicitly declared by the spec as real source-system facts. Airlock returns file identity, workflow state, validation issues, and attachment status separately. - Existing-file operations: attachment and workflow procedures operate on a manifest file and therefore use
file_path/file_filenameorpath/filenameas file identity, not staged file source. - Delegation: delegation is user-to-agent authority for
airlock.agent.*procedures. Delegated action procedures puton_behalf_of_useranddelegation_idat the tail. In the common case, pass onlyon_behalf_of_user; omitdelegation_idunlesslist_my_delegationsshows multiple active grants that could match. Admin procedures are not delegated. ADELEGATED_ACTIONevent records the authorized attempt and identifies the actor, principal, and delegation; it does not by itself assert that the requested mutation committed. Normal file, attachment, and workflow events provide commitment evidence.load_datacreates the file in the spec's configured initial workflow step;edit_file_workflowis required only when the delegate needs to move the file after load. - Expectations:
descriptionshould explain the business reason in human terms.clausesencode the cadence, sequence, interval, oraction_limit;target_milestonenames the workflow state being expected;is_strictsays whether an unmet expectation can block workflow or load. Non-strict schedule/sequence/interval expectations surface as warnings/work items. For specs without file workflow, the current file slot isActiveand the implicit upload milestone isbecame_active.action_limitexpectations are usage limits, not work-inbox items; they are observable throughobserve.usage_limits/observe.usage_limitso auditors and agents can see the rule and recent counted interactions.effective_at/expires_atbound the lifecycle. Discovery should show configuration (observe.expectations/observe.expectation), current operational work (observe.workfor observers oragent.list_my_workfor agents, includingDUE_ATandDAYS_UNTIL_DUE), and usage limits (observe.usage_limits) as distinct surfaces. - Returns: operational mutation procedures should expose branchable outcomes such as
STATUS,CODE,MESSAGE,ISSUES,DETAILS, andIS_CREATED/IS_UPDATED/IS_LOADED. KeepVALIDATIONfor procedures whose result is genuinely a configuration validation report. Agents should branch on stable fields and reason codes, then summarize prose for humans. - Workspace receipts: after each supported
agent.*or intentionaladmin.*mutation, preserve anairlock.commitment-receipt/v1with the authenticated Snowflake actor, represented principal and delegation id derived from the actual call arguments, target, stable status/code/message/issues, and timestamp. After a successful direct file mutation, callagent.file_statewith the returned target and retain itsUPLOAD_ID,FILE_STATE_ID, and workflow state. Do not make that direct actor state read after delegated work; leavecommitted_statenull unless the actor performs a separate independently authorized observation. Denied calls remain useful receipts but have no committed state.
#Admin read migration to Observe
Admin read-only procedures were retired in favor of the observe schema. The versioned app schema does not retain old procedures across app versions, so use these observe equivalents after upgrading:
| Retired admin read | Observe equivalent |
|---|---|
airlock.admin.list_events | airlock.observe.activity |
airlock.admin.get_events_purge_policy | airlock.observe.activity_retention_policy |
airlock.admin.list_roles | airlock.observe.roles |
airlock.admin.describe_role | airlock.observe.role |
airlock.admin.list_assignments | airlock.observe.assignments |
airlock.admin.describe_assignment | airlock.observe.assignment |
airlock.admin.list_delegations | airlock.observe.delegations |
airlock.admin.list_licenses | airlock.observe.licenses |
airlock.admin.get_license_ceiling | airlock.observe.license_policy |
airlock.admin.list_specs | airlock.observe.specs |
airlock.admin.describe_spec | airlock.observe.spec |
airlock.admin.get_spec | airlock.observe.spec_config |
airlock.admin.list_spec_versions | airlock.observe.spec_versions |
airlock.admin.get_spec_version | airlock.observe.spec_version |
airlock.admin.list_pending_specs | airlock.observe.pending_specs |
airlock.admin.describe_spec_import | airlock.observe.spec_import |
airlock.admin.list_spec_templates | airlock.observe.spec_templates |
airlock.admin.describe_spec_template | airlock.observe.spec_template |
airlock.admin.list_spec_template_assignments | airlock.observe.spec_template_assignments |
airlock.admin.list_files | airlock.observe.files |
airlock.admin.list_file_versions | airlock.observe.file_versions |
airlock.admin.list_work_items | airlock.observe.work |
airlock.admin.list_retention_policies | airlock.observe.retention_policies |
airlock.admin.describe_retention_policy | airlock.observe.retention_policy |
airlock.admin.list_outdated_files | airlock.observe.purge_candidates |
airlock.admin.list_expectations | airlock.observe.expectations |
airlock.admin.describe_expectation | airlock.observe.expectation |
airlock.admin.list_expectation_exceptions | airlock.observe.expectation_exceptions |
airlock.admin.describe_expectation_exception | airlock.observe.expectation_exception |
airlock.admin.list_expectation_work | airlock.observe.work |
airlock.admin.list_references | airlock.observe.references |
airlock.admin.list_reference_access | airlock.observe.reference_access |
Use airlock.observe.admin_activity when you specifically need admin mutation and maintenance activity filtered by stable procedure_name and action_family metadata. Use airlock.observe.spec_admin_activity when you need the admin create/update/delete timeline for one spec, with pointers to observe.spec_versions / observe.spec_version snapshots where available. New alter_spec events also include metadata-only CHANGED_SECTIONS and CHANGED_FIELDS labels so observers and agents can see what kind of spec definition changed before loading a full config snapshot.
#Day-one data submission recipe
For a new agent or generated app, follow one small loop before inventing anything:
- Resolve the installed app object and use that name consistently.
- Fetch
CALL <APP_NAME>.AGENT.DOCUMENTATION(CONTENT_MODE => 'PROCEDURES');. - List roles with
CALL <APP_NAME>.AGENT.LIST_MY_ROLES();. - List and describe specs with the selected Airlock role:
LIST_MY_SPECS('<airlock_role>', TRUE)andDESCRIBE_SPEC('<spec_name>', '<airlock_role>', TRUE). - Build records only from the described spec. Records payloads contain only
spec_name,filename, andrecords; all role, path, delegation, workflow, and attachment context stays outside the records payload. - Convert records JSON to CSV locally only for flat specs or declared
variantcolumns, then callAGENT.VALIDATE_DATAandAGENT.LOAD_DATAwith named arguments. - Use the returned
PATHandFILENAMEfor follow-up attachments or workflow moves. Do not recompute file identity from a prompt or filename guess. - Report structured results first:
STATUS,CODE,MESSAGE,ISSUES, path, filename, workflow state, and attachment status.
Direct writes use explicit role/path context such as in_app_role and path_scope. Delegated writes usually pass only on_behalf_of_user; add delegation_id only when Airlock reports multiple matching grants.
#What agents actually receive (no automatic “saved file”)
Snowflake returns a result set (rows/columns), not a workspace file or attachment. agent.documentation does not create a .md on disk for you — anything you retrieve exists only in that query result until you copy it somewhere durable.
Expectations for agents
- Fetch — run
CALL airlock.agent.documentation()(default TOC),CONTENT_MODE => 'PROCEDURES',CONTENT_MODE => 'FULL', orSECTIONwithsection_ids. - Persist — if you need a durable file, assemble from
PAYLOAD(e.g.FULL.body, or TOC/section JSON) and write to an artifact you control. SQL still does not create a file for you. - Re-read — in later turns, open that saved artifact (or call
documentationagain) instead of assuming rows still fit in context. - Avoid scraping — use
PROCEDURESfor structured procedure discovery; useSECTION, or useCHUNKand assemble rows bychunk_indexwhenFULLis too large. - Cache — store
content_hash/etagbeside the saved artifact and re-fetch when the hash changes after an app upgrade.
Do not assume a single historical CALL remains in memory across sessions, tools, or humans; treat persistence as mandatory for any long-lived automation.
#Snowflake CLI / terminal clients (optional)
With CONTENT_MODE => 'FULL', a single row can still serialize as a very wide display: many clients pad to the longest line inside the embedded markdown body. Default TOC rows carry small VARIANT objects and are easier in a terminal grid.
Practical rule: for agents using Snowflake CLI, prefer --format JSON_EXT --silent. JSON_EXT preserves VARIANT columns such as PAYLOAD, VALIDATION, ISSUES, and DETAILS as nested JSON objects. Plain JSON may serialize those VARIANT values as strings, and TABLE is for humans, not automation.
snow sql -c myconn --format JSON_EXT --silent \
-q "CALL airlock.agent.documentation(CONTENT_MODE => 'PROCEDURES');"
For full prose, do not rely on the default ASCII table for huge markdown. Redirect with a non-grid format, or use a driver:
snow sql -c myconn --format JSON_EXT --silent \
-q "CALL airlock.agent.documentation(CONTENT_MODE => 'FULL');"
Agents should prefer a Snowflake driver or Snowflake CLI JSON_EXT when they need structured PAYLOAD in memory without CLI table art. Start with default CALL airlock.agent.documentation() (TOC) before FULL if you only need the outline.
#Explaining next steps to a human app_admin
Admins may be unfamiliar with Airlock. When you hit limits, briefly state what you attempted, the exact procedure name, error text or reason code, and one concrete remediation (below). Separate Snowflake access / role problems from Airlock license, Airlock role, or template problems. Prefer asking the human to run a specific CALL airlock.admin.* or to use Streamlit admin flows (roles, assignments, templates, licenses) when your session is app_agent only.
#Spec templates and delegated ownership (recommended automation path)
Goal: A trusted admin publishes an approved spec template (columns, file rules, workflow, optional guest model, locked fields). Automation accounts use airlock.agent.create_spec_from_template instead of inventing specs ad hoc. Airlock also installs a public default_spec_draft_template so licensed app_agent agents can start unpublished drafts for app_admin review without a custom template assignment.
Delegation: If the template defines template_rules.forced_owner_role (commonly app_admin for centralized ownership), an app_agent caller may still instantiate the spec: assignment to the template is proof the admin delegated that shape. The merged config carries template_config.template_rules so create validates the forced owner. Without forced_owner_role, a non-admin cannot create a spec owned only by a role outside their assignment subtree—design templates explicitly for the delegation you need.
Admin setup checklist (typically once per template/automation principal):
- Ensure an Airlock role exists for the agent (e.g.
ai_agent,automation_user) viaairlock.admin.create_roles(or equivalent). - Create the template with
airlock.admin.create_spec_template(includeforced_owner_rolewhen governance requires a fixed owner). - Assign the template to that Airlock role with
airlock.admin.create_spec_template_assignment, or setis_public: truewhen every licensed app agent may use the template. - Assign the Snowflake user the agent uses to that Airlock role with
airlock.admin.create_assignments(user_idis usually the Snowflake login name). - Ensure the agent holds a named license seat if it will call
airlock.agent.*(get_my_license_seat,claim_license_seat()with no arguments, or admin licensing procedures).
Only app_admin maintains templates and assignments; do not widen automation to all admin.* procedures unless policy requires it.
#Chaining multiple agents (different Airlock roles)
Model multi-step pipelines with different Snowflake users or accounts, each granted APPLICATION ROLE app_agent but holding different Airlock roles (core.in_app_assignments). Each step gets narrow template assignments and spec/guest access—like a business where one party submits data, another validates completeness, a third runs fraud or duplication checks, and a final stage triggers payment or downstream export.
- Airlock roles (
core.in_app_roles) are Airlock’s authorization labels;app_agentis only the Snowflake procedure privilege. - Use file workflow, guest_access with isolated directories, and/or separate specs per stage so each agent sees only permitted paths (see
docs/agent_access_control_model.md). - References and materialized tables can support cross-stage analytics while keeping write scope tight.
When suggesting changes, name the Airlock role, template, and assignment the admin should add or adjust.
#If the agent cannot authenticate to Snowflake
Resolve account / network / credential / role issues before debugging Airlock procedures.
| Approach | What to tell the human |
|---|---|
| Snowflake CLI | Install Snowflake CLI; add a named connection with account, user, authenticator (SNOWFLAKE_JWT for key pair, OAUTH for SSO, or password where allowed), warehouse, database, schema, and a role that can use the Native App and activate app_agent (or app_admin for setup). Run snow sql -c <name> --format JSON_EXT --silent -f script.sql for agent-friendly output. |
| Snowsight | Open Projects → Worksheets, use a role with access to the app, USE APPLICATION ROLE app_agent or app_admin, then run the same CALL statements. |
| Drivers (Python, JDBC, etc.) | Same parameters as Snowflake CLI; use connection parameters documented for the Snowflake connector. |
| Key-pair authentication | Often preferred for service users: admin associates a public key with the Snowflake user; the agent loads the private key (or JWT) with no password in scripts. See Snowflake “Key-pair authentication & key-pair rotation”. |
| Programmatic access tokens (PAT) | Where enabled by account policy; time-bounded REST access—rotate and scope per security team guidance. |
SSH keys are common for git or bastion access to a runner, not a substitute for Snowflake authentication itself—pair Snowflake auth with how your runner reaches the network.
#Permission gaps: options to give the human
| Symptom | What the admin can do |
|---|---|
LICENSE_SEAT_REQUIRED / seat messages | Adjust pool / assignments via airlock.admin.* licensing procedures; or use Streamlit license management if deployed. |
| Template “not assigned to your roles” | create_spec_template_assignment linking the template to the agent’s Airlock role (not only Snowflake app_agent), or mark the template is_public when it should be available to every licensed user. |
Owner / descendant_roles errors on create_spec_from_template | Template likely missing forced_owner_role while spec owner is outside the caller’s subtree; alter template or have admin create the spec; see Spec templates and delegated ownership above. |
Cannot run airlock.admin.* | Expected for dedicated automation with only app_agent; human app_admin runs control-plane calls in Snowsight or Snowflake CLI. |
| Path / guest / workflow denials | describe_spec for permitted path_scope; consult docs/pdp_authorization_runbook.md; adjust guest_access or workflow in admin Streamlit or alter_spec. |
#1. Versioning, Security & Compatibility
- Reference version:
1.0 - Schemas:
airlock.admin– admin‑only mutation/control-plane procedures (Snowflake application roleapp_admin).airlock.observe– read-only governance observability procedures (application rolesapp_adminandapp_observer).airlock.agent– agent-facing procedures (application rolesapp_agentandapp_admin).
#1.1 Security & access model
- Airlock application roles
app_admincan call allairlock.admin.*,airlock.observe.*, andairlock.agent.*procedures.app_observercan call onlyairlock.observe.*procedures. It does not receive direct core table/stage access and cannot call admin mutation procedures. Holdingapp_observeralone does not grant account-wide visibility: Airlock assignments and spec access rules scope each read.app_agentcan call onlyairlock.agent.*procedures.- Snowflake object security (GRANTs on the procedures themselves) is the primary enforcement mechanism for which application role can call which schema. For file mutation/validation flows, both
airlock.agent.*and keyairlock.admin.*wrappers use a shared policy decision point (PDP) for consistent in‑procedure authorization behavior and reason-code outcomes.
- Named-license gate model
airlock.agent.*procedures and Streamlit usage require a named license seat (including callers withapp_admin).- Calls limited to
airlock.observe.*do not claim or consume a named seat. An observer becomes seat-consuming only when it also uses the agent or Streamlit work surface. - Auto-assign may grant a seat on first gated call when enabled.
- License administration under
airlock.admin.*is the break-glass path and does not require the caller to already hold a seat. - Operationally, if the seat pool is full, use
CALL airlock.admin.*licensing procedures in SQL to unassign seats or raise the ceiling.
- In‑app roles and assignments
- Airlock roles (
core.in_app_roles) and assignments (core.in_app_assignments) model application‑level access on top of Snowflake roles. - Agent-facing APIs (
airlock.agent.*) use these to enforce who can see which specs/files/history; internal checks here are about Airlock roles, not Snowflake application roles. - Observer-facing APIs use the same assignment graph without invoking the seat gate.
observer_access.observer_rolesgrants read-only spec visibility and may narrow it withpath_filterandworkflow_states. app_adminhas account-wide observation. Non-admin account-wide auditors must be explicitly assigned the locked Airlock roleglobal_observer; ordinary observer roles see only their assigned specs.global_observeris reserved case-insensitively for read-only observation and cannot be created, altered, dropped, own specs, receive guest/reviewer grants, or manage child roles.- Admin procedures (
airlock.admin.*) may expose a lens parameter (e.g.in_app_role) so admins can “see as Airlock role X” while still relying on Snowflake’s grants for coarse‑grained admin vs agent access.
- Detailed composition guide
- For the exact cascade of owner vs guest lens, role inheritance/assignment, spec state (
is_published,is_archived), path targeting, and workflow mutation checks, seedocs/agent_access_control_model.md. - PDP troubleshooting and reason-code runbook:
docs/pdp_authorization_runbook.md.
- Data access philosophy
- All data access goes through narrow, purpose‑built procedures (e.g.
select_files,observe.activity) instead of direct table access. - Procedures are written to:
- Accept simple scalar parameters (VARCHAR, BOOLEAN, TIMESTAMP) suitable for BI tools and SQL clients.
- Enforce row‑level and object‑level access based on Airlock roles and spec configuration (e.g. guest access).
- Avoid returning sensitive data (only metadata needed for audit/observability).
- Events & observability
- Application activity is recorded in
core.eventsfrom two sources: (1) the Streamlit UI viarecord_event()inlib/utils/events.py, and (2) stored procedures that perform mutations (create/alter/drop roles, assignments, specs, retention policies; load/delete files; workflow transitions). Admin procedure activity is additionally journaled incore.ADMIN_ACTIVITYwith stable procedure and action-family metadata. Events are surfaced through: airlock.observe.activity(callable byapp_adminandapp_observer) for querying events.- Higher‑level analytical helpers in
lib/utils/events.pyfor the Streamlit UI. - Stored-procedure events add
event_context.snowflake_agent_activefromSYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED')::BOOLEANinside the existing event insert. This is informational provenance, costs no additional Snowflake round trip, and never changes Airlock authority, policy, workflow, expectations, or billing. - Event types used by procedures (aligned with UI where applicable):
USER_LOGIN,LICENSE_DENIED,LICENSE_ASSIGNED(named-license gate / self-serve seat claim),RECORD_CREATE,RECORD_IMPORT,RECORD_UPDATE,RECORD_DELETE,FILE_UPLOAD,FILE_DELETE,FILE_RETENTION_PURGE(each row removed bydelete_outdated_files;object_nameiscore.FILE_MANIFEST.pk,filenameandspec_name/pathset,upload_idinevent_description),EVENTS_AUTO_PURGE(summary after scheduled trimming ofcore.eventsandcore.ADMIN_ACTIVITY),WORKFLOW_CHANGE(workflow step transitions fromedit_file_workflow). Admin procedure events are written to both activity stores under one ID, with exact response-loss reconciliation and a visible legacy-event fallback. Non-admin event recording remains best-effort and non-blocking. - The events schema is optimized for:
- Who did what (
username,in_app_role,object_name), - When (
event_timestamp), - Why (
event_type,event_description), - With optional file/spec context (
spec_name,path,filename,workflow_event).
- Retention & purging
- The events purge policy applies the same configured time window to
core.eventsandcore.ADMIN_ACTIVITY. - Retention policies (
core.retention_policies) plusobserve.purge_candidatesandadmin.delete_outdated_filesdefine a predictable lifecycle for spec files and their derived data. - Successful
delete_outdated_filesruns (withdry_run => FALSE) emit oneFILE_RETENTION_PURGEevent per removed upload, in addition to returning one result row per file (includingmanifest_pk). - Procedures are designed to be idempotent and auditable – destructive actions (deletes, drops) are either:
- Soft‑delete first (archive/mark), or
- Guarded by explicit
forceflags and locked‑state checks.
#1.2 Compatibility rules
Within v1, non‑breaking changes (new procedures, optional parameters with safe defaults, new trailing result columns) are allowed; breaking changes (renames, parameter/type/meaning changes) are not. See the rules and versioning guidance following the procedure matrix below.
#1.3 Procedure coverage matrix (admin vs observe vs user)
The platform has three core API categories with different trust boundaries:
airlock.admin.*for control-plane and privileged maintenance operations.airlock.observe.*for read-only governance inventory, activity, and context.airlock.agent.*for assignment-scoped data operations and agent workflows.
The matrix below is a role/action inventory to verify command coverage, read coverage, and intentional boundaries. Read-only governance procedures live in the observe row rather than the admin command column.
| Domain | Conceptual CRUD action | Admin procedures (airlock.admin.*) | Agent procedures (airlock.agent.*) |
|---|---|---|---|
| Roles | Create / Read / Update / Delete | create_roles, alter_role, drop_role | list_my_roles (read own effective roles) |
| Assignments | Create / Read / Update / Delete | create_assignments, alter_assignment, drop_assignment | list_my_assignments (read own assignments) |
| Delegations | Create / Read / Revoke | create_delegation, revoke_delegation | create_delegation (self-service principal-only), list_my_delegations (read delegations involving current user) |
| Expectations | Create / Read / Update / Delete | create_expectation, alter_expectation, drop_expectation, create_expectation_exception, alter_expectation_exception, revoke_expectation_exception | list_my_work (read own current workflow and expectation work) |
| Specs | Create / Read / Update / Delete | create_spec, alter_spec, drop_spec | list_my_specs (read allowed specs), describe_spec (get config/fields for a spec), spec_state / spec_states (poll contract and visible dataset state), create_spec_from_template (scoped create path) |
| Files | Create / Read / Update / Delete | load_data, load_okf_bundle, sync_okf_bundle_metadata, select_files, validate_data, edit_file_workflow, delete_files | load_data, list_my_files, file_state (poll one file UUID), select_my_files, select_reference_data, get_reference_record, validate_data, edit_file_workflow, delete_files |
| Attachments | Create / Read / Update / Delete | attach_to_spec, add_attachment, replace_attachment, delete_attachment | add_attachment, replace_attachment, delete_attachment |
| Spec templates | Create / Read / Update / Delete | create_spec_template, alter_spec_template, drop_spec_template, create_spec_template_assignment, drop_spec_template_assignment | Used indirectly via create_spec_from_template |
| References | Create / Read / Update / Delete | register_reference, revalidate_reference, grant_reference_access, revoke_reference_access, unregister_reference | No direct reference-admin procedures (intentional) |
| Retention | Create / Read / Update / Delete | create_retention_policy, alter_retention_policy, drop_retention_policy, delete_outdated_files | No direct retention-admin procedures (intentional) |
| Events/observability | Read | Mutating/maintenance event settings stay under admin | observe.activity, observe.admin_activity, observe.spec_admin_activity, observe.activity_retention_policy, observe.spec_migrations, observe.spec_migration, observe.spec_migration_items |
| Observe setup/read model | Read | Admin still owns changes | observe.setup, observe.health, observe.governance_map, observe.explain_access, observe.agent_context, observe.procedures, observe.admin_activity, observe.spec_admin_activity, observe.activity_summary, observe.activity_context, observe.billing, observe.billing_line_items, observe.billing_events, observe.billing_event_context, observe.billing_run_context, observe.billing_activity, observe.roles, observe.role, observe.role_context, observe.assignments, observe.observers, observe.assignment, observe.assignment_context, observe.delegations, observe.delegation_context, observe.licenses, observe.license_context, observe.license_policy, observe.specs, observe.spec, observe.spec_state, observe.spec_states, observe.spec_compatibility, observe.spec_config, observe.spec_context, observe.spec_versions, observe.spec_version, observe.spec_version_context, observe.pending_specs, observe.spec_import, observe.spec_templates, observe.spec_template, observe.spec_template_context, observe.spec_template_assignments, observe.files, observe.file_state, observe.file_versions, observe.file_validations, observe.path_context, observe.attachments, observe.attachment_context, observe.file_timeline, observe.workflow_context, observe.work, observe.retention_policies, observe.retention_policy, observe.retention_context, observe.purge_candidates, observe.expectations, observe.expectation, observe.expectation_context, observe.usage_limits, observe.usage_limit, observe.expectation_exceptions, observe.expectation_exception, observe.references, observe.reference_access, observe.reference_context |
| Help / reference metadata | Read | api_info, admin_help | agent.help, agent.documentation |
Completeness check summary
- Agent API completeness: agent-facing CRUD is complete for assignment-scoped file workflows, attachments, and spec discovery/history paths.
spec_state, batchedspec_states, andfile_stateprovide authorization-scoped change detection without retrieving governed rows. Privileged setup/administration actions are intentionally excluded from agent endpoints. - Admin procedure completeness: admin coverage is complete for control-plane CRUD (roles, assignments, specs, templates, references, retention), file workflow and attachments, plus maintenance.
- Observe API completeness: observe coverage starts with read-only governance monitoring for roles, assignments, flattened observer scope and billing posture, delegations, specs, templates, files, compact spec/file state, attachment metadata, file timelines, workflow work, expectations, retention, references, licenses, billing posture, current billing line items, Marketplace billing-event history, billing activity, general activity rows, AI-readable activity summaries, an AI-readable governance map, point access explanations, per-agent context packets, per-role context packets, per-assignment context packets, per-delegation context packets, per-spec context packets, per-spec-version provenance packets, per-template context packets, per-attachment context packets, per-expectation context packets, per-reference context packets, and per-retention-policy context packets.
- Boundary model: agent endpoints perform in-procedure authorization checks and claim a named seat; admin endpoints rely on stronger app-role grants plus admin-only maintenance capabilities; observe endpoints are read-only, never return governed payload rows/bytes, do not claim a seat, and enforce either assignment-scoped spec visibility or explicit
global_observer/app_adminaccess for account-wide surfaces.
#1.3 Observe procedure starting set
All observe procedures are read-only and callable at the Snowflake object layer by app_admin plus app_observer. In-procedure authorization then narrows results: spec procedures require an assigned role with access to that spec; path/workflow filters are enforced where file metadata is returned. Account-wide surfaces such as setup, health, governance map, activity summaries/context, admin activity, roles, assignments, observers, delegations, licenses, billing, templates, pending imports, and unscoped retention/reference inventories require app_admin or the locked Airlock role global_observer. Observe calls do not claim a named seat and never grant raw core table, stage, generated table, or governed file payload access.
airlock.observe.setup(scope VARCHAR DEFAULT 'summary', subject_type VARCHAR DEFAULT NULL, subject_name VARCHAR DEFAULT NULL, max_items FLOAT DEFAULT 200)- Description: Returns an AI-readable, account-wide setup inventory as
TABLE(PAYLOAD VARIANT). Requiresapp_adminor an assignment toglobal_observer. Rows includesetup_manifest,setup_counts,setup_roles,setup_assignments,setup_specs,setup_access_edges,setup_delegations,setup_expectations,setup_expectation_exceptions,setup_references,setup_retention_policies,setup_file_summary,setup_attachment_summary,setup_licenses,setup_billing, andsetup_activity_retention. - Parameters:
scopeacceptssummary,inventory, orfull; current payload shape is the same bounded inventory for all three.subject_typemay berole,user, orspec; when set,subject_nameis required.max_itemscaps array items per section (1-500). - Returns: One
PAYLOADobject per section. Parse byPAYLOAD.kind; do not scrape rendered SQL output. - Examples:
CALL airlock.observe.setup();
CALL airlock.observe.setup(subject_type => 'spec', subject_name => 'budget_requests');
CALL airlock.observe.setup(subject_type => 'role', subject_name => 'budget_analyst', max_items => 50);
airlock.observe.health(scope VARCHAR DEFAULT 'summary', subject_type VARCHAR DEFAULT NULL, subject_name VARCHAR DEFAULT NULL, max_items FLOAT DEFAULT 200)- Description: Returns account-wide governance health findings as
TABLE(PAYLOAD VARIANT). Requiresapp_adminor an assignment toglobal_observer. Rows includehealth_manifest,health_summary, andhealth_findings. Findings use stablecode,severity,domain,subject_type,subject_name,message,evidence, andrecommended_actionfields. - Parameters: Same filter grammar as
observe.setup: optionalsubject_type(role,user, orspec), requiredsubject_namewhen subject type is set, and boundedmax_items(1-500). - Examples:
CALL airlock.observe.health();
CALL airlock.observe.health(subject_type => 'user', subject_name => 'CSMITH');
CALL airlock.observe.health(subject_type => 'spec', subject_name => 'budget_requests');
airlock.observe.governance_map(scope VARCHAR DEFAULT 'summary', subject_type VARCHAR DEFAULT NULL, subject_name VARCHAR DEFAULT NULL, include_inactive BOOLEAN DEFAULT FALSE, max_items FLOAT DEFAULT 200)- Description: Returns an AI-readable governance graph as
TABLE(PAYLOAD VARIANT). Rows includegovernance_map_manifest,governance_map_summary,governance_map_nodes,governance_map_edges,governance_map_evidence, andgovernance_map_findings. Nodes and edges cover Snowflake application roles, procedure schemas, users, Airlock roles, managed-by relationships, assignments, specs, path scopes, workflow steps and reviewers, reference access, restricted-reference policies, delegation grants, attachment policies, expectations, andaction_limitlinks for restricted-reference reads. - Parameters: Same filter grammar as
observe.setup, plusinclude_inactiveto include expired/revoked delegations, inactive expectations, archived specs, and inactive assignments.max_itemscaps array items per section (1-500). - Examples:
CALL airlock.observe.governance_map();
CALL airlock.observe.governance_map(subject_type => 'user', subject_name => 'CSMITH');
CALL airlock.observe.governance_map(subject_type => 'spec', subject_name => 'budget_requests', include_inactive => TRUE);
airlock.observe.explain_access(user_id VARCHAR, spec_name VARCHAR, action VARCHAR DEFAULT 'read_validate', path_scope VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE, max_items FLOAT DEFAULT 200, object_key VARCHAR DEFAULT NULL)- Description: Returns a read-only point explanation for one user, spec, action, optional path scope, optional Airlock role lens, and optional reference object key as
TABLE(PAYLOAD VARIANT). Rows includeaccess_explain_manifest,access_explain_summary,access_explain_roles,access_explain_edges,access_explain_path_scopes,access_explain_restricted_reference, andaccess_explain_findings. - Parameters:
actionaccepts canonical PDP action names (read_validate,get_reference_record,write_create,write_update,write_delete,workflow_transition,write_attachment_add,write_attachment_replace,write_attachment_delete) and common aliases such asload_data,delete_files,add_attachment, andrestricted_reference.path_scopeis a logical Airlock path scope, not a raw stage path. Forget_reference_record, passobject_keyto explain the restricted-reference object path;path_scopeis accepted as a compatibility fallback only.in_app_roleoptionally narrows the Airlock role lens;include_managed_rolescontrols whether managed child roles are considered. - Returns: A compiled-access explanation with
allowed,reason_code, matched assignments/roles, compiled access edges, candidate path scopes, restricted-reference policy/object-path/action-limit evidence when relevant, and findings. It intentionally reads compiled Airlock access evidence and does not run user-procedure side effects such as creating isolated-directory path keys or querying reference data. - Examples:
CALL airlock.observe.explain_access('CSMITH', 'budget_requests', 'load_data', 'asmith');
CALL airlock.observe.explain_access(user_id => 'CSMITH', spec_name => 'budget_requests', action => 'read_validate');
CALL airlock.observe.explain_access(user_id => 'CSMITH', spec_name => 'customer_identity', action => 'get_reference_record', object_key => 'customers');
airlock.observe.agent_context(agent_id VARCHAR, in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE, max_items FLOAT DEFAULT 200)- Description: Returns a read-only agent context packet as
TABLE(PAYLOAD VARIANT). Rows includeagent_context_manifest,agent_context_summary,agent_context_roles,agent_context_specs,agent_context_path_scopes,agent_context_delegations,agent_context_expectations, andagent_context_recent_activity. - Parameters:
agent_idis required and maps to the Snowflake user/principal identifier.in_app_roleoptionally narrows the Airlock role lens, andinclude_managed_rolescontrols managed-role expansion for compiled access evidence.max_itemscaps arrays per section (1-500). - Returns: Effective role closure, accessible specs, path scopes, active delegation boundaries, expectations on accessible specs, and recent activity. It intentionally reads compiled Airlock access evidence and does not run agent-procedure side effects or expose raw file data.
- Examples:
CALL airlock.observe.agent_context('CSMITH');
CALL airlock.observe.agent_context(agent_id => 'CSMITH', in_app_role => 'observer', include_managed_roles => FALSE);
airlock.observe.role_context(role_name VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only Airlock-role context packet as
TABLE(PAYLOAD VARIANT). Rows includerole_context_manifest,role_context_summary,role_context_role,role_context_child_roles,role_context_assignments,role_context_effective_users,role_context_spec_edges,role_context_owned_specs,role_context_delegations,role_context_expectations, androle_context_recent_activity. - Parameters:
role_nameis required.max_itemscaps arrays per section (1-500). - Returns: Role metadata, managed child roles, assignments, effective users through compiled role closure, compiled spec access, owned specs, active delegations, expectations on accessible specs, and recent role-tagged activity. It does not expose raw governed file rows or attachment bytes.
- Examples:
CALL airlock.observe.role_context('observer');
CALL airlock.observe.role_context(role_name => 'finadmin', max_items => 50);
airlock.observe.assignment_context(assignment_name VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only assignment context packet as
TABLE(PAYLOAD VARIANT). Rows includeassignment_context_manifest,assignment_context_summary,assignment_context_assignment,assignment_context_role_closure,assignment_context_spec_edges,assignment_context_specs,assignment_context_path_scopes,assignment_context_delegations,assignment_context_expectations, andassignment_context_recent_activity. - Parameters:
assignment_nameis required.max_itemscaps arrays per section (1-500). - Returns: The assignment row, direct and managed-role closure, compiled spec/path access created by that assignment, related delegations, expectations on accessible specs, and recent activity. It does not expose raw governed file rows or attachment bytes.
- Examples:
CALL airlock.observe.assignment_context('observer.aroberts');
CALL airlock.observe.assignment_context(assignment_name => 'finance.csmith', max_items => 50);
airlock.observe.delegation_context(delegation_id VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only delegation context packet as
TABLE(PAYLOAD VARIANT). Rows includedelegation_context_manifest,delegation_context_summary,delegation_context_delegation,delegation_context_spec,delegation_context_actor_roles,delegation_context_principal_roles,delegation_context_actor_spec_edges,delegation_context_principal_spec_edges,delegation_context_expectations, anddelegation_context_recent_activity. - Parameters:
delegation_idis required.max_itemscaps arrays per section (1-500). - Returns: The delegation grant, delegated spec, actor/principal role evidence, actor/principal spec access evidence, expectations on the delegated spec, and recent activity involving the actor or principal. It does not expose raw governed file rows or attachment bytes.
- Examples:
CALL airlock.observe.delegation_context('01c4f1a6-0313-ba32-0000-c16b022e0f1a');
CALL airlock.observe.delegation_context(delegation_id => '<delegation_id>', max_items => 50);
airlock.observe.spec_context(spec_name VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only spec context packet as
TABLE(PAYLOAD VARIANT). Rows includespec_context_manifest,spec_context_summary,spec_context_spec,spec_context_role_edges,spec_context_users,spec_context_delegations,spec_context_expectations,spec_context_references,spec_context_file_summary,spec_context_spec_admin_activity, andspec_context_recent_activity. Use spec admin activity version ids withobserve.spec_versionfor exact stage-backed config snapshots;alter_specactivity also carries metadata-only changed section/field labels. - Parameters:
spec_nameis required.max_itemscaps arrays per section (1-500). - Returns: Spec metadata/config, compiled role and principal access evidence, active delegation boundaries, expectations, source/reference links, file and attachment counts, and recent activity. It does not expose raw governed file rows or attachment bytes.
- Examples:
CALL airlock.observe.spec_context('finance');
CALL airlock.observe.spec_context(spec_name => 'budget_requests', max_items => 50);
airlock.observe.spec_version_context(spec_name VARCHAR, version_id VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only spec-version provenance packet as
TABLE(PAYLOAD VARIANT). Rows includespec_version_context_manifest,spec_version_context_summary,spec_version_context_selected_version,spec_version_context_current_spec,spec_version_context_neighboring_versions, andspec_version_context_recent_activity. - Parameters:
spec_nameandversion_idare required.max_itemscaps arrays per section (1-500). - Returns: The selected stage-backed spec config, sidecar metadata, current runtime spec config, nearby version metadata, and recent governance activity. It does not expose raw governed file rows or attachment bytes.
- Examples:
CALL airlock.observe.spec_version_context('budget_requests', '20260315123045');
CALL airlock.observe.spec_version_context(spec_name => 'finance', version_id => '20260315123045', max_items => 50);
airlock.observe.expectation_context(expectation_name VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only expectation context packet as
TABLE(PAYLOAD VARIANT). Rows includeexpectation_context_manifest,expectation_context_summary,expectation_context_expectation,expectation_context_spec,expectation_context_exceptions,expectation_context_workflow_counts,expectation_context_file_evidence, andexpectation_context_recent_activity. - Parameters:
expectation_nameis required.max_itemscaps arrays per section (1-500). - Returns: Expectation metadata, owning spec context, active exception evidence, workflow/file evidence, and recent activity. It does not run workflow gates; use
observe.workfor current due, late, unmet, or excepted expectation work. - Examples:
CALL airlock.observe.expectation_context('monthly_finance_upload');
CALL airlock.observe.expectation_context(expectation_name => 'monthly_finance_upload', max_items => 50);
airlock.observe.usage_limits(spec_name VARCHAR DEFAULT NULL, username VARCHAR DEFAULT NULL, max_items FLOAT DEFAULT 200)- Description: Returns a read-only packet for
action_limitExpectations and recentREFERENCE_READevents that consumed those limits. - Parameters:
spec_nameandusernameare optional filters.max_itemscaps arrays per section (1-500). - Returns: Manifest, summary,
action_limitrules, latest emitted usage counters, and bounded recent activity. It does not query raw reference targets or file rows. - Examples:
CALL airlock.observe.usage_limits();
CALL airlock.observe.usage_limits(spec_name => 'customer_identity', username => 'CSMITH', max_items => 50);
airlock.observe.usage_limit(expectation_name VARCHAR, username VARCHAR DEFAULT NULL, max_items FLOAT DEFAULT 200)- Description: Returns a read-only packet for one
action_limitExpectation. - Parameters:
expectation_nameis required.usernameis an optional activity filter.max_itemscaps arrays per section (1-500). - Returns: The configured rule, latest usage status (
action_limit_used,action_time_period,used_count,remaining_countwhen emitted), and bounded recent activity for the matching controlled action. - Examples:
CALL airlock.observe.usage_limit('support_customer_identity_daily_limit');
CALL airlock.observe.usage_limit(expectation_name => 'support_customer_identity_daily_limit', username => 'CSMITH');
airlock.observe.reference_context(reference_name VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only reference context packet as
TABLE(PAYLOAD VARIANT). Rows includereference_context_manifest,reference_context_summary,reference_context_reference,reference_context_allowed_roles,reference_context_effective_users,reference_context_backing_spec,reference_context_restricted_reference,reference_context_source_links,reference_context_file_references, andreference_context_recent_activity. - Parameters:
reference_nameis required.max_itemscaps arrays per section (1-500). - Returns: Reference registry metadata, granted Airlock roles, effective users through role closure, backing spec context, explicit
restricted_referencepolicy shape when present, bounded restricted-read activity evidence, source/file-reference lineage evidence, and recent activity. It does not query raw reference targets or file rows. - Examples:
CALL airlock.observe.reference_context('customer_ref');
CALL airlock.observe.reference_context(reference_name => 'customer_ref', max_items => 50);
airlock.observe.retention_context(policy_name VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only retention-policy context packet as
TABLE(PAYLOAD VARIANT). Rows includeretention_context_manifest,retention_context_summary,retention_context_policy,retention_context_covered_specs,retention_context_purge_candidates, andretention_context_recent_activity. - Parameters:
policy_nameis required.max_itemscaps arrays per section (1-500). - Returns: Policy metadata, specs currently governed by the policy, bounded purge candidate evidence using the same read-only eligibility logic as
observe.purge_candidates, and retention-related activity. For the account default policy, covered specs exclude specs with an active spec-specific policy override. It does not mutate manifests, stages, policies, or files. - Examples:
CALL airlock.observe.retention_context('Account Default');
CALL airlock.observe.retention_context(policy_name => 'Spec X Retention', max_items => 50);
airlock.observe.spec_template_context(template_name VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only spec-template context packet as
TABLE(PAYLOAD VARIANT). Rows includespec_template_context_manifest,spec_template_context_summary,spec_template_context_template,spec_template_context_forced_owner_role,spec_template_context_assignments,spec_template_context_effective_users, andspec_template_context_recent_activity. - Parameters:
template_nameis required.max_itemscaps arrays per section (1-500). - Returns: Template metadata/rules, forced owner role evidence, template assignment grants, effective users through Airlock role closure, and recent template activity. It does not mutate templates, assignments, specs, or files.
- Examples:
CALL airlock.observe.spec_template_context('default_spec_draft_template');
CALL airlock.observe.spec_template_context(template_name => 'finance_spec_template', max_items => 50);
airlock.observe.procedures(procedure_name VARCHAR DEFAULT NULL, schema_name VARCHAR DEFAULT NULL)- Description: Returns a read-only installed procedure catalog from Airlock's bundled API documentation as
TABLE(PAYLOAD VARIANT). The first row isprocedure_catalog_manifest; following rows areprocedure_catalog_entryobjects withprocedure_name,schema_name,short_name,signature,arguments, and documentation section metadata. - Parameters:
procedure_namemay be a short name (setup), schema-qualified name (observe.setup), or full name (airlock.observe.setup).schema_namemay beadmin,observe, oragent. - Examples:
CALL airlock.observe.procedures();
CALL airlock.observe.procedures(schema_name => 'observe');
CALL airlock.observe.procedures(procedure_name => 'observe.health');
airlock.observe.mcp_servers()- Description: Returns the fixed Airlock agent and observer Native App MCP interfaces and whether each Snowflake-managed MCP object is currently enabled. Requires
app_adminor the lockedglobal_observerAirlock role. - Returns:
TABLE(INTERFACE_NAME, MCP_SERVER_NAME, APPLICATION_ROLE, ENABLED, STATUS, CODE, MESSAGE, ENDPOINT_PATH, TOOL_NAMES).MCP_STATUS_UNAVAILABLEdistinguishes an unsupported or blocked Snowflake capability from an ordinary disabled server. - Boundary: Returns setup metadata only. It never returns authentication secrets, chat content, governed rows, restricted-reference values, or attachment bytes.
- Example:
CALL airlock.observe.mcp_servers();
airlock.observe.activity(username VARCHAR DEFAULT NULL, event_types VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL, event_description VARCHAR DEFAULT NULL, start_time TIMESTAMP_LTZ DEFAULT NULL, end_time TIMESTAMP_LTZ DEFAULT NULL, limit_rows INTEGER DEFAULT 200)- Description: Read-only governance activity feed over
core.eventswith optional filters. Scoped observers receive only events within their assigned spec/path scope;app_adminand assignedglobal_observercallers receive account-wide activity. - Returns:
EVENT_TIMESTAMP,EVENT_TYPE,EVENT_DESCRIPTION,USERNAME,IN_APP_ROLE,OBJECT_NAME,SPEC_NAME,EVENT_CONTEXT, plus trailing stableEVENT_ID. PassEVENT_IDtoobserve.activity_contextfor related governance evidence.
airlock.observe.admin_activity(procedure_name VARCHAR DEFAULT NULL, action_family VARCHAR DEFAULT NULL, username VARCHAR DEFAULT NULL, event_types VARCHAR DEFAULT NULL, start_time TIMESTAMP_LTZ DEFAULT NULL, end_time TIMESTAMP_LTZ DEFAULT NULL, limit_rows INTEGER DEFAULT 200)- Description: Read-only admin mutation and maintenance activity feed over the append-only
core.ADMIN_ACTIVITYjournal plus unmatched legacy or fallback rows fromcore.events. Filters and returns structured procedure/action fields so account-wide observers and administrators can ask what admins did without parsing prose. Requiresapp_adminor an assignment toglobal_observer. - Parameters:
procedure_nameaccepts short names such asload_dataor schema-qualified names such asadmin.load_data;action_familygroups related admin actions such asroles,assignments,specs,files,attachments,workflow,billing,licenses,events,references,retention,expectations,okf, andmaintenance. - Returns:
EVENT_TIMESTAMP,EVENT_TYPE,EVENT_DESCRIPTION,USERNAME,IN_APP_ROLE,PROCEDURE_NAME,ACTION_FAMILY,OBJECT_NAME,SPEC_NAME,EVENT_CONTEXT, plus trailing stableEVENT_IDandEVIDENCE_SOURCE(journalfor canonical append-only evidence orlegacy_eventfor an unmatched compatibility row). - Examples:
CALL airlock.observe.admin_activity();
CALL airlock.observe.admin_activity(procedure_name => 'admin.load_data');
CALL airlock.observe.admin_activity(action_family => 'billing', event_types => 'BILLING_EMIT');
airlock.observe.spec_admin_activity(spec_name VARCHAR, event_types VARCHAR DEFAULT NULL, start_time TIMESTAMP_LTZ DEFAULT NULL, end_time TIMESTAMP_LTZ DEFAULT NULL, limit_rows INTEGER DEFAULT 200)- Description: Read-only admin create/update/delete timeline for one spec definition. Returns current
core.specscreated/modified audit fields when the spec still exists, plus spec config version pointers when the event wrote a stage-backed version.alter_specevents include metadata-onlyCHANGED_SECTIONSandCHANGED_FIELDSarrays. - Returns:
EVENT_TIMESTAMP,EVENT_TYPE,EVENT_DESCRIPTION,USERNAME,PROCEDURE_NAME,SPEC_EVENT,SPEC_NAME, current created/modified columns,VERSION_ID,VERSION_PATH,META_PATH,SPEC_CONFIG_HASH,CHANGED_SECTIONS,CHANGED_FIELDS,EVENT_CONTEXT, plus trailing stableEVENT_IDandEVIDENCE_SOURCE(journalorlegacy_event). - Examples:
CALL airlock.observe.spec_admin_activity('budget_requests');
CALL airlock.observe.spec_admin_activity('budget_requests', event_types => 'RECORD_UPDATE');
CALL airlock.observe.spec_versions('budget_requests');
CALL airlock.observe.spec_version('budget_requests', '<version_id>');
airlock.observe.activity_summary(username VARCHAR DEFAULT NULL, event_types VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL, event_description VARCHAR DEFAULT NULL, start_time TIMESTAMP_LTZ DEFAULT NULL, end_time TIMESTAMP_LTZ DEFAULT NULL, max_items FLOAT DEFAULT 200)- Description: Returns a read-only AI-oriented activity packet as
TABLE(PAYLOAD VARIANT). Rows includeactivity_summary_manifest,activity_summary,activity_summary_by_event_type,activity_summary_by_user,activity_summary_by_spec, andactivity_summary_recent_items. - Parameters: Same filters as
observe.activity;event_typesaccepts a comma-separated list.max_itemscaps arrays per section (1-500). - Returns: Event volume, time bounds, grouped activity counts, and bounded recent activity items with
event_context. It does not write read-audit events. - Examples:
CALL airlock.observe.activity_summary();
CALL airlock.observe.activity_summary(event_types => 'FILE_UPLOAD,WORKFLOW_CHANGE', max_items => 50);
CALL airlock.observe.activity_summary(username => 'CSMITH');
airlock.observe.activity_context(event_id VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only context packet for one governance event as
TABLE(PAYLOAD VARIANT). Rows includeactivity_context_manifest,activity_context_summary,activity_context_event,activity_context_related_spec,activity_context_related_files,activity_context_related_attachments,activity_context_actor_access, andactivity_context_nearby_activity. - Parameters:
event_idis required; useEVENT_IDfromobserve.activityor theevent_idreturned byobserve.activity_summarycontext rows.max_itemscaps arrays per section (1-500). - Returns: The source event row, related spec/file/attachment metadata, current actor access evidence for the event spec, and nearby activity for the same object/spec/path. Raw file and attachment bytes are never returned, and the procedure does not mutate events, access, files, attachments, or stages.
- Examples:
CALL airlock.observe.activity_context('event-pk-or-uuid');
CALL airlock.observe.activity_context(event_id => '<event_id>', max_items => 50);
airlock.observe.attachments(spec_name VARCHAR DEFAULT NULL, path VARCHAR DEFAULT NULL, filename VARCHAR DEFAULT NULL, include_deleted BOOLEAN DEFAULT FALSE, max_items FLOAT DEFAULT 200)- Description: Returns a read-only attachment metadata inventory as
TABLE(PAYLOAD VARIANT). Rows includeattachments_manifest,attachments_summary,attachments_items,attachments_by_spec, andattachments_by_tag. - Parameters: Optional
spec_name,path, andfilenamefilters narrow the inventory to one spec or logical file. Deleted attachments are excluded unlessinclude_deleted => TRUE.max_itemscaps arrays per section (1-500). - Returns: Attachment IDs, spec/file linkage, stage paths, filenames, tags, types, sizes, descriptions, audit timestamps, and deleted state. Raw attachment bytes are never returned.
- Examples:
CALL airlock.observe.attachments();
CALL airlock.observe.attachments(spec_name => 'finance');
CALL airlock.observe.attachments(spec_name => 'finance', path => 'asmith', filename => 'budget_june', include_deleted => TRUE);
airlock.observe.attachment_context(attachment_id VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only attachment context packet as
TABLE(PAYLOAD VARIANT). Rows includeattachment_context_manifest,attachment_context_summary,attachment_context_attachment,attachment_context_spec_policy,attachment_context_linked_file,attachment_context_sibling_attachments, andattachment_context_recent_activity. - Parameters:
attachment_idis required.max_itemscaps arrays per section (1-500). - Returns: Attachment metadata, linked logical file metadata when applicable, spec attachment policy, same-file/tag sibling attachment evidence, and recent attachment/file activity. Raw attachment bytes are never returned.
- Examples:
CALL airlock.observe.attachment_context('9d4f5f7f-...');
CALL airlock.observe.attachment_context(attachment_id => '<attachment_id>', max_items => 50);
airlock.observe.file_timeline(spec_name VARCHAR, path VARCHAR, filename VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns an AI-readable timeline for one governed logical file as
TABLE(PAYLOAD VARIANT). Rows includefile_timeline_manifest,file_timeline_summary,file_timeline_items,file_timeline_file_versions,file_timeline_attachments, andfile_timeline_file_references. - Parameters:
spec_name,path, andfilenameidentify the logical file incore.FILE_MANIFEST.max_itemscaps arrays per section (1-500). - Returns: Bounded timeline and inventory rows from manifest versions, activity events, attachment metadata, and file-reference edges. Raw file and attachment bytes are never returned.
- Examples:
CALL airlock.observe.file_timeline('finance', 'default', 'budget_june');
CALL airlock.observe.file_timeline(spec_name => 'finance', path => 'asmith', filename => 'budget_june', max_items => 50);
airlock.observe.path_context(spec_name VARCHAR, path_scope VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only path-scope context packet for one governed spec path as
TABLE(PAYLOAD VARIANT). Rows includepath_context_manifest,path_context_summary,path_context_spec,path_context_role_edges,path_context_users,path_context_file_counts,path_context_files,path_context_attachments,path_context_expectations, andpath_context_recent_activity. - Parameters:
spec_nameandpath_scopeare required.max_itemscaps arrays per section (1-500). - Returns: Exact/broad/dynamic path access evidence, effective users, current file-state counts, bounded active file and attachment metadata, active expectations, and recent path activity. Raw file and attachment bytes are never returned, and the procedure does not mutate access, files, attachments, expectations, or stages.
- Examples:
CALL airlock.observe.path_context('finance', 'asmith');
CALL airlock.observe.path_context(spec_name => 'budget_requests', path_scope => 'public/full_access', max_items => 50);
airlock.observe.workflow_context(spec_name VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only workflow context packet for one governed spec as
TABLE(PAYLOAD VARIANT). Rows includeworkflow_context_manifest,workflow_context_summary,workflow_context_spec,workflow_context_steps,workflow_context_reviewers,workflow_context_file_counts,workflow_context_files, andworkflow_context_recent_activity. - Parameters:
spec_nameis required.max_itemscaps arrays per section (1-500). - Returns: Workflow configuration, reviewer rules, current active file-state counts, bounded active file evidence, and recent workflow/file activity. Raw file and attachment bytes are never returned, and the procedure does not mutate workflow, files, manifests, or stages.
- Examples:
CALL airlock.observe.workflow_context('finance');
CALL airlock.observe.workflow_context(spec_name => 'budget_requests', max_items => 50);
airlock.observe.license_context(user_id VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only license and billing context packet for one named user as
TABLE(PAYLOAD VARIANT). Rows includelicense_context_manifest,license_context_summary,license_context_seat_policy,license_context_license_holder,license_context_license_spells,license_context_seat_assignment_events,license_context_billing_events,license_context_airlock_assignments,license_context_compiled_access, andlicense_context_license_activity. - Parameters:
user_idis required.max_itemscaps arrays per section (1-500). - Returns: Named-license status, seat policy posture, license spell intervals, seat assignment audit rows, billing emit-log rows, Airlock assignments/access evidence, and license-related activity. It does not emit billing events or mutate licenses, billing, assignments, or access.
- Examples:
CALL airlock.observe.license_context('CSMITH');
CALL airlock.observe.license_context(user_id => 'AROBERTS', max_items => 50);
airlock.observe.billing()- Description: Read-only Marketplace billing posture: last emit status, pending queue depth, and latest emit run metadata. It does not emit billing events or change license policy.
airlock.observe.billing_line_items(max_rows FLOAT DEFAULT 5000)- Description: Read-only current UTC month Marketplace billing line-item candidates and emit status. These rows show which assigned seat-months are pending, emitting, or already logged by Airlock; they are not customer bills or invoices.
- Returns:
ASSIGNED_USER,BILL_MONTH_UTC,IDEM_KEY,BILLING_UNITS,LINE_STATUS,EMITTED_AT_UTC,SNOWFLAKE_EMIT_STATUS.
airlock.observe.billing_events(month_start VARCHAR DEFAULT NULL, month_end VARCHAR DEFAULT NULL, max_rows FLOAT DEFAULT 100000)- Description: Read-only history of Marketplace billing-event emissions/attempts from Airlock's billing emit log. These rows are not customer bills or invoices.
- Returns:
IDEM_KEY,BILL_MONTH_UTC,ASSIGNED_USER,EMITTED_AT_UTC,AMOUNT_USD,BILLING_UNITS,STATUS,RESPONSE_PREVIEW,PLAN_ID,DISCOUNT,OFFER_NAME, plus audit columns.
airlock.observe.billing_event_context(idem_key VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only context packet for one Marketplace billing-event row as
TABLE(PAYLOAD VARIANT). Rows includebilling_event_context_manifest,billing_event_context_summary,billing_event_context_billing_event,billing_event_context_license_holder,billing_event_context_license_spells,billing_event_context_seat_assignment_events,billing_event_context_emit_runs, andbilling_event_context_license_activity. - Parameters:
idem_keyis required.max_itemscaps arrays per section (1-500). - Returns: The billing emit-log row, matching license holder/spell evidence, seat assignment audit rows for the billed month, related emit-run log rows, and license-related activity. It does not emit billing events or mutate licenses, billing, assignments, or access.
- Examples:
CALL airlock.observe.billing_event_context('2026-06|CSMITH');
CALL airlock.observe.billing_event_context(idem_key => '<idem_key>', max_items => 50);
airlock.observe.billing_run_context(run_id VARCHAR, max_items FLOAT DEFAULT 200)- Description: Returns a read-only context packet for one Marketplace billing emit run as
TABLE(PAYLOAD VARIANT). Rows includebilling_run_context_manifest,billing_run_context_summary,billing_run_context_billing_run,billing_run_context_billing_health,billing_run_context_billing_events,billing_run_context_assigned_users,billing_run_context_license_holders,billing_run_context_license_spells,billing_run_context_seat_assignment_events, andbilling_run_context_license_activity. - Parameters:
run_idis required.max_itemscaps arrays per section (1-500). - Returns: The billing run-log row, global billing health, referenced billing emit-log rows, assigned-user summaries, matching license holder/spell evidence, seat assignment audit rows, and license-related activity. It does not emit billing events or mutate licenses, billing, assignments, or access.
- Examples:
CALL airlock.observe.billing_run_context('run-uuid');
CALL airlock.observe.billing_run_context(run_id => '<run_id>', max_items => 50);
airlock.observe.billing_activity(max_rows FLOAT DEFAULT 400)- Description: Read-only unified billing activity feed: billing emit run summaries, license policy or seat changes, and per-row seat-month outcomes. Use this for billing audit context without granting
admin.emit_billing. - Returns:
ACTIVITY_AT_UTC,ACTIVITY_KIND,ACTOR_USER,SUMMARY,DETAIL_JSON.
Non‑breaking changes allowed in v1:
- Adding new procedures.
- Adding optional parameters with safe defaults that preserve existing behavior.
- Adding new output columns at the end of result sets (callers must be resilient to extra columns).
Breaking changes (must NOT happen in v1):
- Renaming or removing existing procedures.
- Changing parameter names, types, or required/optional semantics.
- Changing the meaning of parameters or return columns such that the same inputs produce incompatible results.
Future breaking changes must be introduced via:
- New procedures with a version suffix (e.g.
select_files_v2), or - New versioned schemas (e.g.
airlock_v2.admin,airlock_v2.agent).
The current Airlock compatibility version and supported versions can be queried via:
CALL airlock.admin.api_info();
#2. Admin Schema (airlock.admin.*) And Related Observe Reads
Admin command procedures are callable only when the app_admin application role is active. Related read-only governance procedures live in airlock.observe.* and are callable by app_admin and app_observer.
#2.1. Introspection & Help
airlock.admin.help(procedure_name VARCHAR DEFAULT NULL)- Description: Returns help text for admin procedures.
- Parameters:
procedure_name(optional): name of a specific procedure to describe. IfNULL, returns an overview.- Returns:
TABLE(LINE VARCHAR)– one line of help text per row. - Example:
CALL airlock.admin.help();
CALL airlock.admin.help('list_roles');
airlock.admin.api_info()- Description: Returns compatibility version metadata and procedure status.
- Returns:
API_VERSION VARCHAR– e.g.1.0SUPPORTED_VERSIONS VARCHAR– comma‑separated list, e.g.1.0PROCEDURE_NAME VARCHAR– logical name withinairlock.adminCATEGORY VARCHAR– high‑level area:introspection,roles,specs,files,history,events,retention,maintenance, orvalidationSTATUS VARCHAR–existing(others reserved for future use)NOTES VARCHAR– freeform notes (e.g.admin-only; v1 surface)- Example:
CALL airlock.admin.api_info();
airlock.admin.enable_mcp_server(interface_name VARCHAR, warehouse_name VARCHAR, validate_only BOOLEAN DEFAULT FALSE)- Description: Creates one fixed, read-only Snowflake-managed MCP server over the Airlock
agentorobserverallowlist. If it already exists, Airlock verifies the fixed application-role grants without replacing the endpoint. Disable then enable to change the warehouse or refresh the specification. This remains opt-in so consumer feature-policy restrictions cannot block app installation. - Parameters:
interface_nameisagentorobserver;warehouse_nameis required and must be a conventional unquoted Snowflake identifier because owner-rights Native App procedures do not reliably inherit the caller's current warehouse;validate_onlychecks the fixed plan without DDL. - Returns:
TABLE(STATUS, CODE, MESSAGE, ISSUES). Stable codes includeMCP_SERVER_VALID,MCP_SERVER_ENABLED,MCP_SERVER_BLOCKED,MCP_WAREHOUSE_REQUIRED, andMCP_WAREHOUSE_INVALID. - Examples:
CALL airlock.admin.enable_mcp_server('agent', 'AGENT_WH', TRUE);
CALL airlock.admin.enable_mcp_server('agent', 'AGENT_WH');
CALL airlock.admin.enable_mcp_server('observer', 'OBSERVER_WH');
airlock.admin.disable_mcp_server(interface_name VARCHAR, validate_only BOOLEAN DEFAULT FALSE)- Description: Drops the fixed
agentorobserverMCP object without changing the underlying stored procedures, procedure grants, or governed state. - Returns:
TABLE(STATUS, CODE, MESSAGE, ISSUES)with stableMCP_SERVER_DISABLE_VALID,MCP_SERVER_DISABLED, orMCP_SERVER_DISABLE_FAILEDcodes. - Example:
CALL airlock.admin.disable_mcp_server('agent');
#2.1.1. Named license operations
airlock.observe.licenses([include_inactive BOOLEAN])- Description: Lists named license holder rows and policy (same
seat_ceiling/auto_assign_enabledon every row). Default: active only (is_active = TRUE). Passinclude_inactiveTRUE to include deactivated rows (historic usernames,statusinactive).FIRST_LICENSED_AT_UTCis immutable after first assignment (proration anchor).LAST_LICENSED_AT_UTCis spell start (activated_at_utc). - Returns:
TABLE(ASSIGNED_USER, STATUS, FIRST_LICENSED_AT_UTC, LAST_LICENSED_AT_UTC, SEAT_CEILING, AUTO_ASSIGN_ENABLED) - Example:
CALL airlock.observe.licenses();
CALL airlock.observe.licenses(TRUE);
airlock.observe.license_policy()- Description: Read named-license state aligned with
set_license_ceiling: one row withseat_ceiling,auto_assign_enabled,SEATS_ASSIGNED, andSEATS_FREE. Useset_license_auto_assign(BOOLEAN)to change auto-assign. Uselist_licenses()for per-user timestamps; uselist_licenses(TRUE)to include inactive holders. - Returns:
TABLE(SEAT_CEILING, AUTO_ASSIGN_ENABLED, SEATS_ASSIGNED, SEATS_FREE) - Example:
CALL airlock.observe.license_policy();
airlock.admin.set_license_ceiling(new_ceiling NUMBER)- Description: Sets the finite license ceiling (
1to100). Decrease is blocked if active assignments exceed the new ceiling. - Returns:
TABLE(STATUS, CODE, MESSAGE, ISSUES). Branch on stableCODEvalues such asSEAT_CEILING_SET,SEAT_CEILING_MAX, orACTIVE_LICENSES_EXCEED_CEILING;ISSUESis empty on success. - Example:
CALL airlock.admin.set_license_ceiling(10);
airlock.admin.assign_license(user_name VARCHAR)- Description: Activates a named license for the user (
license_holders). Fails if capacity is full. - Returns:
TABLE(STATUS, CODE, MESSAGE, ISSUES). Branch on stableCODEvalues such asLICENSE_ASSIGNED,LICENSE_ALREADY_ASSIGNED, orNO_LICENSE_CAPACITY;ISSUESis empty on success. - Example:
CALL airlock.admin.assign_license('jane.doe');
airlock.admin.unassign_license(user_name VARCHAR)- Description: Deactivates the named license for that user (
is_active = FALSE; row andfirst_licensed_atretained). - Returns:
TABLE(STATUS, CODE, MESSAGE, ISSUES). Branch on stableCODEvalues such asLICENSE_UNASSIGNED,USER_NAME_REQUIRED, orLICENSE_NOT_ACTIVE;ISSUESis empty on success. - Example:
CALL airlock.admin.unassign_license('CSMITH');
airlock.admin.set_license_auto_assign(enabled BOOLEAN)- Description: Enables or disables auto-assignment for gated
agent.*and Streamlit seat checks. - Returns:
TABLE(STATUS, CODE, MESSAGE, ISSUES). Branch on stableCODEvalues such asAUTO_ASSIGN_SETorENABLED_REQUIRED;ISSUESis empty on success. - Example:
CALL airlock.admin.set_license_auto_assign(TRUE);
airlock.admin.emit_billing(max_batches FLOAT DEFAULT 1, trigger_kind VARCHAR DEFAULT 'manual')- Description: Sends unbilled named-seat month rows to Snowflake Marketplace billing using
SYSTEM$CREATE_BILLING_EVENTS. Candidates come from license spell overlap with UTC calendar months plus seat-assignment events, minus successful or claimed rows incore.billing_emit_log. Read-only billing posture, line items, events, run context, and activity are exposed throughairlock.observe.billing*. - Parameters:
max_batcheslimits emit batches for one call;trigger_kindidentifies the source (manual,scheduled_task, or another operator-defined label). - Returns:
TABLE(STATUS, CODE, MESSAGE, EVENTS_EMITTED, ISSUES). Branch on stableCODEvalues such asBILLING_EMIT_SENT,BILLING_EMIT_NO_PENDING, orBILLING_EMIT_FAILED;ISSUESis empty on success. - Examples:
CALL airlock.admin.emit_billing();
CALL airlock.admin.emit_billing(3);
CALL airlock.admin.emit_billing(1, 'scheduled_task');
CALL airlock.observe.billing_activity();
#2.2. Roles & Assignments
airlock.observe.roles(username VARCHAR DEFAULT NULL)- Description: Lists all in‑app roles, optionally filtered by user.
- Parameters:
username(optional): return roles assigned to this user; ifNULL, return all roles.- Returns:
ROLE_NAME VARCHARDESCRIPTION VARCHARMANAGED_BY_ROLE VARCHARIS_SPEC_ADMIN BOOLEANIS_LOCKED BOOLEAN- Example:
CALL airlock.observe.roles();
CALL airlock.observe.roles('jane.doe');
airlock.observe.role(role_name VARCHAR)- Description: Returns a canonical descriptor for a single in‑app role (round‑trippable for create/alter).
- Parameters:
role_name(required): in‑app role name to describe.- Returns:
ROLE_NAME VARCHARDESCRIPTION VARCHARMANAGED_BY_ROLE VARCHARIS_SPEC_ADMIN BOOLEANIS_LOCKED BOOLEAN- Example:
CALL airlock.observe.role('budget_analyst');
airlock.admin.create_roles(role_descriptors VARIANT, validate_only BOOLEAN DEFAULT FALSE)- Description: Creates one or more in‑app roles from an array of
role_descriptorobjects. For a single role, pass a one-element array. Handles dependency resolution automatically: roles withmanaged_by_roledependencies are created in topological order (parents before children) to respect FK constraints. - Parameters:
role_descriptors(required): array of role descriptor objects, each withrole_name(required); optional:description,managed_by_role(role name or PK; must not beapp_admin—every role is managed by app_admin—orglobal_observer; omit for top-level roles),is_spec_admin,is_locked. The case-insensitive role nameglobal_observeris system-reserved.validate_only(optional, defaultFALSE): whenTRUE, validates dependency order and returns creation plan without inserting.- Returns:
- One row per role:
ROLE_NAME VARCHAR,STATUS VARCHAR,IS_CREATED BOOLEAN,VALIDATION VARIANT. VALIDATIONis an object (not a bare boolean):outcome(created\|noop\|validate_only\|error),message, andissues(array of{ code, message, severity }, same general shape asload_dataISSUES). Idempotent re-create:outcome: "noop",IS_CREATED: FALSE, and an informational issue such asROLE_ALREADY_EXISTS. Withvalidate_only,dependency_levelis included;issuesis typically empty.- Behavior:
- Validates that all
managed_by_rolereferences are resolvable (exist in database or in the same create request). - Detects circular dependencies and returns validation errors.
- Creates roles in dependency order using topological sort (Kahn's algorithm).
- Example:
-- Single role
CALL airlock.admin.create_roles([PARSE_JSON('{"role_name": "analyst", "description": "Analyst role"}')]);
-- Multiple roles (with dependency): one PARSE_JSON per element
CALL airlock.admin.create_roles([
PARSE_JSON('{"role_name": "finadmin", "description": "Financial admin", "is_spec_admin": true}'),
PARSE_JSON('{"role_name": "budget_analyst", "description": "Budget analyst", "managed_by_role": "finadmin"}')
]);
airlock.admin.alter_role(role_name VARCHAR, role_descriptor VARIANT, validate_only BOOLEAN DEFAULT FALSE)- Description: Alters an existing in‑app role.
role_descriptor.role_namemust matchrole_nameif set. The reservedglobal_observersystem role cannot be altered. - Parameters:
role_name(required): role to alter.role_descriptor(required): optional fieldsdescription,managed_by_role(must not beapp_admin; omit for top-level),is_spec_admin,is_locked.validate_only(optional, defaultFALSE).- Returns:
ROLE_NAME VARCHAR,STATUS VARCHAR,IS_UPDATED BOOLEAN,VALIDATION VARIANT- Example:
CALL airlock.admin.alter_role('analyst', PARSE_JSON('{"description": "Updated"}'));
airlock.admin.drop_role(role_name VARCHAR, force BOOLEAN DEFAULT FALSE)- Description: Drops an in‑app role. Role must have no assignments and no child roles. When
forceisFALSE, does not delete if role is locked. The reservedglobal_observersystem role cannot be dropped, including withforce => TRUE. - Parameters:
role_name(required).force(optional, defaultFALSE): whenTRUE, allows drop when role is locked (dependents must still be zero).- Returns:
ROLE_NAME VARCHAR,STATUS VARCHAR,CODE VARCHAR,MESSAGE VARCHAR,IS_DROPPED BOOLEAN,ISSUES VARIANT,DETAILS VARIANT- Branch on
STATUSand stableCODE;ISSUESis an array of{code, message, severity}and is empty whenSTATUSisok. - Example:
CALL airlock.admin.drop_role('analyst');
CALL airlock.admin.drop_role('analyst', TRUE);
airlock.observe.assignments(username VARCHAR DEFAULT NULL, role_name VARCHAR DEFAULT NULL)- Description: Lists in‑app role assignments.
- Parameters:
username(optional): filter by user identifier.rolename(optional): filter by role name.- Returns:
ASSIGNMENT_NAME VARCHARDESCRIPTION VARCHARUSER_ID VARCHARASSIGNED_ROLE VARCHAREFFECTIVE_FROM TIMESTAMP_LTZEFFECTIVE_TO TIMESTAMP_LTZIS_LOCKED BOOLEAN- Example:
CALL airlock.observe.assignments();
CALL airlock.observe.assignments('jane.doe');
CALL airlock.observe.assignments(role_name => 'budget_analyst');
airlock.observe.observers(user_id VARCHAR DEFAULT NULL, observer_role VARCHAR DEFAULT NULL, spec_name VARCHAR DEFAULT NULL, include_inactive BOOLEAN DEFAULT FALSE)- Description: Returns an account-wide, flattened observer inventory. Each scoped row binds a user assignment and effective observer role to a spec plus optional path/workflow restrictions.
global_observerassignments appear as explicit global rows with no spec. Requiresapp_adminor an assignment toglobal_observer. - Parameters: Optional
user_id, effectiveobserver_role, andspec_namefilters.include_inactive => TRUEincludes expired/future assignments and observer grants on unpublished or archived specs. - Returns:
USER_ID,ASSIGNMENT_NAME,ASSIGNED_ROLE,OBSERVER_ROLE,SPEC_NAME,PATH_FILTER,WORKFLOW_STATE_FILTER,EFFECTIVE_FROM,EFFECTIVE_TO,IS_CURRENT,IS_GLOBAL,HAS_ACTIVE_LICENSE,HAS_AGENT_ACCESS, andBILLING_POSTURE. - Billing posture:
free_observer_only: no active named seat and no current agent access.active_seat_observer_only: only observer access is present, but an existing active seat remains billable until explicitly unassigned.active_agent_seat: the identity has agent access and an active seat.seat_required_for_agent_use: the identity has agent access but has not yet claimed an active seat.- Examples:
CALL airlock.observe.observers();
CALL airlock.observe.observers(observer_role => 'finance_observer');
CALL airlock.observe.observers(user_id => 'CSMITH', include_inactive => TRUE);
airlock.observe.assignment(assignment_name VARCHAR)- Description: Returns a canonical descriptor for a single in‑app assignment (round‑trippable for alter).
ASSIGNED_ROLEis the role name. - Parameters:
assignment_name(required). - Returns: Same columns as list (single row or empty).
- Example:
CALL airlock.observe.assignment('jane.analyst');
airlock.admin.create_assignments(assignment_descriptors VARIANT, validate_only BOOLEAN DEFAULT FALSE)- Description: Creates one or more in‑app role assignments from an array of
assignment_descriptorobjects. For a single assignment, pass a one-element array. Validates that allassigned_rolereferences exist before creating any assignments. Assignments don't have dependencies on each other, so all are created in parallel after validation. Successful create, alter, and drop operations write assignment identity, effective role, changed fields, and before/after evidence into admin activity context so deleted assignments remain auditable. - Parameters:
assignment_descriptors(required): array of assignment descriptor objects, each withassignment_name,user_id,assigned_role(required); optional:description,effective_from,effective_to,is_locked.assigned_rolecan be role name or PK.validate_only(optional, defaultFALSE): whenTRUE, validates all assignments and returns validation results without inserting.- Returns:
- One row per assignment:
ASSIGNMENT_NAME VARCHAR,STATUS VARCHAR,IS_CREATED BOOLEAN,VALIDATION VARIANT - Behavior:
- Validates that all
assigned_rolereferences exist incore.in_app_roles. - Checks for duplicate
assignment_namevalues (both in input and existing). - Checks for duplicate
(user_id, assigned_role)combinations (both in input and existing). - Creates all assignments after validation passes.
- Example:
-- Single assignment: one PARSE_JSON object inside an array literal (easy to read and edit).
CALL airlock.admin.create_assignments([
PARSE_JSON('{"assignment_name": "jane.analyst", "user_id": "JANE", "assigned_role": "analyst"}')
]);
-- Multiple assignments: one PARSE_JSON per element.
CALL airlock.admin.create_assignments([
PARSE_JSON('{"assignment_name": "alice_finadmin", "user_id": "alice@example.com", "assigned_role": "finadmin"}'),
PARSE_JSON('{"assignment_name": "bob_analyst", "user_id": "bob@example.com", "assigned_role": "budget_analyst"}')
]);
-- When a field must be a SQL expression (e.g. CURRENT_USER(), timestamps), use ARRAY_CONSTRUCT(OBJECT_CONSTRUCT(...)) instead.
airlock.admin.alter_assignment(assignment_name VARCHAR, assignment_descriptor VARIANT, validate_only BOOLEAN DEFAULT FALSE)- Description: Alters an existing assignment (description, effective_from, effective_to, is_locked only).
- Parameters:
assignment_name(required).assignment_descriptor(required): optional fieldsdescription,effective_from,effective_to,is_locked.validate_only(optional, defaultFALSE).- Returns:
ASSIGNMENT_NAME VARCHAR,STATUS VARCHAR,IS_UPDATED BOOLEAN,VALIDATION VARIANT- Example:
CALL airlock.admin.alter_assignment('jane.analyst', PARSE_JSON('{"description": "Updated", "effective_to": "2025-12-31 23:59:59"}'));
airlock.admin.drop_assignment(assignment_name VARCHAR, force BOOLEAN DEFAULT FALSE)- Description: Drops an in‑app role assignment. When
forceisFALSE, does not delete if assignment is locked. - Parameters:
assignment_name(required).force(optional, defaultFALSE): whenTRUE, allows drop when assignment is locked.- Returns:
ASSIGNMENT_NAME VARCHAR,STATUS VARCHAR,CODE VARCHAR,MESSAGE VARCHAR,IS_DROPPED BOOLEAN,ISSUES VARIANT,DETAILS VARIANT- Branch on
STATUSand stableCODE;ISSUESis an array of{code, message, severity}and is empty whenSTATUSisok. - Example:
CALL airlock.admin.drop_assignment('jane.analyst');
CALL airlock.admin.drop_assignment('jane.analyst', TRUE);
#2.3. Events
airlock.observe.activity(username VARCHAR DEFAULT NULL, event_types VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL, event_description VARCHAR DEFAULT NULL, start_time TIMESTAMP_LTZ DEFAULT NULL, end_time TIMESTAMP_LTZ DEFAULT NULL, limit_rows INTEGER DEFAULT 200)- Description: Fetches application activity events from the internal
core.eventstable with optional filters.app_observercallers receive only events within their assigned spec/path scope; workflow-filtered observer grants useobserve.files,observe.file_state, andobserve.workfor state-safe polling.app_adminand assignedglobal_observercallers receive account-wide activity. - Parameters:
username(optional): filter by username (case-insensitive exact match).event_types(optional): comma-separated list of event types (e.g.USER_LOGIN,RECORD_CREATE,FILE_UPLOAD). See event types in app.in_app_role(optional): filter by Airlock role name (case-insensitive exact match).event_description(optional): partial match on event description (case-insensitive).start_time(optional): earliestevent_timestamp(inclusive).end_time(optional): latestevent_timestamp(inclusive).limit_rows(optional, default 200): maximum rows to return; capped at 1000.- Returns:
EVENT_TIMESTAMP TIMESTAMP_LTZEVENT_TYPE VARCHAREVENT_DESCRIPTION VARCHARUSERNAME VARCHARIN_APP_ROLE VARCHAROBJECT_NAME VARCHAR– table or spec name when applicableSPEC_NAME VARCHAR– spec name when applicable (e.g. file events)EVENT_CONTEXT VARIANTEVENT_ID VARCHAR- trailing stable event identity; pass toobserve.activity_context- Example:
CALL airlock.observe.activity();
CALL airlock.observe.activity('jane.doe');
CALL airlock.observe.activity(
event_types => 'USER_LOGIN,RECORD_CREATE',
start_time => '2024-01-01'::TIMESTAMP_LTZ,
end_time => CURRENT_TIMESTAMP(),
limit_rows => 500
);
CALL airlock.observe.activity(in_app_role => 'spec_admin', limit_rows => 100);
airlock.observe.admin_activity(procedure_name VARCHAR DEFAULT NULL, action_family VARCHAR DEFAULT NULL, username VARCHAR DEFAULT NULL, event_types VARCHAR DEFAULT NULL, start_time TIMESTAMP_LTZ DEFAULT NULL, end_time TIMESTAMP_LTZ DEFAULT NULL, limit_rows INTEGER DEFAULT 200)- Description: Fetches admin mutation and maintenance activity from the append-only
core.ADMIN_ACTIVITYjournal, plus pre-journal or fallback admin events fromcore.events. Use this for audit/compliance questions such as "what did admins do?", "which billing actions ran?", or "which role/spec maintenance procedures changed governance?" - Parameters:
procedure_name(optional): short or schema-qualified admin procedure name, e.g.load_dataoradmin.load_data.action_family(optional): stable grouping such asroles,assignments,specs,files,attachments,workflow,billing,licenses,events,references,retention,expectations,okf, ormaintenance.username(optional): filter by event username.event_types(optional): comma-separated list of event types.start_time,end_time(optional): inclusive activity timestamp range.limit_rows(optional, default 200): maximum rows to return; capped at 1000.- Returns:
EVENT_TIMESTAMP TIMESTAMP_LTZEVENT_TYPE VARCHAREVENT_DESCRIPTION VARCHARUSERNAME VARCHARIN_APP_ROLE VARCHARPROCEDURE_NAME VARCHARACTION_FAMILY VARCHAROBJECT_NAME VARCHARSPEC_NAME VARCHAREVENT_CONTEXT VARIANTEVENT_ID VARCHAR- trailing stable activity identityEVIDENCE_SOURCE VARCHAR- trailing provenance:journalfor canonical append-only evidence;legacy_eventfor unmatched pre-journal or fallback evidence- Examples:
CALL airlock.observe.admin_activity();
CALL airlock.observe.admin_activity(procedure_name => 'admin.create_assignments');
CALL airlock.observe.admin_activity(action_family => 'billing', event_types => 'BILLING_EMIT');
airlock.observe.spec_admin_activity(spec_name VARCHAR, event_types VARCHAR DEFAULT NULL, start_time TIMESTAMP_LTZ DEFAULT NULL, end_time TIMESTAMP_LTZ DEFAULT NULL, limit_rows INTEGER DEFAULT 200)- Description: Fetches admin spec-definition events for one
spec_name, limited to structured admin events whoseaction_familyisspecs. Use this for questions like "who changed this spec?", "when was the column config updated?", or "which version id should I inspect?" For newalter_specevents,CHANGED_SECTIONSlists changed top-level spec_config sections andCHANGED_FIELDSlists changed corecore.specsfields. - Parameters:
spec_name(required): logical Airlock spec name.event_types(optional): comma-separated event types such asRECORD_CREATE,RECORD_UPDATE, orRECORD_DELETE.start_time,end_time(optional): inclusive activity timestamp range.limit_rows(optional, default 200): maximum rows to return; capped at 1000.- Returns:
EVENT_TIMESTAMP TIMESTAMP_LTZEVENT_TYPE VARCHAREVENT_DESCRIPTION VARCHARUSERNAME VARCHARPROCEDURE_NAME VARCHARSPEC_EVENT VARCHARSPEC_NAME VARCHARCURRENT_CREATED_AT TIMESTAMP_LTZCURRENT_CREATED_BY VARCHARCURRENT_MODIFIED_AT TIMESTAMP_LTZCURRENT_MODIFIED_BY VARCHARVERSION_ID VARCHARVERSION_PATH VARCHARMETA_PATH VARCHARSPEC_CONFIG_HASH VARCHARCHANGED_SECTIONS VARIANTCHANGED_FIELDS VARIANTEVENT_CONTEXT VARIANTEVENT_ID VARCHAR- trailing stable activity identityEVIDENCE_SOURCE VARCHAR- trailing provenance:journalorlegacy_event- Examples:
CALL airlock.observe.spec_admin_activity('budget_requests');
CALL airlock.observe.spec_admin_activity('budget_requests', event_types => 'RECORD_UPDATE');
CALL airlock.observe.spec_version('budget_requests', '<VERSION_ID_FROM_ACTIVITY>');
#2.4. Specs & Files
airlock.observe.specs(in_app_role VARCHAR DEFAULT NULL)- Description: Lists specs visible through the caller's active Airlock role assignments;
app_adminand assignedglobal_observercallers see the account-wide inventory. Supports an optional Airlock role lens. - Parameters:
in_app_role(optional): restrict results to access through this Airlock role;NULLuses all assigned roles, or all specs for account-wide observers.- Returns:
SPEC_NAME VARCHARSPEC_ALIAS VARCHARDESCRIPTION VARCHAROWNER_ROLE VARCHARACCESS_TYPE VARCHAR–owner,guest,observer, oradminGUEST_ROLE_NAME VARCHARRESTRICTED_PATH VARCHARIS_MULTI_ROLE BOOLEAN- Example:
CALL airlock.observe.specs();
CALL airlock.observe.specs('budget_analyst');
airlock.observe.spec(spec_name VARCHAR)- Description: Returns a canonical descriptor for a single spec, suitable for programmatic use.
- Parameters:
spec_name(required): logical spec identifier.- Returns:
SPEC_NAME VARCHARSPEC_ALIAS VARCHARDESCRIPTION VARCHAROWNER_ROLE VARCHAR– human‑readable role nameSPEC_CONFIG VARIANT– full configuration objectIS_LOCKED BOOLEANIS_ARCHIVED BOOLEANIS_MULTI_ROLE BOOLEANIS_PUBLISHED BOOLEANIS_REFERENCE_SPEC BOOLEANACTIVE_SPEC_VERSION_ID VARCHAR– immutable complete revision currently active.ACTIVE_DATA_VERSION_ID VARCHAR– immutable data contract used for new validation.ACTIVE_SPEC_CONFIG_HASH VARCHAR– canonical hash of the active complete revision.MIGRATION_SOURCE_SPEC_VERSION_ID VARCHAR– optional read-only source revision being drained.MIGRATION_SOURCE_DATA_VERSION_ID VARCHAR– optional second operational data version.ACTIVE_MIGRATION_ID VARCHAR– migration that owns the temporary two-version window.ACCESS_SUMMARY VARIANT– compact explanation of owner, guest, reviewer, observer, attachment, and reference RLS policy- Example:
CALL airlock.observe.spec('budget_requests');
airlock.observe.spec_state(spec_name VARCHAR)- Description: Returns compact, authorization-scoped change tokens for one active spec without returning governed file rows or attachment bytes. Use this for observer polling and retrieve larger observe payloads only when a token changes.
- Returns:
SPEC_NAME, immutable active spec/data version identifiers and config hash,CONTRACT_TOKEN, authorization-scopedACCESS_SCOPE_TOKEN,DATASET_TOKEN, combinedSTATE_TOKEN,ACTIVE_FILE_COUNT, andLAST_CHANGED_AT. - Token semantics:
CONTRACT_TOKENchanges when the active spec contract changes.DATASET_TOKENchanges when the visible active file set, a visible file's mutable UUID, or a spec-level attachment changes.STATE_TOKENcombines those signals with spec metadata changes. Account-wide observers also track the spec revision epoch; scoped observers intentionally ignore epoch changes caused only by hidden paths. A dropped spec or revoked observer grant returns no row; use governance activity for durable evidence. Failures after authorization still surface as procedure errors. - Example:
CALL airlock.observe.spec_state('budget_requests');
airlock.observe.spec_states(spec_names ARRAY)- Description: Returns the same state-token rows as
observe.spec_statefor up to 100 unique, non-empty names while sharing observer identity, role expansion, and compiled-access work. Use it when one observer polls several known specs. - Authorization: Scoped observers receive only specs allowed by their assignments and per-spec observer policy.
global_observerandapp_adminreceive account-wide state. Input order is preserved after case-insensitive deduplication; missing and inaccessible specs are omitted without disclosing which condition applied. - Returns: Zero or one row per accessible requested spec, with the same columns and token semantics as
observe.spec_state. - Example:
CALL airlock.observe.spec_states(ARRAY_CONSTRUCT('budget_requests', 'finance'));
airlock.observe.spec_compatibility(spec_name VARCHAR, proposed_spec_config VARIANT)- Description: Performs a read-only structured comparison of a proposed canonical config with the active spec. It reports data-contract changes, directional compatibility, affected active-file count, and whether
admin.alter_specwould require a governed migration. - Parameters:
spec_nameand the complete proposedspec_configare required. - Returns:
SPEC_NAME,STATUS,CODE,MESSAGE,SOURCE_SPEC_VERSION_ID,SOURCE_DATA_VERSION_ID,PROPOSED_SPEC_CONFIG_HASH,ACTIVE_FILE_COUNT,CONTRACT_RELATION,DATA_CONTRACT_CHANGED,MIGRATION_REQUIRED, andDETAILS. - Example:
CALL airlock.observe.spec_compatibility('budget_requests', :proposed_spec_config);
airlock.observe.spec_config(spec_name VARCHAR)- Description: Returns the current spec configuration as VARIANT (native JSON object in the Snowflake UI), aligned with structured columns such as
load_dataISSUES. Use for round-trip editing or to pass intoalter_spec/validate_specwithallow_existing_spec => TRUE. - Parameters:
spec_name(required): logical spec identifier.- Returns:
VARIANT– parsed spec_config object (NULLif the spec does not exist). - Example:
CALL airlock.observe.spec_config('budget_requests');
-- For automation: snow sql -c myconn --format JSON_EXT --silent -q "CALL airlock.observe.spec_config('my_spec');"
airlock.admin.create_spec(spec_config VARIANT, validate_only BOOLEAN DEFAULT FALSE, reuse_spec BOOLEAN DEFAULT FALSE, from_assigned_spec_template BOOLEAN DEFAULT FALSE)- Description: Creates a new spec from a canonical
spec_configpayload. This is the primary CREATE procedure for specs. - Parameters:
spec_config(required): canonical spec configuration object (same structure used byairlock.admin.validate_spec).validate_only(optional, defaultFALSE): whenTRUE, runs validation and returns the result without inserting intocore.specs.reuse_spec(optional, defaultFALSE): whenTRUE, allowsspec_nameto already exist as a directory in AIRLOCK_STAGE (e.g. after dropping the spec fromcore.specs); validation is called withreuse_spec => TRUE.from_assigned_spec_template(optional, defaultFALSE): setTRUEonly byagent.create_spec_from_template; allows delegated owner rules whentemplate_config.template_rules.forced_owner_rolematches the resolved owner.- Returns:
SPEC_NAME VARCHAR– canonical spec name that would be (or was) created.STATUS VARCHAR–okorerror.IS_CREATED BOOLEAN–TRUEonly when a new row was successfully inserted.VALIDATION VARIANT– parsed JSON result fromvalidate_spec(includingvalidand any invalid tabs/messages).- Example:
CALL airlock.admin.create_spec(PARSE_JSON('{"core_config": {"spec_name": "my_spec", "owner_role": "analyst"}}'), TRUE);
CALL airlock.admin.create_spec(:spec_config_var);
airlock.admin.import_dbt_spec_definition(dbt_payload VARIANT, overrides VARIANT DEFAULT NULL)- Description: Imports a dbt spec definition as a pending Airlock spec. Produces a draft
spec_configper the dbt→Airlock mapping and writes a row tocore.specswithimport_metadata(status=pending,pending_fields,pending_notes,source_payload). dbt models are not supported (models are DAG-based; shape comes from SQL + ancestors). Complete the spec viaairlock.admin.alter_specwith a full config (and optionally removeimport_metadata). - Parameters:
dbt_payload(required): Parsed dbt specs YAML/JSON. Accepts:{ "name", "description", "columns": [...] }, or{ "specs": [ {...} ] }(first spec used).overrides(optional):{ "owner_role", "spec_alias", "description", "is_published", "is_archived", "is_multi_role", "current_mode" }. Ifowner_roleis omitted,app_adminis used andcore_config.owner_roleis added topending_fields.- Returns:
SPEC_NAME,STATUS,IS_CREATED,DRAFT_CONFIG VARIANT,PENDING_FIELDS VARIANT,PENDING_NOTES VARIANT,VALIDATION VARIANT.- Example:
CALL airlock.admin.import_dbt_spec_definition(
PARSE_JSON('{"name":"my_spec","columns":[{"name":"id","tests":["not_null"]}]}'),
PARSE_JSON('{"owner_role":"app_admin"}')
);
airlock.observe.spec_import(spec_name VARCHAR)- Description: Returns the same descriptor as
observe.specplusIMPORT_METADATA VARIANTwhen the spec was created viaimport_dbt_spec_definition. Use to inspectpending_fieldsandpending_notesbefore completing withalter_spec. - Parameters:
spec_name(required). - Returns: Same as
observe.specplusIMPORT_METADATA VARIANT(nullable).
airlock.observe.pending_specs()- Description: Lists specs in pending import state (
import_metadata.status=pending). - Returns:
SPEC_NAME,SPEC_ALIAS,IMPORTED_AT,PENDING_FIELDS,PENDING_NOTES.
airlock.admin.clone_spec_from_template(template_name VARCHAR, new_spec_name VARCHAR, spec_alias VARCHAR DEFAULT NULL, spec_config_overrides VARIANT DEFAULT NULL)- Description: Clones a new spec from a saved spec template. This calls
airlock.admin.create_specwith a clonedspec_config. - Parameters:
template_name(required): spec template to clone.new_spec_name(required): new spec name for the clone (canonicalized).spec_alias(optional): optional alias override for the new spec.spec_config_overrides(optional): optional overrides for editable fields (dot-path keys).- Returns:
SPEC_NAME VARCHAR– canonical spec name that would be (or was) created.STATUS VARCHAR–ok,not_found, orerror.IS_CREATED BOOLEAN–TRUEonly when a new row was successfully inserted.VALIDATION VARIANT– parsed JSON result fromvalidate_spec(includingvalidand any invalid tabs/messages).- Example:
CALL airlock.admin.clone_spec_from_template(
'finance_spec_template',
'fin_budget_requests',
spec_alias => 'FIN_Budget Requests',
spec_config_overrides => PARSE_JSON('{"core_config.description": "FY26"}')
);
airlock.observe.spec_templates()- Description: Lists all spec templates.
- Returns:
TEMPLATE_NAME VARCHARDESCRIPTION VARCHARIS_ACTIVE BOOLEANIS_LOCKED BOOLEANIS_PUBLIC BOOLEAN- Example:
CALL airlock.observe.spec_templates();
airlock.observe.spec_template(template_name VARCHAR)- Description: Returns a single spec template descriptor.
- Parameters:
template_name(required): template name.- Returns:
TEMPLATE_NAME VARCHARDESCRIPTION VARCHARSPEC_CONFIG VARIANTTEMPLATE_RULES VARIANTIS_ACTIVE BOOLEANIS_LOCKED BOOLEANIS_PUBLIC BOOLEAN- Example:
CALL airlock.observe.spec_template('finance_spec_template');
airlock.admin.create_spec_template(template_descriptor VARIANT, validate_only BOOLEAN DEFAULT FALSE)- Description: Creates a new spec template.
- Parameters:
template_descriptor(required):{ template_name, description?, spec_config?, template_rules?, is_locked?, is_active?, is_public? }.is_public: truemakes the template available to every licensedapp_agent.validate_only(optional, defaultFALSE): whenTRUE, validates without inserting.- Returns:
TEMPLATE_NAME VARCHARSTATUS VARCHARIS_CREATED BOOLEANVALIDATION VARIANT- Example:
CALL airlock.admin.create_spec_template(PARSE_JSON('{"template_name": "finance_spec_template"}'));
airlock.admin.alter_spec_template(template_name VARCHAR, template_descriptor VARIANT, validate_only BOOLEAN DEFAULT FALSE)- Description: Alters an existing spec template.
- Parameters:
template_name(required): template name.template_descriptor(required):{ description?, spec_config?, template_rules?, is_locked?, is_active?, is_public? }.validate_only(optional, defaultFALSE): whenTRUE, validates without updating.- Returns:
TEMPLATE_NAME VARCHARSTATUS VARCHARIS_UPDATED BOOLEANVALIDATION VARIANT- Example:
CALL airlock.admin.alter_spec_template('finance_spec_template', PARSE_JSON('{"is_active": false}'));
airlock.admin.drop_spec_template(template_name VARCHAR, force BOOLEAN DEFAULT FALSE)- Description: Drops a spec template (blocked if locked or assigned).
- Parameters:
template_name(required): template name.force(optional, defaultFALSE): whenTRUE, allows dropping locked templates.- Returns:
TEMPLATE_NAME VARCHARSTATUS VARCHARIS_DROPPED BOOLEANMESSAGE VARCHAR- Example:
CALL airlock.admin.drop_spec_template('finance_spec_template');
airlock.observe.spec_template_assignments(template_name VARCHAR DEFAULT NULL)- Description: Lists spec template assignments.
- Parameters:
template_name(optional): filter by template name.- Returns:
ASSIGNMENT_NAME VARCHARTEMPLATE_NAME VARCHARASSIGNED_ROLE VARCHARASSIGNED_ROLE_NAME VARCHARDESCRIPTION VARCHAREFFECTIVE_FROM TIMESTAMP_LTZEFFECTIVE_TO TIMESTAMP_LTZIS_LOCKED BOOLEAN- Example:
CALL airlock.observe.spec_template_assignments();
airlock.admin.create_spec_template_assignment(assignment_descriptor VARIANT, validate_only BOOLEAN DEFAULT FALSE)- Description: Creates a spec template assignment.
- Parameters:
assignment_descriptor(required):{ assignment_name, template_name, assigned_role, description?, effective_from?, effective_to?, is_locked? }.validate_only(optional, defaultFALSE): whenTRUE, validates without inserting.- Returns:
ASSIGNMENT_NAME VARCHARSTATUS VARCHARIS_CREATED BOOLEANVALIDATION VARIANT- Example:
CALL airlock.admin.create_spec_template_assignment(
PARSE_JSON('{"assignment_name": "fin_template_access", "template_name": "finance_spec_template", "assigned_role": "fin_analyst"}')
);
airlock.admin.drop_spec_template_assignment(assignment_name VARCHAR, force BOOLEAN DEFAULT FALSE)- Description: Drops a spec template assignment.
- Parameters:
assignment_name(required): assignment name.force(optional, defaultFALSE): whenTRUE, allows dropping locked assignments.- Returns:
ASSIGNMENT_NAME VARCHARSTATUS VARCHARIS_DROPPED BOOLEANMESSAGE VARCHAR- Example:
CALL airlock.admin.drop_spec_template_assignment('fin_template_access');
airlock.admin.alter_spec(spec_name VARCHAR, spec_config VARIANT, validate_only BOOLEAN DEFAULT FALSE)- Description: Alters an existing spec’s configuration using a canonical
spec_configpayload. Validation-only calls include structured compatibility analysis. Metadata/governance revisions retain the active data version and remain available during a migration. A data-contract change is rejected withSPEC_MIGRATION_REQUIREDwhile active files still depend on the current data version, or withSPEC_MIGRATION_ACTIVEuntil the current migration is retired. Activation is conditional on the version that was analyzed; a concurrent spec change or file load returnsSPEC_ACTIVATION_CONFLICTwithout activating the revision. Also used to complete a pending dbt import: fetch config viaobserve.spec_importorobserve.spec_config, fill required fields, optionally remove or set import_metadata.status to complete, then call alter_spec. - Parameters:
spec_name(required): logical spec identifier of the spec to update.spec_config(required): canonical spec configuration object (same structure used byairlock.admin.validate_spec). The canonicalspec_config.core_config.spec_namemust matchspec_name.validate_only(optional, defaultFALSE): whenTRUE, runs validation and returns the result without updatingcore.specs.- Returns:
SPEC_NAME VARCHAR– canonical spec name that was (or would be) updated.STATUS VARCHAR–ok,not_found, orerror.IS_UPDATED BOOLEAN–TRUEonly when an existing row was successfully updated.VALIDATION VARIANT– validation result pluscompatibility, including active-file impact and migration requirements.- Example:
CALL airlock.admin.alter_spec('my_spec', :spec_config_var);
CALL airlock.admin.alter_spec('my_spec', :spec_config_var, TRUE);
#Spec revisions and governed migration
Airlock separates proposal, proof, approval, activation, bounded execution, and retirement. Approval does not change live behavior. Activation makes the target the sole write version and retains one prior read-only source version while current reads continue to expose the target shape.
AIRLOCK_DATA.ACTIVE.V_<SPEC> and AIRLOCK_DATA.HIST.V_<SPEC> read only rows with a valid attestation for the current data version. During an active migration, the history view also exposes source-version rows through the same approved target-shape transform and suppresses source rows that already have a physical successor. After retirement, older source-native artifacts remain inspectable through observe.file_versions, observe.file_validations, and exact governed file access; Airlock does not reinterpret them with a newer schema merely to keep them in a convenience view.
airlock.admin.create_spec_revision(spec_name VARCHAR, spec_config VARIANT)- Writes an immutable proposed revision whose metadata pins the active parent spec/data versions and version epoch.
- Assigns server-managed IDs to new columns and returns static compatibility.
- Does not update
core.specsorspec_configs/<spec>/latest.json.
airlock.admin.create_spec_migration(spec_name VARCHAR, source_spec_version_id VARCHAR, target_spec_version_id VARCHAR, transform_config VARIANT, reversibility VARCHAR DEFAULT 'source_preserved')- Creates one durable draft migration from the active revision to its immutable child proposal.
- Accepts only bounded operations over immutable
column_idvalues:preserve,rename,drop,add_nullable,add_constant,safe_cast,coalesce, andmap_values. - Rejects arbitrary SQL, Python, prompt text, unknown fields, incomplete column mappings, and revision/hash drift.
source_preservedprotects transformed source bytes from ordinary retention purge so the declared rollback evidence remains available.forward_onlyleaves inactive source bytes under the normal retention policy.- Example:
CALL airlock.admin.create_spec_migration(
'budget_requests',
'20260701010101',
'20260702020202',
PARSE_JSON('{
"operations": [{
"op": "safe_cast",
"source_column_id": "col_222222222222222222222222",
"target_column_id": "col_222222222222222222222222",
"on_failure": "reject"
}]
}'),
'source_preserved'
);
airlock.admin.validate_spec_migration(migration_id VARCHAR, preview_limit INTEGER DEFAULT 25)- Pins every active file to an exact source validation attestation and content hash, then runs one set-based, read-only proof over all source rows.
- Returns source counts, a source-set fingerprint, aggregate failures, and up to 100 transformed preview rows.
- Rechecks the source-set fingerprint before recording
validated. - Returns
MIGRATION_VALIDATOR_UNSUPPORTEDfor changed rule families that do not yet have a set-based migration validator instead of claiming safety. - The current proof engine supports Airlock's tabular CSV storage contract; specs using non-tabular payload adapters such as
okf_knowledge_bundlefail closed withMIGRATION_VALIDATOR_UNSUPPORTED.
airlock.admin.approve_spec_migration(migration_id VARCHAR)- Approves only a validated migration whose active source revision and exact source-set fingerprint still match the proof.
- Records approval intent only; it does not activate the target revision.
airlock.admin.activate_spec_migration(migration_id VARCHAR)- Rechecks immutable source/target revisions, transform identity, exact source set, and current staged content before activation.
- Resolves
core_config.owner_roleto its durable Airlock role ID before writing the target spec, so custom-owned specs activate through the same owner contract ascreate_specandalter_spec. - Makes the target data version current for new validation and writes, pins the prior version as the one read-only migration source, and rebuilds the active view as a target-shaped union.
- Atomically invalidates the old compiled access version, rebuilds target views and any materialized table, then republishes the compiled access index. Stale views/tables are removed instead of serving old-shape data.
- If a post-commit repair fails, the migration remains durably
runningand returnsSPEC_MIGRATION_ACTIVATED_WITH_REPAIR_REQUIREDwith stable issues. Failed access-index repair leaves agent access disabled untiladmin.rebuild_access_index(TRUE)succeeds; failed view/table repair usesadmin.rebuild_spec_view(...)oradmin.rebuild_spec_table(...)before rebuilding access. - Rejects activation when a spec already has two operational versions.
airlock.admin.run_spec_migration(migration_id VARCHAR, batch_size INTEGER DEFAULT 25)- Drains 1 to 100 planned or retryable file items per call without invoking a stored procedure once per row.
- Atomically claims each bounded batch before reading or copying staged bytes; concurrent runners cannot execute the same live item, and abandoned claims become retryable after a bounded stale-claim interval.
- Records a target-version attestation against the existing manifest when the approved transform is byte-compatible. A column rename is not zero-copy because the CSV header must change. Other transforms use one set-based
COPY INTOper file and atomically activates a target-version successor with exact source manifest, validation, content-hash, workflow, and source-reference lineage. - Preserves original validation provenance, uses append-only valid attestations to identify the effective source contract across sequential migrations, verifies staged input and output digests, and leaves failed items retryable without partially replacing the active source file.
- Returns
ZERO_COPY_COUNT,TRANSFORMED_COUNT,FAILED_COUNT, andREMAINING_COUNT; the migration becomescompletedonly after no active file still requires the source contract.
airlock.admin.retire_spec_migration(migration_id VARCHAR)- Accepts only a
completedmigration and rechecks that every item is terminal and every active file has a valid target-data-version attestation. - Clears the read-only source and active migration pointers, rebuilds the current view without the source branch, and releases the spec for a future migration.
- Retains immutable source revisions, original file bytes and manifest rows, validation attestations, migration evidence, and lineage for audit.
airlock.admin.cancel_spec_migration(migration_id VARCHAR)- Cancels a nonterminal migration and releases the spec's one-migration slot.
- Does not change active versions or stored bytes.
airlock.observe.spec_migrations(spec_name VARCHAR DEFAULT NULL, migration_status VARCHAR DEFAULT NULL, max_rows INTEGER DEFAULT 200)- Lists migration lifecycle state, immutable version pointers, proof counts, zero-copy/transformed/superseded/retry counts, and validation, approval, activation, completion, and retirement actors.
airlock.observe.spec_migration(migration_id VARCHAR)- Returns an AI-oriented detail packet with the bounded transform, compatibility relation, validation summary, retry evidence, and exact source-to-target manifest, attestation, and content-hash lineage for up to 200 source items.
- Includes
source_items_total,source_items_returned, andsource_items_truncatedso callers know when to page exact evidence. - Does not expose governed file contents.
airlock.observe.spec_migration_items(migration_id VARCHAR, item_status VARCHAR DEFAULT NULL, after_file_manifest_pk VARCHAR DEFAULT NULL, max_rows INTEGER DEFAULT 200)- Pages exact migration-item lineage in stable
file_manifest_pkorder, up to 500 rows per call. - When
has_moreis true, pass the final returnedfile_manifest_pkasafter_file_manifest_pkon the next call. - Exposes hashes, validation IDs, target lineage, attempts, and status without governed file contents or worker claim tokens.
An opt-in repository proof exercises the whole public lifecycle without direct table writes:
make demo-spec-migration SNOWFLAKE_CONN=<admin-connection>
airlock.observe.spec_versions(spec_name VARCHAR)- Description: Lists versioned spec_config metadata stored in AIRLOCK_STAGE.
- Parameters:
spec_name(required): logical spec identifier.- Returns:
SPEC_NAME VARCHARVERSION_ID VARCHARSTATUS VARCHARIS_LATEST BOOLEANSPEC_CONFIG_HASH VARCHARMETA VARIANT- Example:
CALL airlock.observe.spec_versions('budget_requests');
airlock.observe.spec_version(spec_name VARCHAR, version_id VARCHAR)- Description: Fetches a specific versioned spec_config and metadata from AIRLOCK_STAGE.
- Parameters:
spec_name(required): logical spec identifier.version_id(required): version identifier (YYYYMMDDHHMMSSorYYYYMMDDHHMMSS-<8 char uuid>).- Returns:
SPEC_NAME VARCHARVERSION_ID VARCHARSTATUS VARCHARSPEC_CONFIG VARIANTMETA VARIANT- Example:
CALL airlock.observe.spec_version('budget_requests', '20260315123045');
airlock.admin.backfill_spec_versions(spec_name VARCHAR DEFAULT NULL)- Description: Initializes exact active spec/data version pointers and canonical hashes for legacy specs. It creates a bootstrap revision from the current durable config and deliberately leaves legacy file validation provenance
unknown; it never guesses which old stage snapshot validated a file. Ambiguous legacy column names or identities fail closed and must be corrected before provenance can be initialized. Retryable concurrent-change conflicts are reported asBACKFILL_CONFLICT; install/upgrade setup treats them as failures rather than reporting incomplete initialization as successful. - Parameters:
spec_name(optional): limit to a single spec; default is all specs.- Returns:
SPEC_NAME VARCHARSTATUS VARCHARVERSION_ID VARCHARDETAILS VARIANT- Example:
CALL airlock.admin.backfill_spec_versions();
CALL airlock.admin.backfill_spec_versions('budget_requests');
airlock.admin.drop_spec(spec_name VARCHAR)- Description: Deletes the spec row from
core.specs(actual drop). Archiving a spec is done viaalter_spec(setis_archivedin spec_config). Related files in stage, manifest rows, and views are not deleted; clean them up separately if needed. - Cancels and retains any nonterminal migration history before releasing the spec's active migration slot.
- Parameters:
spec_name(required): logical spec identifier.- Returns:
SPEC_NAME VARCHARSTATUS VARCHAR–dropped,not_found, orerrorIS_DROPPED BOOLEAN–TRUEwhen the row was deletedMESSAGE VARCHAR– human‑readable description of the outcomeISSUES VARIANT– array of{code, message, severity}(same shape asload_dataISSUES); empty whenSTATUSisdropped- Example:
CALL airlock.admin.drop_spec('budget_requests');
airlock.observe.files(spec_name VARCHAR, in_app_role VARCHAR DEFAULT NULL)- Description: Lists files in a spec’s stage directory.
- Parameters:
spec_name(required): spec to list files from.in_app_role(optional): restrict to a specific role directory for multi‑role specs.- Returns: Snowflake
LISToutput: name VARCHARsize NUMBERmd5 VARCHARlast_modified TIMESTAMP- On normal failure:
STATUS,CODE,MESSAGE,ISSUES - Example:
CALL airlock.observe.files('budget_requests');
CALL airlock.observe.files('budget_requests', 'budget_analyst');
airlock.observe.work(spec_name VARCHAR DEFAULT NULL)- Description: Read-only, assignment-scoped projection of current workflow and expectation work. Workflow rows include actionable and observed files; expectation rows include unmet, upcoming, overdue, and excepted expectations. Met expectations are omitted because they are no longer work.
AVAILABLE_ACTIONSdescribes actions available to an authorized agent and does not grant the observer permission to perform them.app_adminand assignedglobal_observercallers receive the account-wide projection. - Returns:
WORK_ITEM_ID,SPEC_NAME,WORK_SOURCE,WORK_ITEM_TYPE,WORK_STATUS,SUMMARY,PATH,FILENAME,DUE_AT,DAYS_UNTIL_DUE,AVAILABLE_ACTIONS,ACCESS_REASON,IS_STRICT,UPDATED_AT,DETAILS. Read-only file visibility usesWORK_ITEM_TYPE = 'observe_visible'andACCESS_REASON = 'observer'. - Example:
CALL airlock.observe.work();
CALL airlock.observe.work('budget_requests');
airlock.admin.edit_file_workflow(spec_name VARCHAR, path VARCHAR, filename VARCHAR, action VARCHAR, comment VARCHAR DEFAULT NULL, validate_only BOOLEAN DEFAULT FALSE)- Description: Moves a file’s workflow state by one step (advance/return) or archives/restores the file. Admin-only. Strict Expectations for the target workflow milestone can block the transition unless an active Exception applies. On
advance, every active spec source link withmin_count > 0must have enough active references to non-removed source files in any configuredrequired_workflow_status; otherwise the procedure returnsSOURCE_REFERENCE_REQUIREDwithout changing workflow state. - Parameters:
spec_name(required): logical spec identifier.path(required): scope/path of the file (e.g.defaultor role name for multi-role specs).filename(required): logical filename (no extension).action(required):advance(next step),return(previous step),archive(move to archived),restore(from archived to initial step).comment(optional): reason or note for the transition (validated for length and control characters).validate_only(optional, defaultFALSE): whenTRUE, returns validation result without updating FILE_MANIFEST.- Returns:
SPEC_NAME,PATH,FILENAME,STATUS,CODE,MESSAGE,IS_UPDATED,FROM_STEP,TO_STEP,ISSUES,DETAILS- Branch on
STATUSandCODE.ISSUEScontains structured warnings/errors;DETAILScontains compact diagnostics such as workflow step names and expectation findings. - Example:
CALL airlock.admin.edit_file_workflow('budget_requests', 'default', 'report_2025', 'advance', 'Ready for review');
CALL airlock.admin.edit_file_workflow(spec_name => 'budget_requests', path => 'finadmin', filename => 'finance', action => 'archive');
airlock.admin.select_files(spec_name VARCHAR, search_string VARCHAR DEFAULT NULL, regex_pattern VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL)- Description: Reads spec data from stage, with fuzzy or regex filename filtering.
- Parameters:
spec_name(required)search_string(optional): fuzzy match anywhere in path (preferred for most users).regex_pattern(optional): full regex; used only whensearch_stringisNULL/empty.in_app_role(optional): restrict to a role directory (multi‑role specs).- Returns:
- All configured data columns for the spec, plus:
source_filefile_last_modified- On normal failure:
STATUS,CODE,MESSAGE,ISSUES - Example:
CALL airlock.admin.select_files('budget_requests');
CALL airlock.admin.select_files('budget_requests', 'report_2025');
CALL airlock.admin.select_files(spec_name => 'budget_requests', regex_pattern => '.*\\.csv\\.gz$');
CALL airlock.admin.select_files(spec_name => 'budget_requests', in_app_role => 'budget_analyst');
airlock.admin.validate_data(spec_name VARCHAR, path VARCHAR DEFAULT NULL, file_content VARCHAR DEFAULT NULL)- Description: Validates a file for a spec. Exactly one of
pathorfile_contentmust be provided. Withstaged_path: checks spec existence, non‑archived, file in app stage, CSV parseability. Withfile_content: CSV string (header + rows) passed inline; validates against speccolumn_config(columns, types, not_null, allowed_values) and FK/reference rules, including opt-in caller-scoped reference validation. For small files only. Excel vs CSV in specfile_rules.file_format.file_typeis for UI only; we never store Excel. Specs configured for Excel still accept CSV and pass validation. - Large file guidance: The Streamlit UI enforces an upload size limit for interactive use. For large files, upload directly to the stage (for example with
PUTthrough Snowflake CLI or a Snowflake driver) and callvalidate_datawithpathinstead offile_content. - Parameters:
spec_name(required): logical spec identifier.path(optional): path to the file under the app stage (relative or with stage reference). Omit when usingfile_content.file_content(optional): CSV string (header + data rows) to validate inline. Omit when usingpath.- Returns:
SPEC_NAME VARCHARPATH VARCHAR– the path, or the logical file name (e.g.finance) whenfile_contentwas usedSTATUS VARCHAR–okorerrorIS_VALID BOOLEANISSUE_COUNT NUMBERISSUES VARIANT– array/object of structured issues.CODE VARCHAR– stable machine-readable outcome code.VALIDon success; otherwise the first error/warning issue code.- Example:
CALL airlock.admin.validate_data('budget_requests', 'budget_requests/path/abc123/report.csv.gz');
CALL airlock.admin.validate_data(
spec_name => 'fy26_budget_requests',
file_content => 'fiscal_year,account,request,narrative\n2026,133-5111,100,Note.'
);
airlock.admin.load_data(spec_name VARCHAR, path VARCHAR DEFAULT NULL, file_content VARCHAR DEFAULT NULL, filename VARCHAR DEFAULT NULL, path_scope VARCHAR DEFAULT NULL, attachment_content_base64 VARCHAR DEFAULT NULL, attachment_filename VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL)- Description: Promotes a validated file into
core.FILE_MANIFEST(authoritative in-app table; read-only account projection isAIRLOCK_DATA.CORE.FILE_MANIFEST) for a spec. Exactly one ofpathorfile_contentmust be provided. For staged paths, Airlock captures the exact object digest before validation and verifies the same digest again immediately before the manifest transaction. A missing digest returnsSTAGED_FILE_HASH_UNAVAILABLE; replacement during validation returnsSTAGED_FILE_CHANGED_RETRY. Neither outcome writes validation evidence. The procedure validates before any manifest write and atomically records the exact spec/data revision used. When an upgraded active legacy manifest already owns the same staged path withunknownprovenance, a successful staged load re-attests that existing manifest in place instead of inserting a duplicate stage path; its workflow, links, and stored bytes remain intact. If the spec changes during validation or manifest commit, no manifest attestation is written and the call returnsSPEC_VERSION_CHANGED_RETRY. Before writing the manifest row, Airlock evaluates active Expectations for the load target: workflow specs target the initial workflow step; no-workflow specs target the implicitbecame_activemilestone and display the current file asActive. A strict unmet expectation rejects the load withSTATUS = 'error',IS_LOADED = FALSE, and issue codeEXPECTATION_BLOCKED. A non-strict unmet expectation allows the load and returns issue codeEXPECTATION_WARNING. Returns spec_name, path (scope), and filename to match FILE_MANIFEST. If the spec hasmaterialize_as_table, callsadmin.sync_table(spec_name)after the manifest update. Multi-role specs require an explicit non-default scope: use a stagedpathwith a scope, or use inlinefile_contentwithpath_scope. When admin-seeding a role-isolated folder, pass the matchingin_app_roleso authorization and reference validation use the intended role lens. Optional attachment: passattachment_content_base64(+ optionalattachment_filename) to register one attachment for the loaded file in the same call. When the spec hasattachment_policy.attachment_required=true, an attachment is required in the same call: ifattachment_content_base64is omitted or empty, the load is rejected (no file is written to the manifest) and the procedure returns statuserrorwith issue codeATTACHMENT_REQUIRED. - Large file guidance: For large files, use a staged
pathinstead of inlinefile_contentto avoid UI upload limits and session memory constraints. - Parameters:
spec_name(required): logical spec identifier.path(optional): path to the file under the app stage (e.g.spec/scope/upload_id/file.csv). Omit when usingfile_content. Multi-role specs require scope in the path.file_content(optional): CSV string (header + rows) passed inline. For multi-role specs, include a non-defaultpath_scope.filename(optional): logical filename when usingfile_content.path_scope(optional): scope for inline uploads (e.g.'public/full_access').attachment_content_base64(optional): base64-encoded attachment to register for this file in the same call; required when spec hasattachment_required.attachment_filename(optional): filename for the attachment (e.g.receipt.pdf).in_app_role(optional): role lens for admin-seeded role-isolated folders and role-scoped reference validation.- Returns:
SPEC_NAME VARCHARPATH VARCHAR– scope (same as FILE_MANIFEST path)FILENAME VARCHAR– logical filename (same as FILE_MANIFEST filename)STATUS VARCHAR–okorerrorIS_LOADED BOOLEANROW_COUNT NUMBER– number of data rows loaded (nullable if unknown)ISSUE_COUNT NUMBERISSUES VARIANT– array/object of structured issues (including validation, expectation, and manifest-level problems).CODE VARCHAR– stable machine-readable outcome code.LOADEDon success; otherwise the first error/warning issue code.- Example:
CALL airlock.admin.load_data('budget_requests', 'budget_requests/scope/abc123/report.csv.gz');
CALL airlock.admin.load_data(
spec_name => 'fy26_budget_requests',
file_content => 'fiscal_year,account,request,narrative\n2026,133-5111,100,Note.',
filename => 'fy26_budget_request',
path_scope => 'finadmin',
in_app_role => 'finadmin'
);
airlock.admin.load_okf_bundle(spec_name VARCHAR, bundle_content_base64 VARCHAR, bundle_filename VARCHAR, bundle_metadata VARIANT, concept_metadata VARIANT, path_scope VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL, validate_only BOOLEAN DEFAULT FALSE)- Description: Loads a governed OKF knowledge bundle into Airlock stage storage and
core.FILE_MANIFEST, then projects parsed concept metadata intocore.OKF_CONCEPT_METADATA. The target spec must declarecore_config.payload_adapter = "okf_knowledge_bundle". Airlock evaluates PDP access and active Expectations before writing. Raw bundle bytes remain governed by normal Airlock file workflow, manifest history, and audit rules. Authoritative agent context should come fromAIRLOCK_DATA.ACTIVE.V_OKF_CONCEPT_METADATA, which includes only currentAcceptedor no-workflowActivemanifest rows. - Parameters:
spec_name(required): spec configured for the OKF bundle adapter.bundle_content_base64(required): base64-encoded bundle bytes, typically a zip archive validated by the local OKF helper.bundle_filename(required): bundle filename. The logical manifest filename is normalized from this value.bundle_metadata(required): root bundle metadata such asbundle_name,bundle_version,domain,owner, andpurpose.concept_metadata(required): array of parsed concept rows. Each row requiresconcept_path,type, andsha256; optional fields includepath,title,description,resource,tags,timestamp, andsource_links. Each row'smetadataobject holds OKF v0.2 provenance, verification, lifecycle, computation descriptors, and extensions. A singleverifiedobject becomes an array. Malformed metadata returnsOKF_METADATA_INVALID. These claims never grant access, advance workflow, or execute computations.path_scope(optional, defaultNULL): scope for the governed bundle. Multi-role specs require a non-default scope.in_app_role(optional, defaultNULL): role lens for admin-seeded role-isolated folders.validate_only(optional, defaultFALSE): validate bundle metadata, PDP access, and Expectations without writing.- Returns:
TABLE(SPEC_NAME, PATH, FILENAME, STATUS, IS_LOADED, CONCEPT_COUNT, ISSUE_COUNT, ISSUES, CODE). Branch onSTATUSandCODE. Successful writes returnOKF_BUNDLE_LOADED; validation-only success returnsVALID. - Example:
CALL airlock.admin.load_okf_bundle(
spec_name => 'okf_knowledge',
bundle_content_base64 => '<base64 zip bytes>',
bundle_filename => 'sales_context.zip',
bundle_metadata => PARSE_JSON('{"bundle_name":"sales_context","bundle_version":"2026.06"}'),
concept_metadata => PARSE_JSON('[{"concept_path":"tables/orders","type":"BigQuery Table","sha256":"abc123"}]'),
validate_only => TRUE
);
airlock.admin.sync_okf_bundle_metadata(spec_name VARCHAR, path VARCHAR, filename VARCHAR, bundle_metadata VARIANT, concept_metadata VARIANT, validate_only BOOLEAN DEFAULT FALSE)- Description: Projects parsed OKF bundle concept metadata for an existing active
core.FILE_MANIFESTrow. Useload_okf_bundlefor the normal governed write path; use this helper when the raw bundle file already has an active manifest row and only the parsed projection needs to be refreshed. Authoritative agent context should come fromAIRLOCK_DATA.ACTIVE.V_OKF_CONCEPT_METADATA, which includes only currentAcceptedor no-workflowActivemanifest rows. - Parameters:
spec_name(required): spec containing the governed bundle file.path(required): FILE_MANIFEST path/scope for the bundle file.filename(required): FILE_MANIFEST logical filename for the bundle file.bundle_metadata(required): root bundle metadata such asbundle_name,bundle_version,domain,owner, andpurpose.concept_metadata(required): array of parsed concept rows. Each row requiresconcept_path,type, andsha256; optional fields includepath,title,description,resource,tags,timestamp, andsource_links. The nestedmetadataobject uses the same OKF v0.2 normalization and validation asload_okf_bundle; it does not change Airlock workflow state. Normalized frontmatter is exposed under the existing view'sMETADATA:metadatafield.validate_only(optional, defaultFALSE): validate shape and manifest linkage without writing metadata.- Returns:
TABLE(STATUS, CODE, MESSAGE, ROW_COUNT, ISSUE_COUNT, ISSUES). Branch onSTATUSandCODE. Successful writes returnOKF_METADATA_SYNCED; validation-only success returnsVALID. - Example:
CALL airlock.admin.sync_okf_bundle_metadata(
'okf_knowledge',
'default',
'sales_context',
PARSE_JSON('{"bundle_name":"sales_context","bundle_version":"2026.06"}'),
PARSE_JSON('[{"concept_path":"tables/orders","type":"BigQuery Table","sha256":"abc123"}]'),
TRUE
);
airlock.admin.delete_files(spec_name VARCHAR, source_files VARCHAR, source_directory VARCHAR DEFAULT NULL, dry_run BOOLEAN DEFAULT FALSE)- Description: Canonical procedure to delete files from a spec: REMOVE from stage and set
removed_at/removed_byon FILE_MANIFEST. Files cited by active governed file references are not removed and returnSOURCE_FILE_REFERENCED. Unmigrated rows pinned as a read-only migration source returnMIGRATION_SOURCE_FILE_FROZEN. Whendry_runisTRUE, returns what would be deleted without side effects. - Parameters:
spec_name(required)source_files(required): single filename or JSON array of filenames.source_directory(optional): path/scope; if provided, only files in that path are considered.dry_run(optional, defaultFALSE).- Returns:
TABLE(SPEC_NAME, PATH, FILENAME, UPLOAD_ID, STAGE_PATH, STATUS, MESSAGE, CODE, ISSUES). Branch onSTATUSand stableCODEvalues such asREMOVED,DRY_RUN,NO_MATCH,ACCESS_DENIED,SOURCE_FILE_REFERENCED,MIGRATION_SOURCE_FILE_FROZEN,LOOKUP_FAILED, andUPDATE_FAILED;ISSUESis empty on success and unblocked dry-run rows. - Example:
CALL airlock.admin.delete_files(spec_name => 'my_spec', source_files => 'report.csv.gz', dry_run => TRUE);
CALL airlock.admin.delete_files('my_spec', '["a.csv", "b.csv"]', 'finadmin', FALSE);
airlock.observe.file_versions(spec_name VARCHAR, source_file VARCHAR DEFAULT NULL, uploaded_after TIMESTAMP_LTZ DEFAULT NULL, uploaded_before TIMESTAMP_LTZ DEFAULT NULL)- Description: Enumerates file versions and their original validation provenance from FILE_MANIFEST.
- Parameters:
spec_name(required)source_file(optional): filter by filename (exact match).uploaded_after/uploaded_before(optional): filter onuploaded_at.- Returns:
SPEC_NAME,PATH,FILENAME,UPLOAD_ID,STAGE_PATH,IS_ACTIVE,UPLOADED_AT,UPLOADED_BY,BECAME_INACTIVE_AT,REMOVED_AT,VALIDATED_SPEC_VERSION_ID,VALIDATED_DATA_VERSION_ID,VALIDATED_SPEC_CONFIG_HASH,VALIDATION_PROVENANCE_STATUS(exactorunknown),MIGRATED_BY_MIGRATION_ID, andMIGRATION_SOURCE_MANIFEST_PK. - Example:
CALL airlock.observe.file_versions('my_spec');
CALL airlock.observe.file_versions('my_spec', 'report.csv');
CALL airlock.observe.file_versions(
spec_name => 'my_spec',
uploaded_after => '2024-01-01'::TIMESTAMP_LTZ,
uploaded_before => '2024-12-31'::TIMESTAMP_LTZ
);
airlock.observe.file_state(spec_name VARCHAR, path VARCHAR, filename VARCHAR)- Description: Returns compact mutable state for one active logical file without reading its governed rows or attachment bytes.
FILE_STATE_IDis a UUID that rotates when the file data is replaced or its workflow, attachments, exact source references, or retained version history change. A missing file or revoked observer grant returns no row; failures after authorization still surface as procedure errors. - Returns:
SPEC_NAME,PATH,FILENAME,UPLOAD_ID,FILE_STATE_ID,WORKFLOW_STEP,WORKFLOW_STATUS,ATTACHMENT_COUNT,REFERENCE_COUNT, andLAST_CHANGED_AT. - Example:
CALL airlock.observe.file_state('budget_requests', 'finadmin', 'fy27_budget');
airlock.observe.file_validations(spec_name VARCHAR, path VARCHAR DEFAULT NULL, filename VARCHAR DEFAULT NULL, max_items FLOAT DEFAULT 500)- Description: Lists append-only validation attestations for governed file versions. Revalidation creates another row rather than replacing the file's original validated-against claim.
- Parameters:
spec_nameis required;path,filename, andmax_itemsare optional filters. - Returns: Manifest identity, immutable spec/data version IDs and hash, optional content checksum, validation status/source, validator contract and app version, optional migration ID/summary, and validation actor/time.
- Example:
CALL airlock.observe.file_validations('budget_requests');
CALL airlock.observe.file_validations('budget_requests', 'finadmin', 'fy27_budget', 100);
#2.4.1. Attachment policy
Spec-level attachment policies (attachment_policy in spec config) control whether attachments are enabled, max count, active count per attachment tag, allowed types/extensions, and optional image profiles. The default active limit per attachment_tag is 1; use default_attachment_tag_limit or attachment_tag_limits to override it. When attachment_required is true, load_data (admin and user) will not accept a file upload unless an attachment is provided in the same call; the load is rejected and no file is written to the manifest (issue code ATTACHMENT_REQUIRED). Attachment metadata is stored in core.ATTACHMENT_MANIFEST; AIRLOCK_DATA.CORE.ATTACHMENT_MANIFEST is the read-only secure view for account consumers. Additional attachment behavior and Streamlit touch points: docs/attachments_batch26.md.
Preview behavior: all attachment file types are supported for storage and download. Inline preview is available for select formats only. Images and text files are previewed directly; PDFs up to 100 MB and 2,000 pages can show one bounded rendered page at a time when the runtime can render them. The selected PDF page and next two pages render in one bounded window so forward navigation can use the private session cache. PDFs larger than 12 MB require an explicit open action, stream to private session-local storage, and use short-lived Snowflake links for full-file download when available. Successful page reads emit metadata-only ATTACHMENT_PREVIEW activity. Other binary formats are download-only.
airlock.admin.attach_to_spec(spec_name VARCHAR, attachment_stage_path VARCHAR DEFAULT NULL, attachment_content_base64 VARCHAR DEFAULT NULL, attachment_filename VARCHAR DEFAULT NULL, attachment_type VARCHAR DEFAULT 'other', description VARCHAR DEFAULT NULL, link_spec_name VARCHAR DEFAULT NULL, link_file_path VARCHAR DEFAULT NULL, link_file_filename VARCHAR DEFAULT NULL, attachment_tag VARCHAR DEFAULT NULL)- Description: Registers a binary file as a spec-level attachment (not tied to a specific data file). Use when creating a spec and its first attachment in one flow. Exactly one input mode: stage (
attachment_stage_pathunder@AIRLOCK_DATA.CORE.AIRLOCK_ATTACHMENTS) or stream (attachment_content_base64and optionalattachment_filename; decoded and uploaded via put_stream). For larger streamed attachments, use server-bound parameters rather than embedding base64 in SQL text; base64 expands raw bytes by roughly one third and remains subject to the account's Snowflake string limit. Whenlink_file_*are provided, the attachment is linked to a logical file. - Parameters:
spec_name(required)attachment_stage_path(optional): path under AIRLOCK_ATTACHMENTS stage.attachment_content_base64(optional): base64-encoded content (stream mode).attachment_filename(optional): filename for stream mode.attachment_type(optional, default'other'): e.g.'other','image','document'.description(optional)link_spec_name,link_file_path,link_file_filename(optional): link attachment to this logical file.attachment_tag(optional, defaultdefault): logical attachment tag. Adding another active attachment for the same tag retires older rows beyond the tag limit.- Returns:
SPEC_NAME,ATTACHMENT_STAGE_PATH,STATUS,CODE,MESSAGE,ATTACHMENT_ID,SIZE_BYTES,ISSUES. Normal failures returnSTATUS = 'error'with stableCODEvalues such asSPEC_NOT_FOUND,ATTACHMENTS_DISABLED,INVALID_BASE64,ACCESS_DENIED_WORKFLOW_STATE, orATTACHMENT_SAVE_FAILED. - Example:
CALL airlock.admin.attach_to_spec(
spec_name => 'my_spec',
attachment_content_base64 => 'YWJj',
attachment_filename => 'note.txt',
attachment_type => 'other',
description => 'Sample note'
);
airlock.admin.add_attachment(spec_name VARCHAR, file_path VARCHAR, file_filename VARCHAR, attachment_stage_path VARCHAR DEFAULT NULL, attachment_content_base64 VARCHAR DEFAULT NULL, attachment_filename VARCHAR DEFAULT NULL, attachment_type VARCHAR DEFAULT 'other', description VARCHAR DEFAULT NULL, attachment_tag VARCHAR DEFAULT NULL)- Description: Adds an attachment to an existing file in a spec. Enforces spec
attachment_policyand file workflow state (add allowed only when the file is in a step that permits file/attachment management). Exactly one ofattachment_stage_pathorattachment_content_base64(with optionalattachment_filename) is required. - Parameters:
spec_name,file_path,file_filename(required): identify the target file.attachment_stage_pathorattachment_content_base64(+ optionalattachment_filename): content source.attachment_type(optional, default'other'),description(optional),attachment_tag(optional, defaultdefault).- Returns:
SPEC_NAME,FILE_PATH,FILE_FILENAME,ATTACHMENT_ID,ATTACHMENT_STAGE_PATH,STATUS,CODE,MESSAGE,SIZE_BYTES,ISSUES. On denial (e.g. workflow state),STATUS='error',CODE='ACCESS_DENIED_WORKFLOW_STATE'.
airlock.admin.replace_attachment(spec_name VARCHAR, file_path VARCHAR, file_filename VARCHAR, attachment_id VARCHAR, attachment_stage_path VARCHAR DEFAULT NULL, attachment_content_base64 VARCHAR DEFAULT NULL, attachment_filename VARCHAR DEFAULT NULL, attachment_type VARCHAR DEFAULT 'other', description VARCHAR DEFAULT NULL, attachment_tag VARCHAR DEFAULT NULL)- Description: Replaces an existing attachment by
attachment_id. Same workflow and policy checks as add; same content options (stage or base64).
airlock.admin.delete_attachment(spec_name VARCHAR, file_path VARCHAR, file_filename VARCHAR, attachment_id VARCHAR)- Description: Deletes an attachment by
attachment_idfor the given file. Subject to same workflow/policy rules (manage allowed only in permitted steps).
#2.5. Retention Policies & Outdated Files
Retention policy CRUD
airlock.observe.retention_policies(spec_name VARCHAR DEFAULT NULL)- Description: Lists retention policies; optional filter by
spec_name(policies for that spec or account-level). - Parameters:
spec_name(optional). - Returns:
POLICY_NAME,SPEC_NAME,RETENTION_DAYS,RETENTION_MIN_VERSIONS,RETENTION_SCOPE,DESCRIPTION,IS_ACTIVE,IS_LOCKED. - Example:
CALL airlock.observe.retention_policies();
CALL airlock.observe.retention_policies('my_spec');
airlock.observe.retention_policy(policy_name VARCHAR)- Description: Returns a canonical descriptor for a single retention policy (by
policy_name). - Parameters:
policy_name(required). - Returns: Same columns as list (single row or empty).
- Example:
CALL airlock.observe.retention_policy('Account Default');
airlock.admin.create_retention_policy(policy_descriptor VARIANT, validate_only BOOLEAN DEFAULT FALSE)- Description: Creates a new retention policy. Descriptor:
policy_name(required); optional:spec_name(NULL = account-level),retention_days,retention_min_versions,retention_scope,description,is_active,is_locked. Validatesretention_days/retention_min_versions>= 0 and thatspec_nameexists incore.specsif provided. - Returns:
POLICY_NAME,STATUS,IS_CREATED,VALIDATION. - Example:
CALL airlock.admin.create_retention_policy(PARSE_JSON('{"policy_name": "Spec X", "spec_name": "x", "retention_days": 30}'));
airlock.admin.alter_retention_policy(policy_name VARCHAR, policy_descriptor VARIANT, validate_only BOOLEAN DEFAULT FALSE)- Description: Alters an existing retention policy. Optional descriptor fields:
spec_name,retention_days,retention_min_versions,retention_scope,description,is_active,is_locked. - Returns:
POLICY_NAME,STATUS,IS_UPDATED,VALIDATION. - Example:
CALL airlock.admin.alter_retention_policy('Spec X', PARSE_JSON('{"retention_days": 60}'));
airlock.admin.drop_retention_policy(policy_name VARCHAR, force BOOLEAN DEFAULT FALSE)- Description: Drops a retention policy. The account default policy cannot be dropped. When
forceisFALSE, does not delete if the policy is locked. - Parameters:
policy_name(required),force(optional, defaultFALSE). - Returns:
POLICY_NAME,STATUS,CODE,MESSAGE,IS_DROPPED,ISSUES,DETAILS. - Branch on
STATUSand stableCODE;ISSUESis an array of{code, message, severity}and is empty whenSTATUSisok. - Example:
CALL airlock.admin.drop_retention_policy('My Policy');
CALL airlock.admin.drop_retention_policy('My Policy', TRUE);
Outdated files (retention-driven purge)
airlock.observe.purge_candidates(spec_name VARCHAR DEFAULT NULL)- Description: Lists non-current uploads that are eligible for purge based on retention policies. Source manifests protected by a
source_preservedmigration are excluded. Scoped observers receive only candidates allowed by their spec, path, and workflow-state grant; omittingspec_namerequiresapp_adminorglobal_observer. - Parameters:
spec_name(optional): limit to a specific spec.- Returns (shape may be extended in future v1.x, but these columns are stable):
spec_namepathfilenameupload_idstage_pathbecame_inactive_atuploaded_at- Example:
CALL airlock.observe.purge_candidates();
CALL airlock.observe.purge_candidates('sample_budget_requests');
airlock.admin.delete_outdated_files(spec_name VARCHAR DEFAULT NULL, dry_run BOOLEAN DEFAULT TRUE, limit INTEGER DEFAULT 1000)- Description: Deletes outdated files from stage and updates history, based on retention policies. Source manifests protected by a
source_preservedmigration are never selected. Whendry_runisFALSE, writes aFILE_RETENTION_PURGErow tocore.eventsper removed file (see Events & observability above). - Parameters:
spec_name(optional): restrict scope.dry_run(optional, defaultTRUE): whenTRUE, report what would be deleted without side effects.limit(optional, default 1000): maximum number of files to delete in one call.- Returns (one row per candidate in this call; shape is stable in v1.x):
spec_name,path,filename,upload_id,stage_path,manifest_pk(core.FILE_MANIFEST.pkfor that upload row)- Example:
CALL airlock.admin.delete_outdated_files(dry_run => TRUE);
CALL airlock.admin.delete_outdated_files(dry_run => FALSE);
CALL airlock.admin.delete_outdated_files('sample_budget_requests', FALSE, 100);
#2.6. File Maintenance
airlock.admin.rebuild_file_manifest_from_stage(force BOOLEAN DEFAULT FALSE)- Description: Rebuilds
FILE_MANIFESTfrom stage listing, optionally truncating first. Refuses to rebuild while append-only validation attestations exist, preserving their manifest identity links. - Parameters:
force(optional, defaultFALSE): whenTRUE, transactionally clears FILE_MANIFEST before rebuilding. Rebuilds serialize with exact-provenance file writers and are refused while validation attestations exist. - Returns: summary string.
- Example:
CALL airlock.admin.rebuild_file_manifest_from_stage();
CALL airlock.admin.rebuild_file_manifest_from_stage(TRUE);
airlock.admin.rebuild_access_index(bump_version BOOLEAN DEFAULT TRUE)- Description: Rebuilds compiled Airlock access rows from app-managed roles, assignments, and specs. Snowflake session/application-role activation is intentionally not cached.
- Parameters:
bump_version(optional, defaultTRUE): whenTRUE, publishes a new access version after both compiled halves rebuild successfully. - Returns:
TABLE(STATUS, CODE, MESSAGE, USER_ROLE_ROWS, SPEC_ACCESS_ROWS, VERSION_UUID, ISSUES, DETAILS) - Example:
CALL airlock.admin.rebuild_access_index();
airlock.admin.rebuild_user_role_closure(user_id VARCHAR DEFAULT NULL, bump_version BOOLEAN DEFAULT FALSE)- Description: Rebuilds compiled user-to-role closure rows for one user or all users. Effective assignment windows remain evaluated at read time.
- Parameters:
bump_version(optional, defaultFALSE): whenTRUEwithoutuser_id, also rebuilds spec access and publishes a new access version; filtered rebuilds keep the current version. - Returns:
TABLE(STATUS, CODE, MESSAGE, USER_ROLE_ROWS, SPEC_ACCESS_ROWS, VERSION_UUID, ISSUES, DETAILS) - Example:
CALL airlock.admin.rebuild_user_role_closure();
CALL airlock.admin.rebuild_user_role_closure('ASMITH');
airlock.admin.rebuild_spec_role_access(spec_name VARCHAR DEFAULT NULL, bump_version BOOLEAN DEFAULT FALSE)- Description: Rebuilds compiled spec role-access rows for one spec or all specs. Per-user isolated guest path keys remain lazy and are not created by this rebuild.
- Parameters:
bump_version(optional, defaultFALSE): whenTRUEwithoutspec_name, also rebuilds user-role closure and publishes a new access version; filtered rebuilds keep the current version. - Returns:
TABLE(STATUS, CODE, MESSAGE, USER_ROLE_ROWS, SPEC_ACCESS_ROWS, VERSION_UUID, ISSUES, DETAILS) - Example:
CALL airlock.admin.rebuild_spec_role_access();
CALL airlock.admin.rebuild_spec_role_access('finance');
airlock.admin.sync_table(spec_name VARCHAR)- Description: Syncs the materialized table for a spec with its view using MERGE. Creates the materialized table on first use if it does not exist. Called automatically by
load_datawhen the spec hasmaterialize_as_tableset. Admin-only; use when you need to force a sync without loading a file. Userebuild_spec_tableonly when the spec's column shape changed and the existing table schema must be replaced. - Parameters:
spec_name(required). - Returns:
STATUS,ROWS_INSERTED,ROWS_UPDATED,ROWS_DELETED,MESSAGE - Example:
CALL airlock.admin.sync_table('budget_requests');
#2.8. Spec Views & Tables
airlock.admin.rebuild_spec_view(spec_name VARCHAR)- Description: Rebuilds ACTIVE and HIST views for a single spec.
- Parameters:
spec_name(required). - Returns: summary string.
- Example:
CALL airlock.admin.rebuild_spec_view('sample_budget_requests');
airlock.admin.rebuild_spec_table(spec_name VARCHAR)- Description: Rebuilds the ACTIVE materialized table (
T_{spec_name}) for a spec and syncs it. - Parameters:
spec_name(required). - Returns: summary string.
- Example:
CALL airlock.admin.rebuild_spec_table('sample_budget_requests');
#2.9. Setup & Validation
airlock.admin.rerun_setup()- Description: Idempotent setup for core objects, baseline data, and setup‑time views.
- Returns: summary string.
- Example:
CALL airlock.admin.rerun_setup();
airlock.admin.validate_spec(spec_config VARIANT, reuse_spec BOOLEAN DEFAULT FALSE, allow_existing_spec BOOLEAN DEFAULT FALSE)- Description: Validates a
spec_configobject using the same rules as the UI: - Core config (
spec_name,spec_alias,owner_role,is_published,is_archived, workflow, etc.) - File rules (CSV/Excel)
- Column config
- Guest access, including DB checks
- Optional
rulesarray: rules may defineforeign_key,reference_map,date_window, orvariant_shapevalidation.foreign_keymay use eitherfieldor afield_pathinside a VARIANT column; see Shared schema & FK validation. Whenallow_existing_specisTRUE,spec_nameandspec_aliasmay already exist for this same spec (round-trip fromobserve.spec_configor alter flow). - Parameters:
spec_config(required): VARIANT or JSON string.reuse_spec(optional): allow reuse of an existing spec directory in stage.allow_existing_spec(optional): allowspec_nameandspec_aliasto already exist for this same spec; use when validating a config returned byobserve.spec_config.- Returns:
- JSON string:
{"valid": true}or{"valid": false, "invalid_tabs": [...], ...}. - Example:
CALL airlock.admin.validate_spec(PARSE_JSON('{"core_config": {"spec_name": "x", "owner_role": "analyst"}}'));
CALL airlock.admin.validate_spec(:config_var, reuse_spec => TRUE);
CALL airlock.admin.validate_spec(:config_var, FALSE, TRUE); -- allow_existing_spec for round-trip
#2.10. Shared schema & FK validation
These procedures support customer-defined shared views and foreign-key-like validation of spec data against those views. The schema AIRLOCK_DATA.SHARED exists in the customer's account; the app does not grant USAGE or CREATE VIEW. The customer grants those privileges (e.g. via SECURITYADMIN). See Shared schema grants.
airlock.admin.create_shared_schema()- Description: Creates the schema
AIRLOCK_DATA.SHAREDif it does not exist. Idempotent. Does not grant USAGE or CREATE VIEW; the customer performs grants (see Shared schema grants). - Parameters: None.
- Returns:
VARCHAR— summary message. - Example:
CALL airlock.admin.create_shared_schema();
- Shared-view references
- Description: Views in
AIRLOCK_DATA.SHAREDare registered through the unified reference API below. Useairlock.admin.register_referencewithref_type => 'shared_view'.
airlock.admin.register_reference(reference_name VARCHAR, ref_type VARCHAR, schema_name VARCHAR, view_name VARCHAR, spec_name VARCHAR, ref_columns VARIANT)- Description: Registers a logical FK reference backed by a shared view, a materialized ingestion spec (
spec), or a read-only reference spec (reference_spec). References are identified by a stable alias (reference_name) that specs use in FK rules (unless the rule points directly at a reference spec name; see FK rules below). - Parameters:
reference_name(required): Logical alias used in spec configs (e.g.employees_ref,ref_employees).ref_type(required):'shared_view','spec', or'reference_spec'.schema_name/view_name(forshared_view): identifyAIRLOCK_DATA.<schema_name>.<view_name>(e.g.SHARED/employees).spec_name(forspec): backing ingestion spec name; FK validation reads distinct values fromAIRLOCK_DATA.ACTIVE.T_<spec_name>.spec_name(forreference_spec): backing reference spec name (core.specswithis_reference_spec=true).view_namestores the defaultobject_keywhen the backing spec has multiplereference_config.object_paths(optional when exactly one path exists).ref_columns(required): Single FK column name (VARIANT array with one element is accepted).- Returns:
TABLE(REFERENCE_NAME VARCHAR, REF_TYPE VARCHAR, REF_COLUMNS VARIANT, STATUS VARCHAR, CODE VARCHAR, MESSAGE VARCHAR, ISSUES VARIANT). CODEis stable for agents; examples includeALLOW,REF_TYPE_INVALID,REF_COLUMNS_REQUIRED,MATERIALIZED_TABLE_REQUIRED,REFERENCE_SAVE_FAILED, andREFERENCE_SPEC_VALIDATION_FAILED.
airlock.admin.revalidate_reference(reference_name VARCHAR)- Description: Smoke-check that a registered reference is still readable (
SELECT 1 … LIMIT 1). Forreference_spec, probes everyobject_pathon the backing spec. - Returns:
TABLE(REFERENCE_NAME VARCHAR, STATUS VARCHAR, CODE VARCHAR, MESSAGE VARCHAR, ISSUES VARIANT). CODEis stable for agents; examples includeALLOW,REFERENCE_NOT_FOUND,REF_TYPE_INVALID, andREFERENCE_TARGET_NOT_READABLE.
airlock.admin.unregister_reference(reference_name VARCHAR)- Description: Removes a logical reference and any role assignments for it. Does not drop the underlying view or spec.
- Returns:
TABLE(REFERENCE_NAME VARCHAR, STATUS VARCHAR, CODE VARCHAR, MESSAGE VARCHAR, ISSUES VARIANT). CODEis stable for agents; examples includeALLOW,REFERENCE_NAME_REQUIRED, andREFERENCE_DELETE_FAILED.
airlock.observe.references(ref_type VARCHAR)- Description: Lists registered references.
ref_typeoptional filter ('shared_view','spec', or'reference_spec'); passNULLto list all. - Returns:
TABLE(REFERENCE_NAME, REF_TYPE, SCHEMA_NAME, VIEW_NAME, SPEC_NAME, REF_COLUMNS, REGISTERED_AT).
airlock.admin.grant_reference_access(reference_name VARCHAR, role_name_or_pk VARCHAR)- Description: Grants an Airlock role access to use a reference in FK rules.
app_adminis implicitly allowed for all references; assignments extend access to additional roles. - Returns:
TABLE(REFERENCE_NAME, ROLE_PK, ROLE_NAME, STATUS, CODE, MESSAGE, ISSUES). CODEis stable for agents; examples includeALLOW,ALREADY_GRANTED,REFERENCE_NOT_FOUND,ROLE_NOT_FOUND,ROLE_NOT_SPEC_ADMIN, andREFERENCE_ACCESS_UPDATE_FAILED.
airlock.admin.revoke_reference_access(reference_name VARCHAR, role_name_or_pk VARCHAR)- Description: Revokes an Airlock role’s access to use a reference in FK rules.
- Returns:
TABLE(REFERENCE_NAME, ROLE_PK, ROLE_NAME, STATUS, CODE, MESSAGE, ISSUES). CODEis stable for agents; examples includeALLOW,NOOP,REFERENCE_NAME_REQUIRED,ROLE_NOT_FOUND, andREFERENCE_ACCESS_UPDATE_FAILED.
airlock.observe.reference_access(reference_name VARCHAR)- Description: Lists roles that have been granted access to a reference (not including the implicit
app_adminaccess). - Returns:
TABLE(REFERENCE_NAME, ROLE_PK, ROLE_NAME).
Spec config FK rule (unified): Spec config may include an optional top-level rules array. A rule with "type": "foreign_key" has either "field" (top-level column name) or "field_path" (a JSON path inside a VARIANT column), plus "ref": { "reference": "<reference_name_or_spec>", "column": "<column_name>", "object_key": "<optional>", "scope": "<optional>" }. The reference value may be:
- A
core.references.reference_name(shared_view,spec, orreference_spec), or - The
spec_nameof a reference spec (is_reference_spec=true) with no separateregister_referencerow (implicit binding).
For VARIANT payloads, use field_path:
{
"type": "foreign_key",
"field_path": {
"column": "evidence",
"json_path": "$.employee.username"
},
"ref": {
"reference": "employee_info",
"column": "employee_username"
}
}
field_path.column must be a variant column in column_config. field_path.json_path supports a small scalar path subset such as $.employee.username, $.items[0].id, or $['employee-id']. Validation parses the JSON/VARIANT cell, extracts the scalar value, and checks it against the same reference target used by normal top-level FK rules.
For flexible business payloads, use variant_shape to make a variant column first-class without freezing every future attribute as a top-level column:
{
"type": "variant_shape",
"field": "expense_details",
"allowed_root_keys": ["expense", "receipt", "allocation"],
"paths": [
{"json_path": "$.expense.business_purpose", "type": "string", "required": true},
{"json_path": "$.receipt.total", "type": "number", "required": true},
{"json_path": "$.allocation.cost_center", "type": "string", "required": false}
]
}
variant_shape.field must be a variant column. Payloads must be valid JSON objects by default. paths[].json_path uses the same simple JSON path subset as field_path; supported path types are string, integer, number, boolean, date, datetime, uuid, object, and array. allowed_root_keys is intentionally strict: when present, any undeclared root key is rejected so business-process drift is reviewed by editing the spec rule, not hidden in uploads.
When the target has multiple reference_config.object_paths, set ref.object_key to the logical key (same as for agent.select_reference_data). FK validation defaults to ref.scope = "global", which runs against the full reference domain. For reference_spec targets, set ref.scope = "caller_visible" to apply that reference spec's row_filters_by_role using the caller's effective Airlock roles, ref.scope = "role_lens" to validate against the explicit in_app_role lens, or ref.scope = "username_lens" to validate against rows where reference_config.row_filter_by_username.column equals trusted CURRENT_USER(). For spec targets, scoped FK validation requires the backing spec to be materialized (core_config.materialize_as_table=true) and to define the matching read_policy.row_filters_by_role or read_policy.row_filter_by_username; Airlock applies that policy to AIRLOCK_DATA.ACTIVE.T_<spec_name> during validation.
Reference specs may omit column_config for legacy/API-minimal setups. When column_config is provided, Airlock validates it and requires every reference_config.object_paths[].readable_columns[] entry to be represented there, so the documented shape matches the selectable reference data.
- For
ref_type = 'shared_view':AIRLOCK_DATA.SHARED.<view_name>.<column>. - For
ref_type = 'spec':AIRLOCK_DATA.ACTIVE.T_<spec_name>.<column>; optional scoped validation uses the target spec'sread_policy.row_filters_by_roleorread_policy.row_filter_by_username. - For reference specs (implicit or
ref_type = 'reference_spec'): FQ object fromreference_config.object_paths.
Spec row read policy: Ingestion specs may include optional top-level read_policy.row_filters_by_role using the same vetted filter shape as reference specs, and/or read_policy.row_filter_by_username with a trusted username column. This requires core_config.materialize_as_table=true. airlock.agent.select_my_files then reads from the materialized table and appends the caller's role and username predicates instead of reading raw stage files. This is the recommended model for specs such as dept_account_map, where HR should only see HR rows and finance should only see finance rows, or user-scoped maps where each caller sees only their rows.
Legacy rules using { "schema": "SHARED", "view": "<view_name>", "column": "<column_name>" } are rejected at validation time; use ref.reference + register_reference.
#3. Agent Schema (airlock.agent.*)
Agent procedures are available to both app_agent and app_admin application roles and are access‑controlled by in‑app roles and assignments.
#3.1. Introspection & Help
airlock.agent.help(procedure_name VARCHAR DEFAULT NULL)- Description: Returns help text for agent procedures.
- Parameters:
procedure_name(optional): specific procedure;NULL= overview.- Returns:
TABLE(LINE VARCHAR). - Example:
CALL airlock.agent.help();
CALL airlock.agent.help('list_my_specs');
airlock.agent.documentation(output_format VARCHAR DEFAULT 'markdown', doc_key VARCHAR DEFAULT 'api_v1', content_mode VARCHAR DEFAULT 'TOC', section_ids VARCHAR DEFAULT NULL)- Description: Returns the bundled copy of this stored procedure reference for the installed app version as
TABLE(PAYLOAD VARIANT)— structured objects (JSON-compatible), not markdown as the column type. Defaultcontent_modeisTOC: first row iskind:documentation_manifestwithagent_instructions,content_hash, andetag, then one row per ATX heading (levels 2–6), each headingPAYLOADincluding e.g.kind:toc_entry,level,section_id,title, plusschema_version. Every TOC row has asection_id; unnumbered headings use slug ids.PROCEDURES: first row iskind:procedure_index_manifest, followed bykind:procedure_entryrows with procedure name, schema, signature, arguments, and section id.FULL: one row withkind:full,agent_instructions,suggested_filename,content_hash,etag, andbody(UTF‑8 markdown).SECTION: one row withkind:section_bundle,agent_instructions, andsections: array of{section_id, body}.CHUNK: multiple smallerkind: "chunk"rows withchunk_index,chunk_count,char_start,char_end,content_hash, andbody. - Parameters:
output_format(optional):'markdown','md', or'text'(same bundled source today).doc_key(optional): document id; only'api_v1'is supported.content_mode(optional):TOC(default),PROCEDURES,FULL,SECTION, orCHUNK(aliases accepted; case-insensitive).section_ids(optional): required forSECTION— comma‑separated TOCsection_idvalues, such as'3','4.8','3,4.8', or a slug id like'guidance-for-ai-agents-and-automation'. Optional forCHUNK— blank/all returns all chunks;'1','1-3', or'offset=0,limit=12000'selects a subset.- Returns:
TABLE(PAYLOAD VARIANT)— parse eachPAYLOADin the client; error rows usekind:errorwithcode/message. - Examples:
CALL airlock.agent.documentation();
CALL airlock.agent.documentation(CONTENT_MODE => 'PROCEDURES');
CALL airlock.agent.documentation(CONTENT_MODE => 'FULL');
CALL airlock.agent.documentation('markdown', 'api_v1', 'SECTION', '4.8');
CALL airlock.agent.documentation('markdown', 'api_v1', 'CHUNK', '1-3');
#3.2. Roles & Assignments
airlock.agent.list_my_roles()- Description: Lists in‑app roles available to the current user (excluding
app_admin). - Returns:
ROLE_NAME VARCHARDESCRIPTION VARCHARMANAGED_BY_ROLE VARCHARIS_SPEC_ADMIN BOOLEANIS_LOCKED BOOLEAN- Example:
CALL airlock.agent.list_my_roles();
airlock.agent.list_my_assignments(username VARCHAR DEFAULT NULL, rolename VARCHAR DEFAULT NULL)- Description: Lists assignments the caller can manage (their roles + child roles).
- Parameters:
username(optional): filter by user; supports comma‑separated list.rolename(optional): filter by role; supports comma‑separated list.- Returns:
ASSIGNMENT_NAME VARCHARDESCRIPTION VARCHARUSER_ID VARCHARASSIGNED_ROLE VARCHAREFFECTIVE_FROM TIMESTAMP_LTZEFFECTIVE_TO TIMESTAMP_LTZIS_LOCKED BOOLEAN- Example:
CALL airlock.agent.list_my_assignments();
CALL airlock.agent.list_my_assignments('jane.doe');
CALL airlock.agent.list_my_assignments(rolename => 'analyst');
airlock.agent.create_delegation(delegation_descriptor VARIANT, validate_only BOOLEAN DEFAULT FALSE)- Description: Creates a self-service user-to-agent delegation for
CURRENT_USER(). The caller is always the principal/on-behalf-of user. Ifdelegation_descriptor.principal_useris supplied, it must matchCURRENT_USER(). The caller must already have write access to the spec, and the spec must enabledelegation_policy. - Guardrail: Delegates cannot create second-order delegations for someone else. For example, if csmith has a grant to act for asmith, csmith cannot call this procedure to grant Charlie access for asmith.
- Admin boundary: Delegation action names are
airlock.agent.*procedure actions only.airlock.admin.*procedures are never delegable throughon_behalf_of_user; admin procedures remain direct control-plane authority granted by Snowflake application role. - Runtime model: Delegations are user-level grants. A delegate's own Airlock role assignments do not expand or restrict delegated authority; Airlock re-checks the principal user's current spec access when a delegated action runs. If a delegation has
path_scope, the requested staged path or inlinepath_scopemust resolve to that same scope. - Spec policy validation:
admin.validate_specrejects invaliddelegation_policyconfig before create/edit. Policy action lists must be arrays of known delegableairlock.agent.*actions; admin action names are rejected; workflow-step action lists may only narrow the global policy and must reference configured workflow steps. - Common descriptor fields:
actor_user: agent/service account user that may act.spec_name: spec where the grant applies.allowed_actions: subset allowed by the spec'sdelegation_policy, such asvalidate_data,load_data,add_attachment,replace_attachment, oredit_file_workflow. Includevalidate_datawheneverload_datais allowed, because loads validate before writing.- Optional:
delegation_id,path_scope,duration_days,comment,replace_existing. - Returns:
TABLE(DELEGATION_ID, STATUS, IS_CREATED, VALIDATION, CODE, MESSAGE, ISSUES) - Branch on
STATUSandCODE.MESSAGEandISSUESare top-level for agents;VALIDATIONkeeps compact diagnostics. - Stable
CODEexamples:CREATED,VALIDATE_ONLY,DELEGATION_DISABLED,DELEGATION_ACTION_NOT_ALLOWED,DELEGATION_PRINCIPAL_SPEC_ACCESS_DENIED,DELEGATION_EXISTS. - Example:
CALL airlock.agent.create_delegation(
PARSE_JSON('{"actor_user":"csmith","spec_name":"demo_employee_reimbursements","allowed_actions":["validate_data","load_data","add_attachment"],"duration_days":30}'),
FALSE
);
airlock.agent.list_my_delegations(direction VARCHAR DEFAULT 'both', spec_name VARCHAR DEFAULT NULL, include_inactive BOOLEAN DEFAULT FALSE)- Description: Lists user-to-agent delegations involving
CURRENT_USER(). Usedirection => 'received'for delegations where the caller is the actor/delegate,direction => 'granted'for delegations where another actor can act for the caller, orbothfor both sides. - Parameters:
direction(optional, defaultboth):received,granted, orboth.spec_name(optional): filter to one spec.include_inactive(optional, defaultFALSE): whenFALSE, returns only currently active delegations. SetTRUEto include future, expired, or revoked delegations for acknowledgement and audit.- Returns:
DIRECTION VARCHAR,DELEGATION_ID VARCHAR,PRINCIPAL_USER VARCHAR,ACTOR_USER VARCHARACTOR_AIRLOCK_ROLE VARCHAR(legacy metadata; not authorization),SPEC_NAME VARCHAR,ALLOWED_ACTIONS VARIANTPATH_SCOPE VARCHAR,EFFECTIVE_FROM TIMESTAMP_LTZ,EFFECTIVE_TO TIMESTAMP_LTZIS_ACTIVE BOOLEAN,COMMENT VARCHAR,ACTION_CONTEXT VARIANT- Agent guidance: For rows where
DIRECTION = 'received'andIS_ACTIVE = TRUE, passPRINCIPAL_USERason_behalf_of_userto delegated agent procedures such asvalidate_data,load_data,add_attachment,replace_attachment, oredit_file_workflow. For inlinevalidate_data/load_data, omitpath,in_app_role, andpath_scope; Airlock resolves the principal's folder/lens. For follow-upadd_attachmentorreplace_attachment, pass thefile_pathandfile_filenamereturned by the delegated load plus the sameon_behalf_of_user. If a follow-up delegated mutation omitson_behalf_of_user, Airlock treats it as a direct actor call and evaluates the actor's own path access. Whenload_dataincludesattachment_content_base64, that call already registers the first attachment; use a distinctattachment_tagfor extra evidence added later.replace_attachmentandedit_file_workfloware explicit actions: the specdelegation_policyand active grant must both allow them, and workflow movement must be allowed at the file's current workflow step. A delegated submit-to-Submitted experience should callload_datafirst, inspect the returned/current workflow state, and calledit_file_workflow(action => 'advance')with the sameon_behalf_of_useronly when the file is still Draft and workflow movement is delegated. It should report policy limits instead of trying to encode workflow state in file data. PassDELEGATION_IDasdelegation_idonly if Airlock reports an ambiguous delegation. Useinclude_inactive => TRUEonly to explain future or no-longer-effective delegations; inactive rows do not authorize work. - Demo note: After
make demo, the csmith demo user can runCALL <APP_NAME>.AGENT.LIST_MY_DELEGATIONS('received', 'demo_employee_reimbursements');and discoverdemo_delegation_csmith_for_asmith_reimbursements. - Example:
CALL airlock.agent.list_my_delegations('received');
CALL airlock.agent.list_my_delegations('granted', 'demo_employee_reimbursements');
CALL airlock.agent.list_my_delegations(direction => 'received', include_inactive => TRUE);
airlock.agent.get_my_license_seat()- Description: Read-only snapshot for
CURRENT_USER: whether they hold a named license,SEAT_CEILING,AUTO_ASSIGN_ENABLED, and a shortMESSAGE. Does not claim a seat; usecheck_my_license_seat/claim_license_seat()to gate or self-assign. - Returns:
TABLE(HAS_SEAT, SEAT_CEILING, AUTO_ASSIGN_ENABLED, MESSAGE) - Example:
CALL airlock.agent.get_my_license_seat();
airlock.agent.get_my_access_context()- Description: Read-only snapshot for
CURRENT_USER: named-license status, compiled access version, assignment-derived role count, and accessible spec count from Airlock's compiled user/spec access index. Does not claim a license, create per-user guest path keys, or useapp_adminsession activation.DETAILSincludesread_only,policy_seeded,access_index_available,billing_status,billing_code, andlicense_capacity_availableso callers can quickly branch on billing/access readiness without running a mutating license procedure. - Returns:
TABLE(USER_ID, HAS_ACTIVE_SEAT, SEAT_CEILING, AUTO_ASSIGN_ENABLED, SEATS_ASSIGNED, SEATS_FREE, ACCESS_VERSION, ROLE_COUNT, ACCESSIBLE_SPEC_COUNT, STATUS, CODE, MESSAGE, DETAILS) - Example:
CALL airlock.agent.get_my_access_context();
#3.3. Specs & Files
airlock.agent.list_my_specs(in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE)- Description: Lists specs accessible to the current user (owner and published guest specs).
- Parameters:
in_app_role(optional): session “lens” role; must be a role the caller holds. When set, access is evaluated for that role and roles it manages inmanaged_by_role(intersected with the caller’s assignment-derived roles), so guest specs invited to a subordinate role still appear under a manager lens. When NULL, all assignment-derived roles (including descendants) are used.include_managed_roles(optional, defaultTRUE): whenFALSE, use only the exactin_app_rolelens instead of managed descendants.- Returns:
SPEC_NAME VARCHAR,SPEC_ALIAS VARCHAR,DESCRIPTION VARCHAR,OWNER_ROLE VARCHAR,ACCESS_TYPE VARCHAR,GUEST_ROLE_NAME VARCHAR,RESTRICTED_PATH VARCHAR,IS_MULTI_ROLE BOOLEANIS_REFERENCE_SPEC BOOLEAN- Example:
CALL airlock.agent.list_my_specs();
CALL airlock.agent.list_my_specs('budget_analyst');
airlock.agent.create_spec_from_template(template_name VARCHAR, new_spec_name VARCHAR, spec_alias VARCHAR DEFAULT NULL, spec_config_overrides VARIANT DEFAULT NULL)- Description: Creates a new spec from a spec template that is assigned to at least one of the caller’s Airlock roles (
core.spec_template_assignments) or markedis_public. Requires a named license seat like otheragent.*calls. Overrides honortemplate_rules.field_access(locked vs editable paths). When the template definestemplate_rules.forced_owner_role, new specs use that owner (e.g. centralapp_admingovernance) even though the caller could not choose that owner onairlock.admin.create_specalone—this is intentional admin-delegated behavior. The installed publicdefault_spec_draft_templateforces owner roleapp_adminand unpublished status so agents can draft specs safely for later admin publication. Ifforced_owner_roleis omitted while the template’s embedded owner is outside the caller’s assignment subtree, creation fails with an owner access error—see Guidance for AI agents and automation above. - Parameters:
template_name(required): assigned or public spec template name.new_spec_name(required): new spec name for the clone (canonicalized).spec_alias(optional): optional spec alias.spec_config_overrides(optional): overrides for editable fields (dot-path keys).- Returns:
SPEC_NAME VARCHARSTATUS VARCHARIS_CREATED BOOLEANVALIDATION VARIANTCODE VARCHARMESSAGE VARCHARISSUES VARIANT- Branch on
STATUSandCODE; useVALIDATIONonly for detailed form feedback. CommonCODEvalues includeCREATED,TEMPLATE_ACCESS_DENIED,TEMPLATE_INACTIVE, andSPEC_TEMPLATE_LOOKUP_FAILED. - Example:
CALL airlock.agent.create_spec_from_template(
'finance_spec_template',
'fin_budget_requests',
spec_alias => 'FIN Budget Requests',
spec_config_overrides => PARSE_JSON('{"core_config.description": "FY26"}')
);
airlock.agent.list_my_files(spec_name VARCHAR, in_app_role VARCHAR DEFAULT NULL)- Description: Lists files in a spec the caller can access.
- Parameters:
spec_name(required)in_app_role(optional): restrict to a role directory for multi‑role specs.- Returns: Snowflake
LISTcolumns (name,size,md5,last_modified). - Example:
CALL airlock.agent.list_my_files('budget_requests');
CALL airlock.agent.list_my_files('budget_requests', 'budget_analyst');
airlock.agent.spec_state(spec_name VARCHAR, in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE)- Description: Returns compact change tokens for one accessible spec without retrieving its governed rows. Poll
STATE_TOKEN; calldescribe_spec,list_my_files, or a governed read procedure only after it changes. Polllist_my_workindependently because due dates, expectation windows, and exceptions can change with time without rotating a spec token. - Authorization: Uses the same Airlock role, managed-role, path, and observer workflow filters as file discovery.
DATASET_TOKEN,ACTIVE_FILE_COUNT, andSTATE_TOKENare computed only from files visible through that role lens, so hidden paths cannot be inferred from token changes. Returns no row when access is denied or the spec no longer exists. - Returns:
SPEC_NAME, immutable active spec/data version identifiers and config hash,CONTRACT_TOKEN,ACCESS_SCOPE_TOKEN, visibleDATASET_TOKEN, combinedSTATE_TOKEN,ACTIVE_FILE_COUNT, andLAST_CHANGED_AT. - Example:
CALL airlock.agent.spec_state('budget_requests');
CALL airlock.agent.spec_state('budget_requests', 'budget_reviewer', FALSE);
airlock.agent.spec_states(spec_names ARRAY, in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE)- Description: Returns the same state-token rows as
agent.spec_statefor up to 100 unique, non-empty names while sharing license, role expansion, and compiled-access work. Prefer it when one agent polls several known specs. - Authorization: Applies one Airlock role lens to the batch. Input order is preserved after case-insensitive deduplication. Missing and inaccessible specs are omitted without disclosing which condition applied; failures while calculating state for an authorized spec remain procedure errors.
- Returns: Zero or one row per accessible requested spec, with the same columns and token semantics as
agent.spec_state. - Example:
CALL airlock.agent.spec_states(
ARRAY_CONSTRUCT('budget_requests', 'finance'),
'budget_reviewer',
FALSE
);
airlock.agent.file_state(spec_name VARCHAR, path VARCHAR, filename VARCHAR, in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE)- Description: Returns the UUID and compact mutable state for one accessible active logical file without retrieving its data or attachment bytes. Compare
FILE_STATE_ID; it rotates for data replacement, workflow changes, attachment changes, exact source-reference changes, and retained version-history changes. - Authorization: Uses the same role/path/observer filters as
spec_stateand returns no row when access is denied or the file is inactive, removed, or deleted. - Returns:
SPEC_NAME,PATH,FILENAME,UPLOAD_ID,FILE_STATE_ID,WORKFLOW_STEP,WORKFLOW_STATUS,ATTACHMENT_COUNT,REFERENCE_COUNT, andLAST_CHANGED_AT. - Example:
CALL airlock.agent.file_state('budget_requests', 'finadmin', 'fy27_budget');
airlock.agent.list_my_work(spec_name VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE)- Description: Lists the caller's unified operational inbox through their Airlock role lens. Includes files to submit or review, read-only observer items, and unmet, upcoming, overdue, or excepted expectation work.
AVAILABLE_ACTIONScontains exact workflow actions such asadvanceorreturnonly when the caller is authorized to perform them. Met expectations are omitted. Delegated file submission does not itself add the principal's work; delegate-only agents should uselist_my_delegationsplus delegated procedure results for on-behalf submission. - Returns:
WORK_ITEM_ID,SPEC_NAME,WORK_SOURCE,WORK_ITEM_TYPE,WORK_STATUS,SUMMARY,PATH,FILENAME,DUE_AT,DAYS_UNTIL_DUE,AVAILABLE_ACTIONS,ACCESS_REASON,IS_STRICT,UPDATED_AT,DETAILS. - Example:
CALL airlock.agent.list_my_work();
CALL airlock.agent.list_my_work('budget_requests', 'budget_reviewer', FALSE);
- Cross-spec source references (required evidence and immutable lineage) Admins declare allowed downstream→upstream pairs with
airlock.admin.create_spec_source_link.link_options.min_count > 0makes that source spec required before each downstream workflowadvance;max_countlimits pins andrequired_workflow_statusrequires the cited source to remain in that state when advancing. Users load the downstream Draft, discover eligible upstream files withairlock.agent.list_eligible_source_files, pin exact manifest rows withairlock.agent.add_file_reference, and inspect pins withairlock.agent.list_file_references. Missing or no-longer-qualifying evidence returnsSOURCE_REFERENCE_REQUIRED; a policy lookup failure returnsSOURCE_REFERENCE_CHECK_FAILED. Active pins also protect cited source files from explicit deletion and retention purge.remove_file_referenceremains available for repair or optional evidence, but a later advance will fail if removal puts the file below a required minimum. create_spec_source_linkreturnsLINK_PK,DOWNSTREAM_SPEC_NAME,SOURCE_SPEC_NAME,STATUS,IS_CREATED,VALIDATION,CODE,MESSAGE,ISSUES.disable_spec_source_linkreturnsLINK_PK,DOWNSTREAM_SPEC_NAME,SOURCE_SPEC_NAME,STATUS,IS_UPDATED,VALIDATION,CODE,MESSAGE,ISSUES.add_file_referencereturnsREFERENCE_PK,DOWNSTREAM_SPEC_NAME,SOURCE_SPEC_NAME,STATUS,IS_CREATED,VALIDATION,CODE,MESSAGE,ISSUES.remove_file_referencereturnsREFERENCE_PK,DOWNSTREAM_SPEC_NAME,STATUS,IS_UPDATED,VALIDATION,CODE,MESSAGE,ISSUES.- Branch on
STATUSand stableCODEvalues such asCREATED,UPDATED,ALLOW,VALIDATE_ONLY,ACCESS_DENIED,SPEC_NOT_FOUND,LINK_EXISTS,MANIFEST_NOT_FOUND,LINK_NOT_FOUND,WORKFLOW_MISMATCH,MAX_REFERENCES,REFERENCE_EXISTS,INSERT_FAILED,NOT_FOUND,NOOP, andUPDATE_FAILED. Workflow advancement can additionally returnSOURCE_REFERENCE_REQUIREDorSOURCE_REFERENCE_CHECK_FAILED.MESSAGEandISSUESare top-level for agents;VALIDATIONkeeps compact diagnostics.
airlock.agent.describe_spec(spec_name VARCHAR, in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE)- Description: Returns a safe descriptor for a spec the caller can access: column_config (field names, types, tests such as
not_null), file_rules (file format, delimiters, etc.), attachment_policy (attachments enabled, max count, allowed extensions, required), minimal core_config / file_workflow (step names and reviewer actions), ACCESSIBLE_PATHS (array of{path_scope, access_level}), and ACCESS_SUMMARY (owner, guest, reviewer, observer, attachment, and reference RLS explanation). Only paths this caller may use are included (other guests and their paths are not shown). Use ACCESSIBLE_PATHS to choose thepath_scopeto pass toload_data(e.g.public/full_accessfor write,public/read_accessfor read-only). Observer roles get read-only visibility for matching path/workflow-state filters. Access is enforced; returns no row if the caller has no access. - Parameters:
spec_name(required)in_app_role(optional): lens role for multi‑role specs.include_managed_roles(optional, defaultTRUE): whenFALSE, use only the exactin_app_rolelens instead of managed descendants.- Returns: One row:
SPEC_NAME,SPEC_ALIAS,DESCRIPTION,CORE_CONFIG(VARIANT),COLUMN_CONFIG(VARIANT),FILE_RULES(VARIANT),ATTACHMENT_POLICY(VARIANT),FILE_WORKFLOW(VARIANT),ACCESSIBLE_PATHS(VARIANT array of{path_scope, access_level}). - Example:
CALL airlock.agent.describe_spec('incoming_line_items');
CALL airlock.agent.describe_spec('incoming_line_items', 'automation_user'); -- ACCESSIBLE_PATHS shows e.g. [{"path_scope": "public/full_access", "access_level": "full_access"}]
airlock.agent.validate_data(spec_name VARCHAR, path VARCHAR DEFAULT NULL, file_content VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL, path_scope VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE, on_behalf_of_user VARCHAR DEFAULT NULL, delegation_id VARCHAR DEFAULT NULL)- Description: Validates a file for a spec using assignment-based authorization. Same semantics as
airlock.admin.validate_data; caller must have write access to the spec/path (owner or guest with write on that path). Exactly one ofpath(staged file) orfile_content(inline CSV string) required. Prefer named arguments. For normal delegated inline validation, passfile_contentandon_behalf_of_user; Airlock resolves the principal user's folder/lens and matching delegation. If role-lens FK/reference rules require an explicit role, pass the principal's Airlock role asin_app_role; Airlock checks it against the principal's assignments, not the actor's. Usedelegation_idonly if more than one active grant matches. Inline CSV validation includes FK/reference rules. Direct calls use the caller's Airlock role context; delegated calls use the principal user's resolved lens. - Returns:
SPEC_NAME,PATH,STATUS,IS_VALID,ISSUE_COUNT,ISSUES(VARIANT),CODE.CODEisVALIDon success; otherwise it is the first error/warning issue code. Delegation denials returnSTATUS = 'error'withCODEandISSUES[0].codeset to a stable delegation code such asDELEGATION_NOT_FOUND,DELEGATION_ACTION_NOT_ALLOWED, orINVALID_DELEGATION_POLICY. - Example:
CALL airlock.agent.validate_data(
spec_name => 'incoming_line_items',
file_content => 'record_id,category,attachment_filename\nINV-2025-001,operations,invoice_scan.pdf',
in_app_role => 'automation_user',
path_scope => 'public/full_access'
);
airlock.agent.load_data(spec_name VARCHAR, path VARCHAR DEFAULT NULL, file_content VARCHAR DEFAULT NULL, filename VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL, path_scope VARCHAR DEFAULT NULL, attachment_content_base64 VARCHAR DEFAULT NULL, attachment_filename VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE, on_behalf_of_user VARCHAR DEFAULT NULL, delegation_id VARCHAR DEFAULT NULL)- Description: Loads a file for a spec using assignment-based authorization. Same semantics as
airlock.admin.load_data; caller must have write access to the spec/path. Exactly one ofpath(staged file) orfile_content(inline CSV) required. For staged paths, Airlock requires an exact object digest before validation and verifies it again before the manifest transaction. A missing digest returnsSTAGED_FILE_HASH_UNAVAILABLE; replacement during validation returnsSTAGED_FILE_CHANGED_RETRY. Neither outcome writes validation evidence. Prefer named arguments. For normal delegated inline loads, passfile_content,filename, any attachment fields, andon_behalf_of_user; Airlock resolves the principal user's folder/lens and matching delegation. If validation needs a role-lens FK/reference check, pass the principal's Airlock role asin_app_role. Usedelegation_idonly if more than one active grant matches. For non-delegated guest-enabled specs with shared folders, passpath_scope(e.g.'public/full_access','public/append_access') when needed to disambiguate the target scope shown bydescribe_spec. Active Expectations are evaluated before the manifest row is written. Strict unmet expectations reject the load withEXPECTATION_BLOCKED; non-strict unmet expectations returnEXPECTATION_WARNINGbut still load. For no-workflow specs, the load target isbecame_activeand the visible current state isActive. Optional attachment: passattachment_content_base64(+ optionalattachment_filename) to register one attachment for the loaded file in the same call. If you later calladd_attachmentfor the same file, use the returnedPATH/FILENAMEand a distinctattachment_tagfor extra evidence. When the spec hasattachment_policy.attachment_required=true, an attachment is required in the same call: ifattachment_content_base64is omitted or empty, the load is rejected (no file is written to the manifest) and the procedure returns statuserrorwith issue codeATTACHMENT_REQUIRED. - Returns:
SPEC_NAME,PATH,FILENAME,STATUS,IS_LOADED,ROW_COUNT,ISSUE_COUNT,ISSUES,CODE.CODEisLOADEDon success; otherwise it is the first error/warning issue code. Delegation denials returnSTATUS = 'error'withCODEandISSUES[0].codeset to a stable delegation code. Expectation findings useEXPECTATION_BLOCKEDfor strict failures andEXPECTATION_WARNINGfor non-strict warnings. - Example (create file and attach in one call):
CALL airlock.agent.load_data(
spec_name => 'incoming_line_items',
file_content => 'record_id,category,attachment_filename\nINV-2025-001,operations,invoice_scan.pdf',
filename => 'INV-2025-001',
in_app_role => 'automation_user',
path_scope => 'public/full_access',
attachment_content_base64 => '<base64_encoded_image>',
attachment_filename => 'invoice_scan.pdf'
);
airlock.agent.select_my_files(spec_name VARCHAR, search_string VARCHAR DEFAULT NULL, regex_pattern VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL)- Description: Reads spec data the caller can access, with the same fuzzy/regex semantics as
airlock.admin.select_files. For specs withread_policy.row_filters_by_roleorread_policy.row_filter_by_username, reads fromAIRLOCK_DATA.ACTIVE.T_<spec_name>and applies the caller's effective-role and/or username row filters; raw stage reads are not used for those row-filtered specs. - Parameters:
spec_name(required)search_string(optional): fuzzy match in pathregex_pattern(optional): full regex whensearch_stringis NULL/emptyin_app_role(optional): restrict to a role directory- Returns: Same as
airlock.admin.select_files(configured data columns plussource_file,file_last_modified). On normal failure:STATUS,CODE,MESSAGE,ISSUES. - Example:
CALL airlock.agent.select_my_files('budget_requests');
CALL airlock.agent.select_my_files('budget_requests', 'report_2025');
CALL airlock.agent.select_my_files(spec_name => 'budget_requests', in_app_role => 'budget_analyst');
airlock.agent.select_reference_data(spec_name VARCHAR, object_key VARCHAR, row_limit NUMBER DEFAULT 500, in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE, row_offset NUMBER DEFAULT 0, record_reference_read_event BOOLEAN DEFAULT TRUE)- Description: Governed
SELECTagainst a read-only reference spec (core_config.is_reference_spec). Resolves access likedescribe_spec, then readsreference_config.object_pathsforobject_key(FQdatabase_name.schema_name.object_name). Optionalreference_config.row_filters_by_roleappends vettedANDpredicates for the caller’s effective Airlock roles; optionalreference_config.row_filter_by_usernameappends a trustedCURRENT_USER()predicate.row_limitdefaults to 500 (max 5000);row_offsetdefaults to 0 (max 100000) forLIMIT/OFFSETpagination. Whenrecord_reference_read_eventisTRUE(default), inserts a best-effortREFERENCE_READrow intocore.events. Whenreadable_columnsis set on the object path, only those columns are selected; otherwiseSELECT *; if the reference spec also providescolumn_config, those readable columns must be documented there. If the spec declaresrestricted_referencefor the object key, this procedure returnsRESTRICTED_REFERENCE_DENIED; useagent.get_reference_record. The account must grant the applicationSELECTon the underlying object. - Returns:
TABLE()— dynamic columns from the target object on success. On normal failure, returnsTABLE(STATUS, CODE, MESSAGE, ISSUES)withSTATUS='error'so agents can branch onCODEinstead of parsing Snowflake exception text. - Example:
CALL airlock.agent.select_reference_data('finance_ref', 'gl_entries');
CALL airlock.agent.select_reference_data(
spec_name => 'finance_ref',
object_key => 'gl_entries',
row_limit => 100,
in_app_role => 'budget_analyst'
);
CALL airlock.agent.select_reference_data(
spec_name => 'finance_ref',
object_key => 'gl_entries',
row_limit => 100,
in_app_role => 'budget_analyst',
row_offset => 100,
record_reference_read_event => FALSE
);
airlock.agent.get_reference_record(spec_name VARCHAR, object_key VARCHAR, lookup_value VARCHAR, purpose VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE, record_reference_read_event BOOLEAN DEFAULT TRUE)- Description: Governed one-record lookup for a read-only reference spec that declares
restricted_reference. Snowflake/upstream systems own masking, hashing, secure views, and projections; Airlock enforces the interaction shape, applies configured reference row filters, checks activeaction_limitExpectations before returning a restricted record, and logs safeREFERENCE_READevents with hashed lookup/purpose metadata. Restricted-reference events are always recorded because usage limits are enforced from that event ledger; the final Boolean is retained for signature stability. - Returns:
TABLE(STATUS, CODE, MESSAGE, SPEC_NAME, OBJECT_KEY, LOOKUP_KEY, ROW_COUNT, RECORD, ACCESS_CONTEXT, USAGE_CONTEXT, ISSUES). Common codes:OK,NOT_FOUND,NON_UNIQUE_LOOKUP_KEY,RESTRICTED_REFERENCE_DENIED,PURPOSE_REQUIRED,USAGE_LIMIT_BLOCKED,REFERENCE_READ_EVENT_FAILED.USAGE_CONTEXTincludes fields such asaction_limit_used(5/25) andaction_time_period(hour,day,week,month,ever, or a rolling-hours label) when an action-limit Expectation applies. - Example:
CALL airlock.agent.get_reference_record(
spec_name => 'customer_identity',
object_key => 'customers',
lookup_value => 'CUST-123',
purpose => 'support_case:CASE-456',
in_app_role => 'support_agent'
);
airlock.agent.edit_file_workflow(spec_name VARCHAR, path VARCHAR, filename VARCHAR, action VARCHAR, comment VARCHAR DEFAULT NULL, validate_only BOOLEAN DEFAULT FALSE, in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE, on_behalf_of_user VARCHAR DEFAULT NULL, delegation_id VARCHAR DEFAULT NULL)- Description: Moves file workflow state for files the caller can access. Same actions as
airlock.admin.edit_file_workflow(advance,return,archive,restore). Direct calls use the caller's Airlock role and assignment/workflow permissions (owner or guest with manage permission in the current step). Delegated calls passon_behalf_of_user; Airlock checks the active delegation, principal access, target file path, workflow step policy, and the principal's workflow PDP. The specdelegation_policyand active grant must both allowedit_file_workflowat the current workflow step. Strict Expectations for the target workflow milestone can block the transition unless an active Exception applies. Anadvancealso enforces active source-link minimums and returnsSOURCE_REFERENCE_REQUIREDwhen exact qualifying evidence has not been pinned. - Parameters:
spec_name,path,filename,action(required);comment(optional);validate_only(optional, defaultFALSE).in_app_role(optional): lens role; must be in caller’s effective roles; whenNULL, current context role is used.- Returns: Same as admin
edit_file_workflow:SPEC_NAME,PATH,FILENAME,STATUS,CODE,MESSAGE,IS_UPDATED,FROM_STEP,TO_STEP,ISSUES,DETAILS. Delegation denials returnSTATUS = 'error'with a stable delegation code inCODEandISSUES[0].code. - Example:
CALL airlock.agent.edit_file_workflow('budget_requests', 'default', 'report_2025', 'advance', 'Ready for review');
CALL airlock.agent.edit_file_workflow(spec_name => 'timesheets', path => 'joe', filename => 'joe_2026_05_17', action => 'advance', comment => 'Submitted by agent', on_behalf_of_user => 'joe');
airlock.agent.add_attachment(spec_name VARCHAR, file_path VARCHAR, file_filename VARCHAR, attachment_stage_path VARCHAR DEFAULT NULL, attachment_content_base64 VARCHAR DEFAULT NULL, attachment_filename VARCHAR DEFAULT NULL, attachment_type VARCHAR DEFAULT 'other', description VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE, attachment_tag VARCHAR DEFAULT NULL, on_behalf_of_user VARCHAR DEFAULT NULL, delegation_id VARCHAR DEFAULT NULL)- Description: Adds an attachment to an existing file. Same semantics as
airlock.admin.add_attachmentwith in-procedure authorization (caller must have access to the spec/file and the file must be in a workflow step that allows attachment management). Exactly one ofattachment_stage_pathorattachment_content_base64(+ optionalattachment_filename) required. For delegated follow-up attachments, passon_behalf_of_user; Airlock checks the active delegation, principal access, target file path, and workflow step. Ifon_behalf_of_useris omitted, the call is direct actor work and can correctly fail whenfile_pathbelongs to the principal's isolated folder. Usedelegation_idonly if more than one active grant matches. - Guest roles (e.g.
automation_user): The procedure is granted toapp_agent. A user who only has guest access to a spec (e.g. assigned roleautomation_userwithfull_accesson the spec’spublic/full_accessfolder) can attach files to existing files in that path by calling this procedure with the samefile_path/file_filename. Passin_app_role => 'automation_user'(or the guest role name) so the PDP evaluates access under that role; the file path must be under a directory where that guest has write/update (e.g.public/full_access). For delegated calls, omitin_app_roleunless selecting the principal's role lens; delegation never uses the actor's role to expand the principal's authority. - Returns:
SPEC_NAME,FILE_PATH,FILE_FILENAME,ATTACHMENT_ID,ATTACHMENT_STAGE_PATH,STATUS,CODE,MESSAGE,SIZE_BYTES,ISSUES. On workflow denial,CODE='ACCESS_DENIED_WORKFLOW_STATE'; on delegation denial,CODEis the stable delegation code. - Example (guest role attaching to an existing file):
-- Guest role with write on public/full_access attaches supporting evidence to an existing row file.
CALL airlock.agent.add_attachment(
spec_name => 'incoming_line_items',
file_path => 'public/full_access',
file_filename => 'INV-2025-001',
attachment_content_base64 => '<base64_encoded_image>',
attachment_filename => 'invoice_scan.pdf',
attachment_type => 'other',
description => 'Supporting scan',
in_app_role => 'automation_user'
);
CALL airlock.agent.add_attachment(
spec_name => 'demo_employee_reimbursements',
file_path => 'asmith',
file_filename => 'rb_asmith_001',
attachment_content_base64 => '<base64_encoded_image>',
attachment_filename => 'receipt.png',
on_behalf_of_user => 'asmith'
);
airlock.agent.replace_attachment(spec_name VARCHAR, file_path VARCHAR, file_filename VARCHAR, attachment_id VARCHAR, attachment_stage_path VARCHAR DEFAULT NULL, attachment_content_base64 VARCHAR DEFAULT NULL, attachment_filename VARCHAR DEFAULT NULL, attachment_type VARCHAR DEFAULT 'other', description VARCHAR DEFAULT NULL, in_app_role VARCHAR DEFAULT NULL, include_managed_roles BOOLEAN DEFAULT TRUE, attachment_tag VARCHAR DEFAULT NULL, on_behalf_of_user VARCHAR DEFAULT NULL, delegation_id VARCHAR DEFAULT NULL)- Description: Replaces an attachment by
attachment_id; same content options as admin (stage or base64). Optionalin_app_rolelens. Subject to same workflow and access checks as add. For delegated replacement, passon_behalf_of_user; Airlock checks the active delegation, principal access, target file path, workflow step, and requiresreplace_attachmentin both the specdelegation_policyand the active grant. Usedelegation_idonly if more than one active grant matches. Replacement is permanent in Airlock, so grant it only for specs where corrected evidence replacement is part of the business process. - Returns:
SPEC_NAME,FILE_PATH,FILE_FILENAME,ATTACHMENT_ID,ATTACHMENT_STAGE_PATH,STATUS,CODE,MESSAGE,SIZE_BYTES,ISSUES. On delegation denial,CODEis the stable delegation code.
airlock.agent.delete_attachment(spec_name VARCHAR, file_path VARCHAR, file_filename VARCHAR, attachment_id VARCHAR)- Description: Deletes an attachment by
attachment_idfor the given file; subject to same workflow/access rules as add/replace. This is a direct-role destructive action, not an on-behalf-of delegation surface in the current contract.
#4. Common Workflows
#4.1 Manage in‑app roles and assignments (admin + user)
- Goal: Define application roles and assign users to them, then let users see their own roles/assignments.
- Typical admin flow:
-- Create a new role (one JSON object per array element)
CALL airlock.admin.create_roles([
PARSE_JSON('{
"role_name": "budget_analyst",
"description": "Can view and manage budget specs",
"is_spec_admin": true
}')
]);
-- Assign a user to the role (same style as create_assignments)
CALL airlock.admin.create_assignments([
PARSE_JSON('{
"assignment_name": "jane.budget_analyst",
"user_id": "JANE",
"assigned_role": "budget_analyst"
}')
]);
-- Inspect roles/assignments (admin)
CALL airlock.observe.roles();
CALL airlock.observe.assignments('JANE');
- Typical user flow:
-- See which Airlock roles you have
CALL airlock.agent.list_my_roles();
-- See which assignments you can manage
CALL airlock.agent.list_my_assignments();
#4.2 Spec lifecycle: from config to current data
- Goal: Create or update a spec, validate a staged file, load it, and inspect current versions.
- Admin flow:
-- Validate a spec config (without changing data)
CALL airlock.admin.validate_spec(:spec_config_json);
-- Create a new spec (or alter an existing one)
CALL airlock.admin.create_spec(:spec_config_json);
-- or
CALL airlock.admin.alter_spec('budget_requests', :spec_config_json);
-- Validate a staged file for the spec
CALL airlock.admin.validate_data(
'budget_requests',
'budget_requests/path/abc123/report_2025.csv.gz'
);
-- Load the validated file into FILE_MANIFEST
CALL airlock.admin.load_data(
'budget_requests',
'budget_requests/path/abc123/report_2025.csv.gz'
);
-- Inspect active file versions
CALL airlock.observe.file_versions('budget_requests');
- User‑side access:
-- List specs you can see
CALL airlock.agent.list_my_specs();
-- List files you can see for a spec
CALL airlock.agent.list_my_files('budget_requests');
-- Read current data for a spec you can access
CALL airlock.agent.select_my_files('budget_requests');
#4.3 Retention and purge (outdated files)
- Goal: Configure retention for a spec and safely purge outdated files.
- Admin flow:
-- Create or update a retention policy for a spec
CALL airlock.admin.create_retention_policy(PARSE_JSON('{
"policy_name": "Budget Retention",
"spec_name": "budget_requests",
"retention_days": 365,
"retention_min_versions": 2,
"retention_scope": "non_current"
}'));
-- Preview which files are eligible for purge
CALL airlock.observe.purge_candidates('budget_requests');
-- Dry-run delete to see what WOULD be removed
CALL airlock.admin.delete_outdated_files('budget_requests', dry_run => TRUE, limit => 100);
-- Commit delete (REMOVE from stage + mark history)
CALL airlock.admin.delete_outdated_files('budget_requests', dry_run => FALSE, limit => 100);
-- Audit retention removals (see FILE_RETENTION_PURGE in list_events event_types)
CALL airlock.observe.activity(event_types => 'FILE_RETENTION_PURGE', limit_rows => 200);
#4.4 Auditing activity and history
- Goal: Inspect application activity and historical changes to spec data.
- Admin activity audit:
-- Recent events
CALL airlock.observe.activity();
-- Events for a specific user
CALL airlock.observe.activity('jane.doe');
-- Events for a specific Airlock role in a time window
CALL airlock.observe.activity(
event_types => 'FILE_UPLOAD,FILE_DELETE,FILE_RETENTION_PURGE',
in_app_role => 'budget_analyst',
start_time => '2025-01-01'::TIMESTAMP_LTZ,
end_time => CURRENT_TIMESTAMP(),
limit_rows => 500
);
#4.5. Shared view and FK validation
- Goal: Define a shared view (e.g. employees), register it, and create a spec whose column values must match that view (e.g. email_address must exist in employees.email).
- Prerequisite: Customer has granted USAGE and CREATE VIEW on
AIRLOCK_DATA.SHAREDto the role that will create views (see Shared schema grants). - Admin flow:
-- 1) Create shared schema if needed (idempotent)
CALL airlock.admin.create_shared_schema();
-- 2) Customer creates the view (standard SQL; role must have CREATE VIEW on AIRLOCK_DATA.SHARED)
CREATE OR REPLACE VIEW AIRLOCK_DATA.SHARED.employees AS
SELECT id, name, email FROM my_db.my_schema.employee_source;
-- 3) Register the view so Airlock can use it in FK rules
CALL airlock.admin.register_reference(
reference_name => 'employees_ref',
ref_type => 'shared_view',
schema_name => 'SHARED',
view_name => 'employees',
spec_name => NULL,
ref_columns => PARSE_JSON('["email"]')
);
-- 4) Create (or alter) a spec with a FK rule in spec_config.rules
CALL airlock.admin.create_spec(PARSE_JSON('{
"core_config": {"spec_name": "contacts_shared_fk", "owner_role": "app_admin", ...},
"column_config": [
{"name": "full_name", "type": "string", "tests": ["not_null"], "description": ""},
{"name": "email_address", "type": "string", "tests": ["not_null", "foreign_key"], "description": ""}
],
"rules": [{"type": "foreign_key", "field": "email_address", "ref": {"schema": "SHARED", "view": "employees", "column": "email"}}],
"file_rules": { ... },
"guest_access": { ... },
"sample_data": { ... }
}'));
-- 5) Validate and load files (FK validation runs; invalid rows are reported and blocked)
CALL airlock.admin.validate_data('contacts_shared_fk', 'path/to/file.csv.gz');
CALL airlock.admin.load_data('contacts_shared_fk', 'path/to/file.csv.gz');
#4.6. File workflow and attachments
- Goal: Move files through workflow steps (advance/return/archive/restore) and attach files to specs or to specific data files.
- File workflow (specs with
file_workflow: true):
-- Admin: move file to next step
CALL airlock.admin.edit_file_workflow('budget_requests', 'default', 'report_2025', 'advance', 'Ready for review');
-- User: same, with Airlock role context
CALL airlock.agent.edit_file_workflow('budget_requests', 'default', 'report_2025', 'advance', 'Ready for review');
- Attachments (specs with
attachment_policy.attachments_enabled): add/replace/delete are allowed only when the file is in a workflow step that permits file/attachment management.attachment_tagidentifies the logical attachment tag and defaults todefaultwith one active row. On denial, procedures returnCODE='ACCESS_DENIED_WORKFLOW_STATE'.
-- Admin: spec-level attachment (e.g. when creating spec + first attachment)
CALL airlock.admin.attach_to_spec(
spec_name => 'my_spec',
attachment_content_base64 => 'YWJj',
attachment_filename => 'note.txt',
attachment_type => 'other',
description => 'Sample'
);
-- Admin or user: add attachment to an existing file (path/scope + filename)
CALL airlock.admin.add_attachment(
spec_name => 'my_spec',
file_path => 'default',
file_filename => 'finance',
attachment_content_base64 => 'base64...',
attachment_filename => 'receipt.pdf',
attachment_type => 'document',
description => 'Q1 receipt',
attachment_tag => 'receipt'
);
CALL airlock.agent.add_attachment(
spec_name => 'my_spec',
file_path => 'default',
file_filename => 'finance',
attachment_content_base64 => 'base64...',
attachment_filename => 'receipt.pdf',
attachment_type => 'other',
description => 'Receipt',
in_app_role => 'my_role',
attachment_tag => 'receipt'
);
#4.7. User: create a file and attach a file
- Goal: As an app agent (or an AI agent acting as that user), create a new data file in a spec and optionally attach a binary (e.g. CSV row + PDF receipt) in the same call or in a follow-up call. This is the typical flow for line-of-business submissions where evidence is attached to a logical record.
- Prerequisites: Caller has access to the spec (owner or guest). For guest access on a spec with a shared folder, the caller must use
path_scope(e.g.'public/full_access') when loading the file. Calldescribe_spec(spec_name, in_app_role, include_managed_roles)and use the ACCESSIBLE_PATHS array to see whichpath_scopevalues you may use and their access level (e.g.public/full_accessfor write); only your paths are listed, not other guests. - One-call option (recommended when the spec has
attachment_required): Passattachment_content_base64and optionalattachment_filenametoload_dataso the file and attachment are registered in one procedure call. When the spec hasattachment_policy.attachment_required=true, you must provide the attachment in this call; otherwise the load is rejected and no file is uploaded (procedure returns statuserror, issue codeATTACHMENT_REQUIRED).
CALL airlock.agent.describe_spec('incoming_line_items', 'automation_user', TRUE);
CALL airlock.agent.validate_data(
spec_name => 'incoming_line_items',
file_content => 'record_id,category,attachment_filename\nINV-2025-001,operations,invoice_scan.pdf',
in_app_role => 'automation_user',
path_scope => 'public/full_access'
);
CALL airlock.agent.load_data(
spec_name => 'incoming_line_items',
file_content => 'record_id,category,attachment_filename\nINV-2025-001,operations,invoice_scan.pdf',
filename => 'INV-2025-001',
in_app_role => 'automation_user',
path_scope => 'public/full_access',
attachment_content_base64 => '<base64_encoded_image>',
attachment_filename => 'invoice_scan.pdf'
);
- Two-step option (load then attach in a second call):
- Describe the spec → Validate → Load (without attachment params).
- Attach with
add_attachment(spec_name, file_path, file_filename, ...)using the samePATHandFILENAMEreturned byload_data.
- Reimbursements (CSV + receipt image/PDF) same pattern: use
load_datawithattachment_content_base64(andattachment_filename) in the same call, or load thenadd_attachmentwith the samefile_path/file_filename. - Submit-to-workflow option: If the business action means "submit for review", first check the spec workflow. Some specs configure
Submittedas the initial workflow step, soload_dataalready creates the reviewer-facing item andDraftis only used for reviewer pushback. If the spec starts inDraft, first load the clean business payload, then calledit_file_workflow(action => 'advance')to move the returnedPATH/FILENAMEto Submitted. For delegated submissions, pass the sameon_behalf_of_userto both calls and requireedit_file_workflowin both the spec delegation policy and active grant. Reviewer pushback should return the file to Draft with a workflow comment; the principal then sees the item in My Work and can resubmit before any expectation due date.
#4.8. AI and agent integration
- Goal: Integrate AI assistants or automation so they can submit data and files into Snowflake through Airlock without direct stage or broad write privileges. See Product intent in
docs/product_intent.mdfor the vision (receipts, budget drafts, etc.). - Procedure surface for agents (all under
airlock.agent.*, granted toapp_agent): - Discovery:
list_my_roles(),list_my_specs(in_app_role, include_managed_roles),describe_spec(spec_name, in_app_role, include_managed_roles)to learn schema, required fields, and attachment rules. - Efficient polling: cache
spec_state(...).STATE_TOKENfor one spec, or callspec_states(...)to poll up to 100 known specs under one role lens. Compare tokens before retrieving descriptors, file lists, or governed data. Polllist_my_workon its own cadence because time-based expectation and exception changes do not rotateSTATE_TOKEN. After selecting one logical file, cachefile_state(...).FILE_STATE_IDto detect data replacement, workflow movement, attachment changes, or source-reference changes without fetching the file. Treat an omitted or missing row as loss of access, retirement, deletion, or a nonexistent object and perform normal discovery rather than guessing which occurred. - Delegation discovery:
list_my_delegations('received')to learn active grants where the agent may act for another user; passinclude_inactive => TRUEonly when explaining future, expired, or revoked grants. - Author payloads: build records from
describe_specfield names and types. Tooling may convert records JSON to CSV forfile_contentonly for flat specs or specs with declaredvariantcolumns for nested values. Keep Airlock lifecycle fields such as workflow state, approvals, validation status, and audit metadata out of the source payload. - Create file (and optional attachment in one call):
validate_data(...)thenload_data(..., path_scope, attachment_content_base64, attachment_filename). When the spec hasattachment_required, pass the attachment in this call or the load fails. For delegated inline loads, omitpath_scopeunless deliberately targeting a non-default shared scope; Airlock resolves the principal's folder. - Submit for review: after a successful load, inspect the returned/current workflow state. If the spec configured
Submittedas its initial step, the load already created the reviewer-facing item. If the file is in Draft, calledit_file_workflow(..., action => 'advance')to reach Submitted only when workflow policy allows it. For delegated work, keep passingon_behalf_of_user; if the grant does not include workflow movement, report the loaded Draft file instead. - Attach to file (if not done in
load_data, or for additional evidence):add_attachment(spec_name => ..., file_path => ..., file_filename => ..., attachment_content_base64 => ..., attachment_filename => ..., attachment_tag => ...);file_path/file_filenamemust match the loaded file. For delegated follow-up evidence, also passon_behalf_of_user; use a distinctattachment_tagwhen the originalload_dataalready attached the default evidence. - Read:
spec_state(spec_name),spec_states(spec_names), andfile_state(spec_name, path, filename)for cheap change detection;list_my_files(spec_name)andselect_my_files(spec_name)for governed file discovery/data; for reference specs,select_reference_data(spec_name, object_key, ...). - Authorization: Procedures enforce Airlock roles and assignments; guest roles (e.g.
automation_user) only see and write to specs/paths they are granted (e.g.public/full_access). Passin_app_rolewhen the user has multiple roles so the PDP evaluates under the correct lens. - Native MCP: An administrator may opt in to separate read-only
core.airlock_agent_mcpandcore.airlock_observer_mcpservers withadmin.enable_mcp_server. They expose only identity/access context, work, safe spec descriptions, and compact spec/file state. They do not expose governed or administrative mutation, governed row reads, restricted-reference reads,admin.*, or attachment bytes. Except for the non-claiming access-context tool, agent MCP calls retain ordinary named-license enforcement and may auto-assign a license when consumer policy enables it; observer MCP calls do not require or assign named licenses. Snowflake's managed endpoint targets internal scalarVARIANTadapters because its SQL API accepts JSONV2 rather than Arrow result sets from table-returning Python procedures; those adapters retain the same handler-level policy checks and do not change the public API. Before relying on a new PAT-based client, runscripts/run_batch62_mcp_identity_probe.pywith two independently issued, role-restricted tokens and require distinct matchingUSER_IDvalues. - External provenance: Buzz/Grok-style channel, thread, event, bot, or public-key ids are authority-neutral provenance. Keep a bounded
airlock.external-provenance/v1envelope in the workspace commitment receipt and optionally place onlysourceplus an opaquecorrelation_idinQUERY_TAG. Never derive a Snowflake principal, Airlock role, or delegation from workspace metadata.
#5. Where to Go Next
- For examples and usage patterns, see:
- Streamlit code under
src/airlock/streamlit/lib/** - Tests under
tests/airlock/app_setup_scripts/procedures/scripts/
This document should be kept in sync with:
src/airlock/app_setup_scripts/procedures/**(SQL procedure definitions)src/airlock/app_setup_scripts/procedures/scripts/**(Python handlers)src/airlock/app_setup_scripts/procedures/scripts/admin_help.py(admin help text)src/airlock/app_setup_scripts/procedures/scripts/user_help.py(agent help text)docs/attachments_batch26.md(attachment policy companion; Streamlit behavior)