sql-write-gate
写库前门禁 · Policy firewall for AI agents writing to databases.
Prevent Claude Code, Codex, Cursor and MCP agents from executing unsafe database operations.
Agent SQL ──► sql-write-gate ──► ALLOW / BLOCK / APPROVAL ──► Database
Deterministic policy engine (sqlglot AST + catalog + policy.yaml). No LLM. No API key.
非生产唯一边界 — Early gate prototype; not the sole production security boundary. 未列语法拒绝 — unsupported / ambiguous SQL → REJECT/BLOCK (fail closed), never silent ALLOW as read-only.
Install
pip install sql-write-gate
pip install 'sql-write-gate[postgres]' # optional: psycopg
pip install 'sql-write-gate[mysql]' # optional: pymysql
pip install 'sql-write-gate[mcp]' # optional: MCP server
From a clone:
pip install -e ".[dev]" # or: make install
pip install -e ".[postgres,mysql]"
make test # PG/MySQL live tests skip if no service
sql-write-gate check "DELETE FROM users"
# → BLOCKED rule=delete_without_where
Commands
sql-write-gate check "SQL" # evaluate SQL; no execute
sql-write-gate hook # PreToolUse: block raw psql/mysql/…
sql-write-gate mcp # MCP stdio (query_sql / write_sql)
sql-write-gate proxy --sql "..." # gate then execute if ALLOW
sql-write-gate approve <id> # human approve then write
sql-write-gate audit # TIME / SOURCE / OP / TABLE / VERDICT
sql-write-gate init # scaffold policy.yaml + catalog.json
What it does (current)
DROP/TRUNCATE/ALTER→ BLOCKDELETE/UPDATEwithoutWHERE→ BLOCK- Blast-radius COUNT vs
update_rows/delete_rows(dialect quoting; fail-closed on estimate error) - Schema / PII / restricted columns; PII
SELECT→ REQUIRE_APPROVAL (approve executes once) - Freshness partitions (
dt); range / NOT / OR / UPSERT SET expired → BLOCK - Nested / data-modifying CTE /
SELECT INTO→ REJECT (unsupported_sql) - Approval queue with atomic
pending→executingclaim underfcntl.flock - JSONL audit (redacts URL passwords; records execute failures)
- Adapters: DuckDB (default), PostgreSQL, MySQL, SQLite
Platform support matrix
| Surface | Linux / macOS | Windows |
|---|---|---|
pip install / CLI check / init / audit |
✅ | ✅ |
| DuckDB file backend | ✅ | ✅ |
SQLite sqlite:/// paths (incl. C:/…) |
✅ | ✅ |
| Postgres / MySQL URL adapters | ✅ | ✅ (drivers via extras) |
| PreToolUse hook / MCP stdio | ✅ | ✅ (same Python entrypoints) |
Concurrent approve (flock + atomic replace) |
✅ | ❌ fail closed — ApprovalError if fcntl.flock unavailable (no silent unlock) |
Windows: install, CLI evaluate/execute on DuckDB/SQLite/URL backends work. The approvals JSONL concurrency lock requires Unix fcntl; without it, approval mutations refuse rather than silently degrading. Use a single-process approve path on Unix hosts, or run the gate where flock is available.
Database URLs
| Env / kwarg | Backend |
|---|---|
POSTGRES_URL or postgresql://… / postgres://… |
PostgreSQL |
MYSQL_URL or mysql://… / mysql+pymysql://… |
MySQL |
DATABASE_URL (scheme-detected) |
Postgres / MySQL / SQLite |
sqlite:/// / sqlite+aiosqlite:// |
SQLite (stdlib) |
file path / default seed/warehouse.duckdb |
DuckDB |
Priority: database= → database_url= → db_path= → POSTGRES_URL → MYSQL_URL → DATABASE_URL → DuckDB default.
Live integration tests (optional locally)
export POSTGRES_URL=postgresql://gate:gate@localhost:5432/writegate
export MYSQL_URL=mysql://gate:gate@127.0.0.1:3306/writegate
pip install -e ".[dev,postgres,mysql]"
make test
Without those services, live tests skip; CI runs Postgres + MySQL service containers.
Policy (default production)
| operation | rule |
|---|---|
| select | allow |
| insert | approval |
| update | approval |
| delete | block |
| ddl | block |
Limits: update_rows: 100, delete_rows: 50. Demo policy (examples/policy.demo.yaml) allows insert/update for walkthroughs.
Guards (any BLOCK wins, else any APPROVAL, else ALLOW):
destructive → schema → pii → freshness → blast_radius → environment
Decision model
ALLOW | BLOCK | REQUIRE_APPROVAL with risk, rule_id, reason, evidence.
Boundaries (non-goals)
- 非生产唯一边界 — combine with least-privilege DB roles, network isolation, and human workflows
- Not a distributed approval lock, MySQL wire-protocol proxy, or Web UI
- Not an enterprise DQ / lineage / ChatBI / multi-tenant platform
See CHANGELOG.md for version history (v0.1 → v0.20).
Backlog (post-0.20)
- Real Postgres / MySQL CI services + persist/recheck integration tests (0.20)
- R1–R6 permanent regression (dangerous + safe paths) (0.20)
- Windows support matrix + flock fail-closed (0.20)
- Release gate: wheel install smoke; publish needs test+build on same tag (0.20)
- Deferred: distributed / multi-host approval lock
- Deferred: MySQL wire-protocol proxy
- Deferred: Web UI
许可
MIT。见 LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sql_write_gate-0.20.0.tar.gz.
File metadata
- Download URL: sql_write_gate-0.20.0.tar.gz
- Upload date:
- Size: 74.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a12cb87e8885b6893acbd96d252db928fa5b6ecd100b1ead7f7afa6d752bce8
|
|
| MD5 |
d380552346406f8097d1477b9784130b
|
|
| BLAKE2b-256 |
69186906c6898df9e97bb711b86f0916b46b49e6b760f6497d5bfcd0972835c8
|
Provenance
The following attestation bundles were made for sql_write_gate-0.20.0.tar.gz:
Publisher:
publish.yml on tangyf07/sql-write-gate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sql_write_gate-0.20.0.tar.gz -
Subject digest:
3a12cb87e8885b6893acbd96d252db928fa5b6ecd100b1ead7f7afa6d752bce8 - Sigstore transparency entry: 2738009852
- Sigstore integration time:
-
Permalink:
tangyf07/sql-write-gate@b1e8e6f3dea60bc13d5fadcf2aa87c1248d75bc5 -
Branch / Tag:
refs/tags/v0.20.0 - Owner: https://github.com/tangyf07
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b1e8e6f3dea60bc13d5fadcf2aa87c1248d75bc5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sql_write_gate-0.20.0-py3-none-any.whl.
File metadata
- Download URL: sql_write_gate-0.20.0-py3-none-any.whl
- Upload date:
- Size: 63.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6a595d5a9a68f648cf77475d4d07b68eb8d92355a894e118585d38ab9705f34b
|
|
| MD5 |
894f1a62e0100cfc1fce2ae3d3b87ba9
|
|
| BLAKE2b-256 |
d7194da14a252bfe6a62314c4e6d71a621204b774cbe729c090474d0c5bb5af8
|
Provenance
The following attestation bundles were made for sql_write_gate-0.20.0-py3-none-any.whl:
Publisher:
publish.yml on tangyf07/sql-write-gate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sql_write_gate-0.20.0-py3-none-any.whl -
Subject digest:
6a595d5a9a68f648cf77475d4d07b68eb8d92355a894e118585d38ab9705f34b - Sigstore transparency entry: 2738009896
- Sigstore integration time:
-
Permalink:
tangyf07/sql-write-gate@b1e8e6f3dea60bc13d5fadcf2aa87c1248d75bc5 -
Branch / Tag:
refs/tags/v0.20.0 - Owner: https://github.com/tangyf07
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b1e8e6f3dea60bc13d5fadcf2aa87c1248d75bc5 -
Trigger Event:
push
-
Statement type: