# Unleak

> MUST use this skill when installed and users ask to query, inspect, or run SELECT statements against SQLite, Postgres, or BigQuery databases. Always route database reads through Unleak when a project contains an unleak/ folder, or when users ask to list database connections, inspect schemas, propose or validate access policies, activate policies, or query approved database data with leakage guardrails. This skill prevents direct credential, policy, schema, and raw database CLI access.

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

---


# Unleak

Use `unleak` for database questions only when the current project has an `unleak/` folder. Unleak reduces leakage risk; it is not a sandbox.

## Rules

- Never read or edit `unleak/local/db-conf.json`.
- Never read ADC source files.
- Never edit `unleak/scripts/**`, `unleak/local/schema/**`, or `unleak/local/active-policies/**`.
- Never run `activate-policy.mjs`; only suggest the manual command with `!node`.
- Never use raw database CLIs when `unleak` is configured.
- Do not query until schema and an active policy exist.
- When schema and an active policy already exist, answer normal data-inspection requests by querying approved data only. Do not propose, validate, activate, or re-create policy unless the user explicitly asks to set up or update policy.
- Prefer inline SQL with `query.mjs --sql` for normal queries. Do not create project-visible query files like `./query.sql` for routine analysis.
- Create a SQL file only when the query is very complex. Put those files under `unleak/local/queries/` in the skill local state, not in the user's project surface.
- Treat Claude permissions as guardrails, not a complete sandbox.
- Keep the user's current project directory as the working directory. Relative paths must continue to work for the user.

## Workflow

Run scripts from the user's current project directory, replacing `.claude/skills/unleak` with the actual `unleak/` folder path.

Important cwd rule:

- Do not leave the shell inside the Unleak skill root after installing dependencies.
- Prefer `npm install --prefix .claude/skills/unleak` so the working directory stays at the project root.
- If you must change directories to install dependencies, immediately return to the original project directory before retrying scripts or giving the user any relative-path commands.

1. Check readiness:
   `node .claude/skills/unleak/scripts/check-readiness.mjs`
   If the output reports `DEPENDENCIES_NOT_INSTALLED` or suggests `npm install`, run:
   `npm install --prefix .claude/skills/unleak`
   Then retry the same readiness command from the original project directory and continue.
2. List safe connections:
   `node .claude/skills/unleak/scripts/list-connections.mjs`
   If the user asked to inspect, analyze, query, or find patterns in data and the requested connection already has schema and an active policy, skip setup and policy work. Write focused SELECT queries and run:
   `node .claude/skills/unleak/scripts/query.mjs --connection <name> --sql "SELECT ..."`
   Continue iterating with more SELECT queries until the user's data question is answered.
   If a query is too complex for inline SQL, create it under `.claude/skills/unleak/local/queries/<short-name>.sql` and run:
   `node .claude/skills/unleak/scripts/query.mjs --connection <name> --file .claude/skills/unleak/local/queries/<short-name>.sql`
   Only continue to setup steps 3-12 when config, schema, or active policy is missing, or when the user explicitly asks to set up, propose, validate, activate, or update policy.
3. If config is missing, do not read or create `unleak/local/db-conf.json`. Ask the user to run:
   `node .claude/skills/unleak/scripts/init-config.mjs`
4. Ask the user to edit `unleak/local/db-conf.json` manually:
   - Set a random `hmacSecret`.
   - Keep and configure only the connections they need.
   - For SQLite, set the database file path.
   - For Postgres, set host, port, dbname, username, and password.
   - For BigQuery, users may run `gcloud auth application-default login`, then manually paste ADC JSON into `credentials.adc` and set `credentials.projectId`. Service account JSON is supported for advanced or CI use.
5. When the user says the config is saved, rerun:
   `node .claude/skills/unleak/scripts/list-connections.mjs`
6. Install deny rules if needed:
   `node .claude/skills/unleak/scripts/install-claude-settings.mjs`
7. Dump schema:
   `node .claude/skills/unleak/scripts/dump-schema.mjs`
8. Propose policy:
   `node .claude/skills/unleak/scripts/propose-policy.mjs`
9. Read schema and proposed policy files. Briefly explain policy recommendations.
10. Edit only files under `./unleak-policy-review/*.policy.proposed.json`.
11. Validate:
   `node .claude/skills/unleak/scripts/validate-policy.mjs`
12. Ask the user to activate manually:
   `!node .claude/skills/unleak/scripts/activate-policy.mjs ./unleak-policy-review/<connection>.policy.proposed.json`
13. Query approved data:
    `node .claude/skills/unleak/scripts/query.mjs --connection <name> --sql "SELECT ..."`
    For BigQuery, include dataset schema:
    `node .claude/skills/unleak/scripts/query.mjs --connection <name> --schema <dataset> --sql "SELECT ..."`

## Initialized Query Playbook

When a schema and active policy exist for the requested connection, optimize for valid queries:

1. Read the schema file and active policy for that connection before writing analysis SQL:
   - `.claude/skills/unleak/local/schema/<connection>.schema.json`
   - `.claude/skills/unleak/local/active-policies/<connection>.json`
2. Make a quick allowed-field map:
   - objects with `objectPolicy: "enabled"`
   - columns with policy `visible`, `masked`, `hashed`, or `joinable`
   - columns with policy `visible` only
3. Build queries from this map, not from guessed database knowledge.
4. For first-pass exploration, prefer simple one-table summaries: `COUNT(*)`, visible categorical counts, visible numeric `MIN`/`MAX`/`AVG`/`SUM`, and visible date buckets.
5. Avoid `UNION` for table overviews. Run simple count queries separately instead.
6. Avoid parallel or chained query batches until each query shape has passed once. One failed query can cancel useful follow-up work.
7. For non-trivial queries, run `--dry-run` first, then run the same SQL without `--dry-run` after it validates.
8. For BigQuery, always use local table names only, such as `FROM orders` or `JOIN customers c`. Do not use fully qualified names, dataset-qualified names, wildcard tables, decorators, or system-time queries. BigQuery dry-run cost checks run automatically.

Policy-aware SQL rules:

- `WHERE`, `HAVING`, `ORDER BY`, scalar expressions, and aggregate expressions may reference only columns with the matching capability. Without explicit `capabilities`, this keeps the legacy rule: only `visible` columns are allowed.
- `GROUP BY` may reference any column policy by default. Grouped protected values are still transformed or hidden according to their output policy.
- Direct `SELECT` may include non-hidden columns, but protected columns (`masked`, `hashed`, `joinable`) should not be used in filters, sorts, calculations, or aggregate expressions.
- Join conditions may use equality between `visible` or `joinable` columns only.
- Every derived expression must have an explicit alias, e.g. `COUNT(*) AS cnt`.
- `ORDER BY` may use output aliases or direct sortable columns. Do not use ordinals or raw expressions.
- Prefer qualified column names in joins, e.g. `t.company_id = lc.company_id`.
- If validation fails, read the error code and adjust the SQL to use visible columns or simpler direct selections. Do not respond by proposing a new policy unless the user asked to update policy.

Column policy handling:

- `visible`: Safe for normal analysis. May be selected, filtered, grouped, sorted, joined, and used in expressions or aggregates.
- `masked`: May be selected directly when useful for display or grouped counts, but the output is transformed. Do not use it for filters, sorting, joins, expressions, or aggregates.
- `hashed`: May be selected directly for pseudonymous display, local comparison, or grouped counts, but the output is transformed. Do not use it for filters, sorting, joins, expressions, or aggregates.
- `joinable`: Intended for equality joins, direct pseudonymous selection, and grouped counts. May be used in `ON a.col = b.col` when both sides are `visible` or `joinable`. Do not filter, sort, aggregate, or calculate with it.
- `hidden`: May be used only in `GROUP BY` when needed for counts. Never select it or reference it elsewhere.
- `disabled` object: Never query it.

Capability overrides:

- Policies may add `capabilities` per column to allow analysis use without changing output transformation.
- Valid capabilities: `select`, `filter`, `group`, `sort`, `join`, `aggregate`, `expression`.
- Example: a hashed `account_id` can use `["select", "join", "group"]` for pseudonymous investigation, or add `filter`/`sort` only when policy owners explicitly allow that workflow.
- Hidden columns may only declare `group`.

## Policy Updates

Treat an active policy as initialized state. For prompts like "inspect data", "find patterns", "analyze this connection", or "run a SELECT", use the active policy and focus on queries. Do not refresh schema, propose a replacement policy, edit proposed policies, validate proposals, or ask for activation just because the user requested analysis.

Policy can be updated only when the user explicitly asks to update, revise, expand, tighten, regenerate, or re-activate policy. In that case, run the relevant setup/policy steps above and keep edits limited to `./unleak-policy-review/*.policy.proposed.json`.

