Safe database access
for AI agents.
A policy-enforced runtime on top of your ORM. Redaction, tenant scoping, query budgets, and audit logs — built in.
MIT licensed · Python & TypeScript · v0.2.0 (beta)
from ormai.quickstart import mount_sqlalchemy
from ormai.utils import PolicyBuilder, DEFAULT_PROD
policy = (
PolicyBuilder(DEFAULT_PROD)
.register_models([Customer, Order])
.deny_fields("*password*", "*secret*", "*token*")
.mask_fields(["email", "phone"])
.tenant_scope("tenant_id")
.enable_writes(models=["Order"], require_reason=True)
.build()
)
toolset = mount_sqlalchemy(engine=engine, session_factory=Session, policy=policy)
# Your agent now has db.query, db.get, db.aggregate — safely. One policy, every tool. No raw SQL ever leaves the policy boundary.
Built for production agents
Six hard problems, solved on day one.
Most teams reinvent these poorly. OrmAI gives you the same primitives that real-world security teams demand from data infrastructure — declaratively.
Field-level redaction
Mask, hash, or deny sensitive columns. Agents see what policy allows — never raw PII.
Automatic tenant scoping
Every query is filtered by tenant_id by default. Cross-tenant access becomes structurally impossible.
Query budgets & timeouts
Row caps, statement timeouts, and join-depth limits keep runaway agents from melting your DB.
Immutable audit log
Every read and write is logged with principal, tenant, trace ID, sanitized inputs, and policy decisions.
Gated writes
Approve which models can be mutated, require a reason on writes, and cap rows-per-statement.
Works with your ORM
SQLAlchemy, Tortoise, Peewee, Django, SQLModel — and Prisma, Drizzle, TypeORM on the TS side.
Side-by-side
Raw SQL gives the agent a loaded gun.
OrmAI gives it a curated, typed toolbox — same expressiveness, none of the foot-guns.
# The model can write any SQL it wants
def execute_sql(query: str) -> list[dict]:
return db.execute(query).fetchall()
# Hope nobody ever asks it to:
# "show me all rows in users WHERE 1=1"
# "DROP TABLE invoices"
# "show all rows where tenant_id != 42"
# The agent calls a typed, policy-checked tool
result = await toolset.execute("db.query", {
"model": "Order",
"where": {"status": "pending"},
"limit": 50,
}, ctx=run_ctx)
# Policy enforced automatically:
# • tenant_id auto-injected from RunContext
# • forbidden columns redacted in response
# • row limit capped at policy max
# • full audit row written before return
Benchmark
1,034 natural-language queries.
Zero unsafe operations.
We replayed the entire Spider text-to-SQL benchmark through OrmAI. The result: 0 SQL injections, 0 unscoped queries, 0 destructive operations. The text-to-SQL baseline produced 23.
Read the methodology →Plays well with
FAQ
Frequently asked.
Is OrmAI an ORM? +
No. OrmAI is a policy-enforced runtime layer that wraps the ORM you already use (SQLAlchemy, Prisma, Drizzle, etc.). The ORM still owns schemas and migrations; OrmAI controls what your AI agent can do with it.
How is this different from text-to-SQL? +
Text-to-SQL asks the LLM to generate SQL strings. OrmAI exposes a small, typed tool surface (db.query, db.get, db.aggregate, db.create, db.update, db.delete) that compiles to parameterized ORM operations. SQL injection is structurally impossible. Our Spider benchmark recorded zero unsafe operations vs. 23 for a text-to-SQL baseline.
Will it slow my agent down? +
Policy evaluation is in-process and adds well under a millisecond per call. Query budgets are checked at compile time — they reject expensive queries before they hit the database, which usually makes agents faster.
Is it production ready? +
OrmAI is in beta (v0.2.0) and is in production at Neul Labs and partner companies. v0.2 added rate limiting, health checks, structured logging, and audit retention.
What languages are supported? +
Python (SQLAlchemy, Tortoise, Peewee, Django, SQLModel) and TypeScript (Prisma, Drizzle, TypeORM). Both share the same policy model.
Does OrmAI store my data? +
Never. OrmAI runs entirely inside your application process. There is no SaaS component, no telemetry, no external calls.