Over the past few months, as part of an ongoing exploratory project on agent safety and runtime boundaries, I've been experimenting with how autonomous agents interact with backend services and APIs. It is very much an early-stage exploration — not a finished product, not a proposed standard, and certainly not something that has undergone formal outside security audits.
Still, while experimenting with multi-agent workflows and tool invocation, I kept running into an uncomfortable design pattern:
In many contemporary agent setups (whether using LangChain, custom scripts, or standard Model Context Protocol (MCP) servers), the runtime hands an LLM an API token or a set of tools with direct execute permissions. If the model generates a function call, the framework executes it directly against backend services.
That setup makes me nervous. An LLM cannot provide formal mathematical guarantees about its own behavior. A prompt injection, a subtle context shift, or a sudden hallucination can push it to invoke a tool with unintended parameters.
Furthermore, if two agents collaborate in a loop, they can easily enter cyclic ping-pong calls or trigger deep runaway recursion before anyone notices.
Here is the architectural hypothesis I am testing, what I have implemented in code so far, and where the boundaries currently lie.
The Working Hypothesis: Separating Reasoning from Execution Authority
The core idea I keep returning to is that reasoning and execution authority should live in two completely separate layers:
┌─────────────────────────────────────────────────────────────┐
│ SEMANTIC PLANE (Probabilistic) │
│ - Handled by AI models (LLMs, autonomous agents) │
│ - Understands and manages INTENT & rich task CONTEXT │
│ - Discovers tools, proposes actions, maps business schemas │
│ - Holds ZERO execution credentials. Cannot mutate state. │
└──────────────────────────────┬──────────────────────────────┘
│ ProposedAction envelope
│ (Intent + Context + Arguments)
▼
┌─────────────────────────────────────────────────────────────┐
│ CONTROL PLANE (Deterministic) │
│ - Pure Rust logic, no LLM involved │
│ - Evaluates call-chain depth and cycle limits (Layer 0) │
│ - Checks policy rules, contracts, & monetary thresholds │
│ - Mints short-lived, single-use Execution Grants (Ed25519) │
└──────────────────────────────┬──────────────────────────────┘
│ Cryptographic Grant
▼
┌─────────────────────────────────────────────────────────────┐
│ TOOL EXECUTOR │
│ - Verifies grant signature & argument hash (RFC 8785) │
│ - Dispatches call to destination system │
│ - Scrubs PII / secrets from the output │
└─────────────────────────────────────────────────────────────┘
- The Semantic Plane is probabilistic. This is where intent is captured and understood. What is truly essential here is the context — the ongoing conversation, domain knowledge, multi-step problem formulation, and business objectives. LLMs excel at processing messy, unstructured data, grasping what a user or counterparty is trying to accomplish, and proposing appropriate actions. But precisely because context can be manipulated (via prompt injections, context poisoning, or hallucinations), the semantic plane should never hold raw API keys or decide the ultimate boundary of its own execution authority.
- The Control Plane is deterministic. Implemented in Rust, this layer contains no machine learning models. It does not attempt to interpret fuzzy human intent. Instead, it takes the proposed action and verifies it against hard facts: static policies, caller identity, session state, call-chain depth, and cryptographic contracts before issuing a single-use execution ticket.
If an agent wants to perform an action, it does not call the tool directly. It packages its intent and parameters into a structured proposal (ProposedAction). Only if the deterministic control plane approves does an executor receive a valid, signed grant to run the call.
A Note on B2B Agent Boundaries
Most discussions around AI commerce focus on consumer chatbots checking out carts. But in business-to-business (B2B) integrations, external agents interacting with your APIs cannot be governed by a friendly system prompt. Enterprises operate under negotiated terms: transaction caps, payment terms, geographical constraints, and bilateral liabilities.
In this context, I think of an agent's authority not as a static token, but as the strict mathematical intersection of three independent boundaries:
┌────────────────────────────────────────────────────────┐
│ EFFECTIVE AUTHORITY │
│ = │
│ Negotiated Terms ∩ Enterprise Policy ∩ Identity │
└────────────────────────────────────────────────────────┘
An agent has zero inherent authority of its own. It only gains the right to trigger an action if the request satisfies all three constraints simultaneously:
- Negotiated Terms (Bilateral): Did both organizations agree that this capability is allowed within this transaction limit, currency, and geography?
- Enterprise Policy (Local): Does the host organization's internal policy allow this action right now (e.g. rate limits, operating hours, maintenance windows)?
-
Agent Identity (Cryptographic): Is the agent proven to represent the claimed counterparty via decentralized identity (e.g.
did:web) and active cryptographic keys?
If any single piece of that intersection fails, the action is denied deterministically.
Concrete example: Even if an LLM is 100% confident it should place a €30,000 purchase order, the control plane rejects the proposal before it ever touches a database if the bilateral agreement caps automated orders at €25,000.
What Has Actually Been Implemented in Code
To test whether this approach is practical in real systems, I built a prototype implementation in Rust to explore these mechanics hands-on. Here is what is working in code today:
1. Cryptographic Parameter Binding (Execution Grants)
When an action proposal passes policy checks, the gateway does not simply return approved: true. It mints an ExecutionGrant using Ed25519 signatures.
To prevent parameter tampering between the moment a policy approves a call and when the executor actually runs it, the grant cryptographically binds the exact arguments using RFC 8785 JSON Canonicalization Scheme (JCS) and SHA-256.
(Think of canonicalization as formatting and sorting all JSON keys in a deterministic order so two different systems always compute the exact same hash for the same data payload).
If an attacker, a compromised agent, or an intermediary intercepts the grant token and alters any parameter (e.g., modifying an account number or a dollar amount), the executor recalculates the SHA-256 hash over the canonical JSON. The hash mismatch causes immediate rejection. Each grant also carries a single-use nonce to prevent replay attacks.
2. Layer 0 Call-Chain Guard
In multi-agent architectures, agents call other agents. Without low-level safety controls, this easily leads to recursion loops or accidental spamming of downstream APIs.
Before evaluating business policies, the gateway evaluates a Layer 0 Call-Chain Guard (in the policy engine):
-
Cycle Detection: Rejects invocations if a tool or agent already appears in the active call stack (preventing
Agent A$\leftrightarrow$Agent Bloops). - Depth Ceilings: Automatically rejects requests if the call depth exceeds a configured threshold (default is 10 hops).
- Per-Tool Frequency Caps: Restricts how many times a single tool can be invoked within a given session trace (e.g., maximum 3 calls per task).
- Session Integrity Checking: The gateway maintains an authoritative call stack in memory keyed by trace ID. If an agent attempts to submit a spoofed or truncated history to bypass limits, the gateway detects the divergence and denies the call.
3. Bilateral Interaction Contracts (NICP)
In the contract module, I implemented an initial prototype of a bilateral negotiation protocol (Negotiated Interaction Contract Protocol):
- Two enterprise parties (identified via decentralized identifiers such as
did:web) establish a structured draft agreement containing allowed capabilities, transaction limits, and geographical constraints. - The contract is serialized into canonical RFC 8785 JSON and hashed.
- Both parties execute an attestation ceremony, producing Ed25519 signatures over the canonical hash.
- During runtime, incoming proposals are evaluated against this active contract. If an agent attempts an order exceeding the agreed ceiling, the request is blocked deterministically before any grant is minted.
4. Surface CLI Policy Enforcement Point (trustctl)
To ensure administrative scripts and local developer tools adhere to the same boundaries, I built a command-line Policy Enforcement Point (trustctl).
trustctl dynamically reads tool JSON Schemas, maps them into CLI flags, computes canonical input hashes, and maps gateway responses directly to standard POSIX exit codes:
-
0— Success / execution completed -
1— Schema validation failure -
126— Policy denied (permission / limit violation) -
127— Unknown tool requested
5. Egress Redaction
On the response path (in the egress filtering layer), the gateway runs redaction logic before returning tool outputs back to an agent or caller:
- Regex scrubbing for common patterns (emails, credit card numbers, bearer tokens, API keys).
- Recursive JSON traversals to sanitize nested objects without corrupting valid data structures.
- Audience-aware field masking, filtering sensitive properties based on whether the recipient is an external agent or an internal auditor.
Open Questions and Honest Limitations
While these components function and pass automated tests in the prototype, this architecture is still young, and several hard questions remain open:
- Latency Overheads: Introducing canonical JSON serialization, cryptographic signing, and gateway hops adds millisecond overhead. For high-frequency, low-latency read operations, full grant minting may be overkill.
- Schema Evolution: If an enterprise updates its backend API schema, how should existing bilateral contracts handle version mismatches gracefully without breaking ongoing workflows?
- Formal Security Audits: None of the cryptographic ceremonies or state machines have undergone external peer review or third-party penetration testing yet.
- LLM Expressiveness vs. Rigid Rules: Striking the right balance between giving agents creative freedom to solve problems and keeping deterministic guardrails tight enough to ensure safety is an ongoing design challenge.
Wrapping Up & Asking for Feedback
I don't think the long-term solution to agent safety is asking humans to manually approve every single low-level read or click. But I also don't think the solution is giving autonomous models raw API keys and hoping system prompts prevent errors.
Separating probabilistic reasoning from deterministic execution authority feels like a much sturdier foundation for real-world agent integration.
That said, I want to emphasize with genuine humility: this is an exploratory project, not a solved problem. I am one engineer investigating an architectural hypothesis, and I know there are likely blind spots, subtle edge cases, and practical hurdles I haven't encountered yet.
I would be deeply grateful for feedback, critiques, and counterarguments from engineers working on agent runtimes, API security, and authorization systems. Where does this model break down in your production environments? What failure modes are unaddressed? Let's discuss in the comments below.
Note on the code: If you are curious to inspect how these mechanics look in practice or want to audit the Rust implementation, the exploratory prototype is open on GitHub at fcn06/trust_gateway (along with a working draft whitepaper on the threat model in the repository). Please treat it as an experimental research artifact rather than a polished library.
Top comments (0)