OpenFlow Demo Bootstrap
Shared prerequisite gate for all OpenFlow demo skills. Scaffolds the user's project folder and verifies the environment is ready before any demo-specific logic runs.
When to Load
Called by demo skills (e.g., openflow-gdrive-demo) as their first gate. Not user-invocable.
Workflow
All operations target the user's current working directory ($PWD), referred to as PROJECT_DIR. The plugin install directory (PLUGIN_ROOT) is only used as a source for template files.
Step 1: Identify Paths
PLUGIN_ROOT= the directory containing this SKILL.md's grandparent (i.e., the repo root with.cortex-plugin/plugin.json)PROJECT_DIR= the user's current working directory ($PWD)
Step 2: Scaffold Project Files
Copy from PLUGIN_ROOT to PROJECT_DIR (skip files that already exist):
.env.example.sfutils/manifest.toml.example.mise.tomlsql/setup.sqlsql/cleanup.sql.gitignore
Step 3: Check mise
command -v mise
If missing: STOP. Tell user to install mise (https://mise.jdx.dev/getting-started.html).
Step 4: Resolve Snowflake Connection
Always confirm before proceeding -- env vars may be stale from a previous session.
Resolution order:
$PROJECT_DIR/.sfutils/manifest.toml->[snowflake].connection(if manifest exists)$PROJECT_DIR/.env->SNOWFLAKE_DEFAULT_CONNECTION_NAMEsnow connection list-> ask the user which connection to use
After resolution:
- Verify with
snow connection test -c <connection> - Write the confirmed connection to both
$PROJECT_DIR/.envand$PROJECT_DIR/.sfutils/manifest.toml
Step 5: Create .env
If $PROJECT_DIR/.env does not exist:
- Copy from
$PROJECT_DIR/.env.example - Set
SNOWFLAKE_DEFAULT_CONNECTION_NAMEfrom Step 4 - Ask for demo-specific values (caller provides the list of required env vars)
- Write all values
Step 6: Create Manifest
If $PROJECT_DIR/.sfutils/manifest.toml does not exist:
- Copy from
$PROJECT_DIR/.sfutils/manifest.toml.example - Fill
[snowflake].connectionfrom Step 4
Step 7: Install Tools and Deps
Run from $PROJECT_DIR (where .mise.toml was copied):
mise trust # auto-trust the config (avoids interactive prompt)
mise install # ensures snow CLI + python 3.12 are available
mise run setup # uv sync + nipyapi verification
If any command fails: STOP and show the error.
Step 7b: Bootstrap Snowflake Objects
Run the SQL setup to create roles, database, and warehouse:
mise run sf-setup
This executes sql/setup.sql which creates OPENFLOW_ADMIN, OPENFLOW_DEMOS_ROLE, the database, warehouse, and all grants. Idempotent -- safe to run multiple times.
Step 7c: Copy Sample Documents (conditional)
Only run this step if the user selected "I need to set this up" in Gate 1. Skip entirely when the user confirmed their Shared Drive already has documents.
mise run sample-data
This sparse-checkouts sample documents from the demo content repo into $PROJECT_DIR/.content/. Tell the user:
Sample documents are available at
.content/openflow-unstructured-data-pipeline-demo/sample-data/google-drive-docs/. Upload these to your Google Shared Drive if you need test data.
Step 8: Create Service User and PAT
STOP. Invoke $sfutils:programmatic-access-token with these requirements:
- Service user name:
{PREFIX}_RUNNER(orGDRIVE_DEMO_RUNNERif no prefix) - Service role name:
{PREFIX}_ACCESS(orGDRIVE_DEMO_ACCESSif no prefix) - Role restriction:
OPENFLOW_ADMIN - Database: value from
.envOPENFLOW_DATABASE(default:OPENFLOW_DEMOS) - Network policy: yes (lock to current IP)
- Manifest path:
$PROJECT_DIR/.sfutils/manifest.toml
Wait for the skill to confirm completion. It will:
- Create the service user and role
- Create a network policy locked to the current IP
- Generate the PAT with OPENFLOW_ADMIN role restriction
- Store the PAT in OS keychain
- Grant OPENFLOW_ADMIN to the user and role
- Write results to manifest under
[pat.openflow-runner]
Do NOT write any SQL for this step. Do NOT proceed until the skill confirms success.
Step 9: Create Network Rule and EAI
STOP. Invoke $sfutils:network-rule with these requirements:
- Type: EGRESS
- Rule name:
{PREFIX}_EGRESS_RULE(orEGRESS_RULEif no prefix) - Hosts:
googleapis.com,www.googleapis.com,oauth2.googleapis.com,accounts.google.com - EAI name:
{PREFIX}_OPENFLOW_EAI(orOPENFLOW_EAIif no prefix) - Database/Schema for rule:
OPENFLOW_DEMOS.NETWORKS - Grant USAGE on EAI to role:
OPENFLOW_DEMOS_ROLE - Manifest path:
$PROJECT_DIR/.sfutils/manifest.toml
Wait for the skill to confirm completion. It will:
- Create the EGRESS network rule
- Create the External Access Integration (EAI)
- Grant USAGE on the EAI to the specified role
- Write results to manifest under
[openflow]
Do NOT write any SQL for this step. Do NOT proceed until the skill confirms success.
After the skill completes, ask the user to attach the EAI to the runtime via Snowsight:
EAI has been created and USAGE granted. Please attach it to your runtime:
- Go to Snowsight > Ingestion > OpenFlow > Deployments > your runtime
- Edit the runtime and add the EAI to External Access Integrations
- Save and confirm the runtime returns to ACTIVE
STOP: Wait for user to confirm EAI is attached.
Step 10: Discover OpenFlow Runtime and Create Profile
STOP. Invoke $openflow with "setup" intent.
The $openflow skill will discover runtimes and create a nipyapi profile. When it asks for a PAT or bearer token during its auth step, extract the token from keychain (created in Step 8) and provide it:
sfutils pat show-pat --user {SA_USER} --yes 2>/dev/null | grep -o 'eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*'
Provide the extracted JWT value as the answer to $openflow's PAT prompt. This ensures both skills use the same token.
The $openflow skill will:
- Discover deployments and runtimes
- Extract
server_url,execute_as_role,runtime_key - Create the nipyapi profile using the provided PAT
- Verify connectivity
- Write results to
~/.snowflake/cortex/memory/openflow_infrastructure_<CONNECTION>.json
If $openflow fails (no runtimes found, permissions error):
- STOP. Guide user to create a runtime via Snowsight > Ingestion > OpenFlow > Deployments.
After $openflow completes, resolve execute_as_role:
Ask the user:
- Use
ask_user_questionwithtype: "text",defaultValue: "OPENFLOW_DEMOS_ROLE" - Question: "What role does the runtime execute as? (Check Snowsight > OpenFlow > Deployment settings)"
Write execute_as_role to manifest.
Do NOT proceed until $openflow confirms a working nipyapi profile.
Output
On success, the calling skill can assume:
$PROJECT_DIR/.envexists with a verified Snowflake connection$PROJECT_DIR/.sfutils/manifest.tomlexists with PAT, NW, and EAI trackedmise,snow,uv, andnipyapiare available on PATH- Snowflake objects exist (roles, database, warehouse, grants)
- PAT exists with OPENFLOW_ADMIN role restriction, stored in keychain
- EAI created and attached to runtime (user confirmed)
- nipyapi profile exists and connectivity to NiFi is verified
Stopping Points
- Step 3: mise not installed
- Step 4: no valid Snowflake connection
- Step 7: tool installation fails
- Step 8: PAT skill fails
- Step 9: network-rule skill fails
- Step 10: $openflow discovery fails or connectivity check fails