Gantry NoSQL¶
Gantry NoSQL is a governed execution boundary for agent-generated MongoDB pipelines. It does
not expose a database connection or invent a portable query language — every filter or pipeline
runs exactly as written, against MongoDB, through pymongo.AsyncMongoClient.
Configure once¶
Install the MongoDB driver, then connect once:
import gantry
db = gantry.nosql.connect("mongodb", uri="mongodb://localhost:27017", database="analytics")
query = db.query(
read_only=True,
collections=("orders",),
max_documents=500,
timeout=30,
checks=[gantry.verify.document_count(max=500)],
)
Call the governed operation directly, with a plain filter dict:
result = await query(
"orders",
{"status": "open"},
verify=[gantry.verify.not_empty(), gantry.verify.required_fields(["_id"])],
)
or a read-only aggregation pipeline:
result = await query(
"orders",
[{"$match": {"status": "open"}}, {"$group": {"_id": "$region", "total": {"$sum": "$amount"}}}],
)
NoSQLResult.inline holds the returned documents, BSON types converted to JSON-safe Python
values, capped at max_documents and flagged with truncated when the cap was hit.
Or expose its narrow, framework-neutral form to an agent:
tool = query.tool()
tool.name # "query_nosql"
tool.input_schema # collection, pipeline, and a capability-filtered verify array
result = await tool.invoke(
collection="orders",
pipeline={"status": "open"},
verify=[{"type": "not_empty"}],
)
Query policy — allowed/denied collections, document/timeout/byte/cost limits — never appears in the agent tool schema.
Governed writes: db.materialize()¶
Writes only ever happen through a pipeline that ends in exactly one $out or $merge stage,
naming a destination the operator has approved:
materialize = db.materialize(
sources=["orders"],
destinations=["reporting.daily_rollup"],
checks=[gantry.verify.destination_exists()],
)
result = await materialize(
"orders",
[
{"$match": {"status": "open"}},
{"$group": {"_id": "$region", "total": {"$sum": "$amount"}}},
{"$out": "reporting.daily_rollup"},
],
verify=[
gantry.verify.document_count(min=1),
gantry.verify.required_fields(["_id", "total"]),
],
)
Checks passed to checks= are trusted application commitments. Checks passed at invocation time
through verify= are additional agent-proposed commitments: they can strengthen acceptance but
cannot remove or weaken trusted checks. Gantry assigns each result's provenance, evaluates both
sets against the bounded query result or the destination collection, and records them in
result.evidence.
$out is create-only: if the destination collection already exists, the run is rejected with
DESTINATION_EXISTS before anything is written. $merge is allowed to write into an existing
destination — that is its purpose — but only with whenMatched set to "merge" or "replace";
other write modes ("keepExisting", "pipeline", arbitrary update pipelines) are rejected, to
keep the governed-write guarantee meaningful.
Every collection referenced anywhere in the pipeline — the collection being aggregated, and any
$lookup.from target — must be listed in sources.
Policy fields¶
NoSQLPolicy (built from db.query(...)'s keyword arguments):
| Field | Default | Meaning |
|---|---|---|
read_only |
True |
Reject any pipeline that classifies as a write. |
allowed_collections |
frozenset() |
If non-empty, only these collections may be referenced. |
denied_collections |
frozenset() |
These collections are always rejected. |
max_documents |
1_000 |
Upper bound on returned documents; excess is truncated and flagged. |
timeout_seconds |
30 |
Maximum wall-clock time for the aggregation. |
max_bytes_scanned |
None |
Requires adapter support to enforce. |
max_cost_usd |
None |
Requires adapter support to enforce. |
MaterializationPolicy (built from db.materialize(...)'s keyword arguments) additionally takes
sources and destinations glob patterns, and has no read_only field — materialization is
always a governed write.
Supported pipeline stages¶
Reads: $match, $project, $group, $sort, $limit, $unwind, $lookup. Writes (materialize only):
$out, $merge. Any other stage fails classification at validate() and never reaches MongoDB.
Capabilities¶
NoSQLCapabilities declares what the adapter actually enforces — read_only_session,
document_limit, operation_timeout, bytes_scanned, cost_limit, out_merge_writes,
destination_introspection, and more. A policy requirement with no matching capability fails
admission before the pipeline reaches MongoDB — see gantry/nosql/enforcement.py.
Verification checks¶
The public gantry.verify vocabulary includes MongoDB checks adapted to schemaless documents:
destination_exists()— the destination collection exists after materialization.document_count(min=, max=)— readsCollectionSnapshot.metadata["document_count"].required_fields([...])— checks field presence within a bounded sample (default 100 documents) of the destination. Fields absent from every sampled document read as missing even if present in unsampled documents — a documented limitation, not a bug.
Query tools expose not_empty, document_count, and required_fields. Materialization tools also
expose destination_exists. Unsupported or malformed agent requests fail closed; contradictory
count bounds are rejected before MongoDB executes the pipeline. The older gantry.nosql check
constructors and trusted configuration verify= spelling remain available for compatibility.
What is out of scope (v0)¶
- Any NoSQL engine other than MongoDB.
db.describe()/ schema discovery — collections are schemaless.- Exhaustive (non-sampled) field presence guarantees.
- Drivers other than
pymongo.AsyncMongoClient(nomotor).
Worked example¶
examples/mongodb_rollup.py is a runnable version of the query and materialize paths above,
against a local MongoDB started with docker run mongo. See
examples/README.md for setup.