AI & Engineering

Authority belongs in code

Authority over money belongs in code, not in a prompt

11 min read

Once a support system can move money, the governing question stops being what the model answered. It becomes who authorized it. Those are different questions, and only one of them has a control attached to it.

The common answer is a line in a system prompt. Never issue a refund outside the return window. Always check the order first. That line is not access control. It is advice, delivered to a probabilistic system through the same channel that carries the customer's message — and a system whose one faculty is reading cannot reliably tell an instruction from a message shaped like one.

Access control is a different object. It is a check that runs outside the model, on the path to the action, and it does not care what the conversation concluded.

A prompt is advice, not a permission

OWASP puts prompt injection first in its Top 10 for Large Language Model Applications, and the entry is direct about the ceiling of its own defenses: given the stochastic influence at the heart of the way models work, it says, it is unclear whether fool-proof methods of prevention exist. The body cataloguing the risk is the one saying so.

The surface is wider than a typed message. The seventh attack scenario in the same entry is multimodal — a prompt embedded inside an image that accompanies benign text, altering the model's behavior when the two are read together. In support that is not exotic. Photographs are ordinary evidence: the crushed box, the wrong color, the item that arrived broken.

So the rule and the attack arrive through the same door. A rule that lives in a prompt is a rule the input is allowed to argue with.

OWASP's own conclusion is architectural. Its entry on excessive agency names complete mediation in the prevention list: implement authorization in downstream systems rather than relying on an LLM to decide if an action is allowed.

NIST frames the same move as governance. The AI Risk Management Framework lists, among practical approaches to safety, the ability to shut down, modify, or have human intervention into systems that deviate from intended or expected functionality. Its MANAGE function asks for mechanisms that are in place and applied, with responsibilities assigned and understood, able to supersede or disengage a system whose outcomes fall outside intended use. A mechanism with an owner. Not an instruction with a hope.

Four checks, and all four have to pass

Razviel's design places that authority in deterministic code that runs in the backend at the moment an action would execute. The model proposes: an action drawn from a closed vocabulary, and a value. The gate decides.

It decides by asking four things. Whether a policy exists at all for this case and this customer. Whether the proposed action is among the actions that policy allows. Whether the value sits within that policy's limit for that specific action. Whether the eligibility conditions hold — the return window, the exchange count, the fulfillment status of the order. All four, or the action does not fire.

EVERY PROPOSED ACTION, AND THE FOUR CHECKS IT HAS TO CLEAR permit abcd A policy exists for thiscase and this customer.if not — escalateThe proposed action is onethe cell allows.if not — forbidThe value is within thecell’s limit for that action.if not — escalateThe eligibility conditionshold.if not — forbid All four, and only all four. the lens is the only place every region overlaps
EVERY PROPOSED ACTION, AND THE FOUR CHECKS IT HAS TO CLEAR abcd permit aA policy exists for thiscase and this customer.if not — escalatebThe proposed action is onethe cell allows.if not — forbidcThe value is within thecell’s limit for that action.if not — escalatedThe eligibility conditionshold.if not — forbid All four, and only all four. the lens is the only place every region overlaps
A conjunction, not a list. The gate re-loads the cell from source at the moment of the check — never from cache, and never from what the model asserted about it. A failed eligibility condition escalates instead of forbidding when the brand has marked that class of case for review.

Where the values come from matters as much as the checks. The design re-loads the policy from source at the moment of the decision, and re-reads the facts behind eligibility: the purchase date, the delivery date, the order's status. A model that reports "this is within the limit" has said nothing the gate consumes. And because the read is live, a brand that raises a limit changes the next decision, not one after a sync window.

The failure mode is narrow by design. An action that carries a limit with no limit recorded fails the third check, so that action does not fire — and only that action. The ticket continues down the cascade.

There is also a check the design deliberately omits: there is no fraud detector, on the customer or on the operator. A detector would make the outcome depend on a judgment about a person's history rather than a comparison of case, segment, action and value — the exact property the design exists to have. What protects the money is the limit inside the cell.

This describes the architecture. The gate is specified as the mandatory link before any write, and that ordering is a dependency of the build rather than an option inside it.

Forbidding is not escalating

A gate with two outcomes is a filter. This one has three, and the distinction between the last two decides whether the operation underneath it is affordable.

three outcomes, three audiences

Permit

The customer hears it, from the system.

Costs no human time.

Forbid

The customer hears it, from the system.

Costs no human time.

Escalate

The brand hears it, with a typed reason.

Costs a person, and a wait.

Two of the three never reach a queue. Collapsing them moves the line.

The three outcomes, by who receives the answer. The distinction that decides the cost is not what each one means, but which of them ever reaches a person.

Permit: all four pass, and the action executes. Forbid: the policy exists and the answer is no — the action is not one the cell allows, or an eligibility condition is not met. The system delivers the no itself, with whatever route is still open. Escalate: there is no policy, or the value is over the limit, or the brand has claimed that class of case for itself. The case moves to the brand assembled, with a typed reason.

A “no” the system already knows is not an escalation. It is an answer.

Collapsing forbid into escalate is the expensive mistake, and an easy one. A system that escalates every no it already knows buries the humans in work it could have finished itself — and it does so with the highest-volume, lowest-value cases in the queue: the return that arrived after the window, the request for something the brand does not do. Escalate when you lack the answer or the standing. Not when the answer is unwelcome.

A conversation that ends in agreement is not an authorization. "Yes, go ahead and refund it" is a fact about what the customer wants. The customer is a party to the transaction, not an approver of the brand's policy. In the design, the execution that follows the agreement passes the same gate as the execution that would have preceded it. Consent changes what the system is trying to do. It does not change what the system is permitted to do.

The same holds against the operator. A human node working a case in the console proposes an action, and that action runs through the same authorization path, because the gate is not a filter on the model. It is the authorization layer for the action, whoever proposed it. The operator never holds the brand's credentials and never works inside the brand's store — the console speaks to the brand's systems, and the gate stands between the two.

One place decides whether an action may execute, and it is the same place for every path into it.

The rule lives inside the case, not in a global registry

A policy could be stored as a list of rules about the brand: refunds up to a limit, exchanges inside a window, store credit for these categories. That structure is simpler, and wrong — for a reason that only shows up in a real conversation.

A customer writes in asking where their order is. Somewhere in the message they mention that they will want a refund if it does not arrive this week. If the rules live in a global list, the word is enough to reach one. The system now holds a refund rule in a case that is not a refund case.

Under a global registry

The word “refund” appears, and it reaches a refund rule.

The key is a word in the message.

Under a cell lookup

The case is classified where-is-my-order. No refund rule is reachable.

The key is the classified case.

One message: “where is my order — I’ll want a refund if it is late”.

The same sentence, under two ways of storing a policy. What changes is not the rule set. It is what the system uses as the key.

So the architecture stores the policy as a matrix and looks it up by the pair that identifies the situation: the case type and the customer's segment. The limits live inside that cell. A rule belonging to the refund case is unreachable from a where-is-my-order case, because the lookup key is not a word in the message — it is the classified case. There is no global registry to reach into.

That property is an injection defense as much as a correctness one. Naming an action does not select a rule. In a design where authority is a lookup, saying the word buys nothing.

It also bounds the residual risk, which is a wrong classification. The gate always imposes the ceiling of a real cell, re-read from source, so the worst outcome of a misclassification is an action inside some other real limit — never one with no limit at all. A brand-level ceiling sits above every cell. The catastrophic direction is absent, not merely rare.

Where the matrix has no cell — an influencer proposal arriving at a store with no partnership program — the case escalates. That is the design working. A gap in a policy is the brand's decision to make, and the brand's answer becomes the rule.

The record of an executed action, in this design, is not that a model was confident. It is that a policy existed for that case and that customer, that the action was one the cell allowed, that the value sat under the limit, and that the conditions held — each re-read from source at the moment of the write, by code that does not read instructions.

The takeaway

Once a support system can move money, the control that matters runs outside the model. The model proposes an action and a value; deterministic code re-reads the brand’s policy cell from source and decides. For a Shopify Plus brand, that turns a question about prompt wording into a question about access control — and access control can be inspected.

What to check before any system, AI or human, is allowed to move money:

  1. Where the limit is enforced. A rule that lives in a system prompt is a rule the input is allowed to argue with. The enforcement point is code on the path to the action, and it reads the policy, not the conversation.
  2. All four checks, every time. A policy exists for this case and this customer; the action is one it allows; the value sits within the limit for that action; the eligibility conditions hold. All four pass, or the action does not fire.
  3. Where the values come from. The limit and the facts behind eligibility — purchase date, delivery date, order status — are re-read from source at the moment of the decision. A model’s statement that a value is within the limit is not an input the check consumes.
  4. Three outcomes, not two. A no the system already knows is delivered by the system, as an answer. Only a missing policy, a value over the limit, or a class of case the brand claimed for itself reaches a person. The share of a queue made of unwelcome answers is the cost of collapsing the two.
  5. One path for every proposer. The customer’s yes, the model’s proposal and a human operator’s proposal pass the same check. An exception for any one of them is a second place where authority lives.

Authority over money is a mechanism with an owner, and it runs the same way whether the input is a customer, an operator, or a paragraph painted into the pixels of a photograph.

Sources

  1. OWASP Top 10 for Large Language Model Applications (v2.0, 2025) "LLM01:2025 Prompt Injection" — sections "Prevention and Mitigation Strategies" and "Example Attack Scenarios," Scenario #7 (Multimodal Injection)

    https://github.com/OWASP/www-project-top-10-for-large-language-model-applications/blob/main/2_0_vulns/LLM01_PromptInjection.md

    SupportsThe two sentences reporting that OWASP itself states it is unclear whether fool-proof methods of prevention for prompt injection exist, and that one of its cataloged attack scenarios is an instruction embedded in an image that accompanies benign text.

  2. OWASP Top 10 for Large Language Model Applications (v2.0, 2025) "LLM06:2025 Excessive Agency" — section "Prevention and Mitigation Strategies," item "Complete mediation"

    https://github.com/OWASP/www-project-top-10-for-large-language-model-applications/blob/main/2_0_vulns/LLM06_ExcessiveAgency.md

    SupportsThe sentence attributing to OWASP the instruction to implement authorization in downstream systems rather than relying on an LLM to decide whether an action is allowed.

  3. National Institute of Standards and Technology (NIST) Artificial Intelligence Risk Management Framework (AI RMF 1.0), NIST AI 100-1 — §3.2 "Safe" and §5.4 "Manage," subcategory MANAGE 2.4

    https://nvlpubs.nist.gov/nistpubs/ai/NIST.AI.100-1.pdf

    SupportsThe paragraph reporting that NIST lists, among its practical approaches to AI safety, the ability to shut down, modify, or have human intervention into systems that deviate from intended or expected functionality, and that MANAGE 2.4 asks for mechanisms that are in place and applied, with responsibilities assigned and understood.

The gate in place, the rules around it, and what the design refuses to promise: How Writ enforces authority