How do I control what an AI agent can query on a per-request basis?

Per-request control means the decision about whether a query is allowed happens when the query is made, using the context of that specific request, rather than being fixed in advance by a role grant. Database permissions cannot do this: a grant is evaluated against identity and object, not against what the agent is currently doing, where the request came from, or how much it has already retrieved.

There are three places the decision can live, and they have different properties.

A dialog titled Mint an auth token, with fields for expiry and use count, permitted and forbidden IP ranges, permitted and forbidden countries, a valid-from and valid-until time, day-of-week checkboxes, and options to require MFA and to accept signed requests only.
Conditions carried by the credential itself. Each one is evaluated server-side on every request the token makes, not once when it is issued.

In the tool definition

The simplest option. The agent calls a function you wrote, and that function contains the logic: which tables, which columns, which filters are mandatory.

This works and it is where most teams start. Two limitations show up quickly. The policy is scattered across every tool definition, so there is no single place to answer the question "what can this agent reach". And changing the policy means changing code and shipping it, which puts security changes on the engineering release cycle.

Acceptable for one agent. It degrades badly at five.

In a gateway in the query path

A proxy sits between the agent and the database, inspects each query, and rewrites or rejects it. Tools in this category, such as hoop.dev, apply masking at the protocol layer so the database still sees the original query.

This is the right architecture when the agent must reach live production data, because it is the only approach that governs the production path without moving anything. The costs are an additional hop in the query path, which platform teams evaluate for latency and availability before they evaluate it for security, and a bypass surface: anyone holding direct database credentials can route around the proxy.

In a data control layer over a governed dataset

The third option separates the two problems. The agent queries a dataset that is already anonymized and scoped, and a policy layer resolves each request against that dataset at the moment it is made.

The production path is untouched, so there is nothing to bypass and no latency added to live traffic. The trade is that the agent is not working against live production data, which rules this out where real-time production access is the requirement and makes it the stronger option where it is not, such as development, testing, evaluation, and analytical workloads.

This is the approach Ascent implements.

What a per-request decision should be able to see

Whatever layer you choose, these are the inputs that make per-request control meaningfully different from a role grant:

  • Which agent or identity is asking, distinct from the human who started it
  • Which columns and tables the request touches
  • What has already happened in this session, so cumulative volume is visible
  • Where the request originates, so an approved workload and an unapproved one are distinguishable

Ascent scopes policy along requestors, actions, on_objects and conditions, evaluated against twelve conditions: from_IP_cidrs, not_from_IP_cidrs, from_countries, not_from_countries, days_of_the_week, between_times, multifactor_authentication_present, request_is_signed, requestor_kind, record_type, record_tag and without_active_grant. Conflicts resolve deny-wins, so a narrow denial cannot be widened by a broader grant elsewhere.

A rehearse panel with requestor, action and object filled in, a context block offering address, country, time, weekday, MFA present and request signed, and a result reading Would be denied, two probes, naming the rule that decided it.
The same inputs, asked in advance. Rehearsal runs the real evaluator and decides nothing, so a policy can be checked against a request context before anything depends on it.
First, your Ascent and a token to talk to it
ASCENT=https://ascent.example.com

TOKEN=$(curl -sS -X POST "$ASCENT/api/login" \
  -H 'Content-Type: application/json' \
  -d '{"email": "you@example.com", "password": "..."}' \
  | jq -r .access_token)
Ask what would happen, without it happening. The context takes any of ip, country, time, weekday, mfa and signed
curl -sS -X POST "$ASCENT/api/authorization/dry_run" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "requestor": "application:demo-support-agent",
    "action": "record:get_with_decrypt",
    "object": "acrn:12345678:object/workspace:1:container:*",
    "context": {
      "ip": "10.4.2.10",
      "country": "US",
      "time": "02:30",
      "weekday": "tuesday",
      "mfa": false,
      "signed": false
    }
  }'

A worked example

A permission document is a list of rules, each naming who, what action, on which objects, and under what conditions. This one lets a coding assistant read orders through the redacted view, and refuses anything tagged payment outright:

{
  "version": 1,
  "rules": [
    {
      "comment": "Payment records are refused whatever else is granted. Deny wins.",
      "requestors": ["acrn:12345678:entity/application:coding-assistant"],
      "actions": ["acrn:12345678:action/*:*"],
      "on_objects": ["acrn:12345678:object/workspace:Commerce:container:*:record:*"],
      "decision": "deny",
      "conditions": { "record_tag": ["payment"] }
    },
    {
      "comment": "Read orders, redacted, as an agent, from the office range.",
      "requestors": ["acrn:12345678:entity/application:coding-assistant"],
      "actions": [
        "acrn:12345678:action/record:get_with_redact",
        "acrn:12345678:action/container:search_with_redact"
      ],
      "on_objects": ["acrn:12345678:object/workspace:Commerce:container:orders:record:*"],
      "decision": "allow",
      "conditions": {
        "requestor_kind": ["ai_agent"],
        "from_IP_cidrs": ["10.0.0.0/8"]
      }
    }
  ]
}

Two things this policy does not do, because the model does not express them. Masking is chosen per view, not per column: the redacted view masks every field the record type marks encrypted, so the customer email is masked along with everything else rather than singled out. And there is no session row ceiling to set here; volume is a workflow threshold counting requests, outside the document.

Mint a credential carrying the conditions. The body IS the condition set; leave a field out to leave that condition off
APPLICATION_ID=42

curl -sS -X POST "$ASCENT/api/application/$APPLICATION_ID/token" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "expires_at": "2026-12-31T17:30:00+00:00",
    "use_count": 1,
    "from_IP_cidrs": ["10.0.0.0/8"],
    "from_countries": ["US", "CA"],
    "days_of_the_week": ["monday", "tuesday", "wednesday", "thursday", "friday"],
    "between_times": {"start_time": "09:00", "end_time": "17:30"},
    "multifactor_authentication_present": true,
    "request_is_signed": true
  }'

How to choose

If the agent must query live production, you need a gateway. If the agent's work can happen against a governed dataset, a data control layer gives you the same per-request decisions without touching the production path. The tool-definition approach is fine for a first agent and stops scaling around the third.

How Ascent does this: Only approved AI models near your data.

Three ways to see Ascent working.

Book a demo

Talk it through with us.

30 minutes with the team, on your questions.

Book a demo

Try the labs

Hands-on, synthetic data, no sign-up.

Two hands-on labs that simulate Ascent on synthetic data, with nothing to install: the containment lab and the vibe coding lab.

Try the labs

Request a trial

Your own instance.

We set up an instance for you on our infrastructure and email you within one business day. No credit card. Please use test or sample data.

0 of 1,000 characters