# Openflow Bootstrap

> Internal bootstrap skill shared by all OpenFlow demo skills. Scaffolds project files, resolves Snowflake connection, installs tools, and confirms runtime readiness.

- Skill: `snowflake-labs/openflow-bootstrap` (Agent Skill)
- Install (CLI): `npx skillmds@latest add snowflake-labs/openflow-bootstrap`
- Raw SKILL.md: https://api.skillmd.com/api/skills/snowflake-labs/openflow-bootstrap/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: snowflake-labs (https://skillmd.com/u/snowflake-labs)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/snowflake-labs/openflow-bootstrap

---


# 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.toml`
- `sql/setup.sql`
- `sql/cleanup.sql`
- `.gitignore`

### Step 3: Check mise

```bash
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:
1. `$PROJECT_DIR/.sfutils/manifest.toml` -> `[snowflake].connection` (if manifest exists)
2. `$PROJECT_DIR/.env` -> `SNOWFLAKE_DEFAULT_CONNECTION_NAME`
3. `snow 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/.env` and `$PROJECT_DIR/.sfutils/manifest.toml`

### Step 5: Create .env

If `$PROJECT_DIR/.env` does not exist:
1. Copy from `$PROJECT_DIR/.env.example`
2. Set `SNOWFLAKE_DEFAULT_CONNECTION_NAME` from Step 4
3. Ask for demo-specific values (caller provides the list of required env vars)
4. Write all values

### Step 6: Create Manifest

If `$PROJECT_DIR/.sfutils/manifest.toml` does not exist:
1. Copy from `$PROJECT_DIR/.sfutils/manifest.toml.example`
2. Fill `[snowflake].connection` from Step 4

### Step 7: Install Tools and Deps

Run from `$PROJECT_DIR` (where `.mise.toml` was copied):

```bash
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:

```bash
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.

```bash
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` (or `GDRIVE_DEMO_RUNNER` if no prefix)
- Service role name: `{PREFIX}_ACCESS` (or `GDRIVE_DEMO_ACCESS` if no prefix)
- Role restriction: `OPENFLOW_ADMIN`
- Database: value from `.env` `OPENFLOW_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` (or `EGRESS_RULE` if no prefix)
- Hosts: `googleapis.com`, `www.googleapis.com`, `oauth2.googleapis.com`, `accounts.google.com`
- EAI name: `{PREFIX}_OPENFLOW_EAI` (or `OPENFLOW_EAI` if 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:
> 1. Go to Snowsight > Ingestion > OpenFlow > Deployments > your runtime
> 2. Edit the runtime and add the EAI to External Access Integrations
> 3. 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:

```bash
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_question` with `type: "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/.env` exists with a verified Snowflake connection
- `$PROJECT_DIR/.sfutils/manifest.toml` exists with PAT, NW, and EAI tracked
- `mise`, `snow`, `uv`, and `nipyapi` are 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

