# SQLPermit Deterministic PostgreSQL statement policy checks with a signed, statement-bound execution permit. SQLPermit parses a candidate statement with PostgreSQL's own grammar (libpg-query, PG 18), walks the syntax tree, and reports whether it complies with the policy you supply — read-only, schema and table allowlists, row ceilings, a function allowlist, single-statement enforcement. Detection is structural, so comments, dollar quoting, Unicode escapes, and stacked statements cannot hide a construct from it. On a compliant statement it can issue a signed Ed25519 permit, valid for seconds, bound to the sha256 of the deparsed canonical statement so it cannot be moved onto a different one. An offline reference verifier is published for your executor. What it cannot do: it never connects to your database, so it cannot see your schema, cannot resolve search_path, and cannot know the role a statement will run as. It is defence in depth alongside a least-privilege database role, not a replacement for one. ## Payment - Protocol: x402 V2, scheme `exact`, network `eip155:8453` (Base mainnet) - Asset: USDC `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` - No accounts, no API keys, no subscriptions. - Failed calls are not charged. Only a successful, usable result settles. - Payment authorization window: 60s ## Endpoints ### `POST /v1/guard/sql` — $0.01 per successful call Decide whether an agent-authored PostgreSQL statement complies with an execution policy, using PostgreSQL's own parser rather than pattern matching. Detects statement stacking, data-modifying CTEs, COPY PROGRAM, privilege changes, and dangerous functions that read as ordinary SELECTs. Returns reason codes and an optional short-lived signed permit bound to the exact normalized statement. Never connects to your database. Unpaid request returns HTTP 402 with the challenge in the `PAYMENT-REQUIRED` header: ```bash curl -i -sS -X POST https://sqlpermit.schemasure.com/v1/guard/sql \ -H 'content-type: application/json' \ -d '{"dialect":"postgresql","target_pg_major":18,"sql":"SELECT id, total FROM analytics.orders ORDER BY created_at DESC LIMIT 100","policy":{"read_only":true,"allowed_schemas":["analytics"],"allowed_tables":["analytics.orders"],"max_rows":1000,"allowed_functions":["count","sum","avg"],"allow_multi_statement":false},"audience":"executor:acme-prod-01","issue_permit":true}' ``` Example successful response: ```json { "ok": true, "verdict": "pass", "confidence": 1, "risk_codes": [], "evidence": [], "result": { "statement_count": 1, "statement_types": [ "SelectStmt" ], "referenced_schemas": [ "analytics" ], "referenced_tables": [ "analytics.orders" ], "referenced_functions": [], "classification": "read_only", "findings": [], "normalized_sql": "SELECT id, total FROM analytics.orders ORDER BY created_at DESC LIMIT 100", "stmt_hash": "sha256:0f4c1d8e2b7a9c5d3e6f8a1b2c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d", "stmt_fingerprint": "a1b2c3d4e5f60718", "policy_hash": "sha256:2b7a9c5d3e6f8a1b2c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d0f4c1d8e", "grammar": "pg18/libpg-query@18.1.4", "target_pg_major": 18, "obligations": { "max_rows": 1000, "statement_timeout_ms": 30000, "require_read_only_tx": true }, "permit": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoi…", "permit_expires_at": "2026-08-03T12:00:45.000Z", "permit_kid": "uGzLTTwi4LTsOiBLgwf2wMU7ILTecj18Y_71Y37bVD8" }, "policy_version": "sqlpermit-policy-1.0.0", "request_hash": "sha256:1c2d0f4c1d8e2b7a9c5d3e6f8a1b2c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b", "data_versions": { "grammar": "pg18/libpg-query@18.1.4", "parser": "libpg-query@18.1.4", "pg_parse_version": "180004" }, "warnings": [ "stmt_fingerprint is ADVISORY and must not be used for authorization: libpg-query fingerprints discard literal constants, so two statements differing only in a value share one fingerprint. stmt_hash is the binding value." ] } ``` ## Result contract Every response is the shared portfolio envelope: `ok`, `verdict`, `confidence`, `risk_codes`, `evidence`, `result`, `policy_version`, `request_hash`, `data_versions`, `warnings`. `verdict: "unknown"` is a real answer, not a failure. It means the service could not determine the result and says why in `evidence`. It is never silently reported as a safe or clean outcome. ## Service level - Target P50: 50 ms - Target P95: 150 ms ## Data handling - Caller content is processed transiently and never retained. - No training on customer content. - No subprocessors see caller content. ## Machine-readable surfaces - https://sqlpermit.schemasure.com/openapi.json - https://sqlpermit.schemasure.com/.well-known/x402.json - https://sqlpermit.schemasure.com/.well-known/pricing.json - https://sqlpermit.schemasure.com/.well-known/benchmarks.json - https://sqlpermit.schemasure.com/.well-known/data-handling.json - https://sqlpermit.schemasure.com/apis.json - https://sqlpermit.schemasure.com/SKILL.md