Trusted context
Grounding often needs identity that the model must not control: the authenticated customer, workspace, role, region, or request tenant.
Bind those values around each invocation with trusted_context():
from actionrail import trusted_context
def handle_agent_request(session, inputs):
with trusted_context(
customer_id=session.customer_id,
workspace_id=session.workspace_id,
role=session.role,
):
return agent.invoke(inputs)
A grounding condition can then compare a Source record to the authenticated value:
match:
- column: customer_id
ctx: customer_id
Why it is trusted
Trusted context must originate in host application code after authentication and authorization. Do not populate it from:
- model output;
- tool arguments;
- user text without server-side verification;
- arbitrary values copied from an agent message.
The runtime exposes only scalar context values for query binding. When a context name collides with a tool argument name, trusted context wins.
Concurrency isolation
trusted_context() uses Python ContextVar, so one shared compiled agent can serve concurrent sync and async requests without mixing caller identity.
The binding is reset when the with block exits, including when the invocation raises:
with trusted_context(customer_id="customer-a"):
first = agent.invoke(inputs_a)
with trusted_context(customer_id="customer-b"):
second = agent.invoke(inputs_b)
Nested scopes inherit the parent and can override individual fields:
with trusted_context(workspace_id="workspace-1", role="member"):
with trusted_context(role="admin"):
result = agent.invoke(inputs)
After the inner block, role is member again.
Static defaults
The ctx= argument to enforce() remains available for application-wide defaults:
agent = enforce(agent, config="actionrail.yaml", ctx={"region": "us-east-1"})
Invocation-scoped values override those defaults. Do not place request-specific identity in ctx= when the wrapped agent is shared across requests.
Framework integration pattern
Bind context at the boundary where the application already has an authenticated principal:
def invoke_for_request(request, inputs):
principal = authenticate_request(request)
with trusted_context(
customer_id=principal.customer_id,
workspace_id=principal.workspace_id,
):
return agent.invoke(inputs)
This makes the trust boundary visible in code review: authentication establishes identity, and ActionRail consumes that identity without asking the model to repeat it.