Engine and gateway
The engine runs in your account. The controls on this page are enforced there, in code, before and around every model call and every tool call. None of them is an instruction in a prompt, so none of them can be talked out of by a model, an injected document or a user.
The gateway is the only path to a model
Section titled “The gateway is the only path to a model”What it is. Every model call an agent makes goes through the gateway. Agents have no other way to reach a model. For each call, in order:
- The call’s envelope is checked: which client, pack, task, agent and run it belongs to, its sovereign tier and its data class. A call that cannot be attributed is refused, because it cannot be budgeted.
- The model is checked against your policy’s allow list for that tier and data class, and priced from your price card.
- The kill switch, the run’s guards and every enclosing budget are checked, and the call’s estimated cost is reserved, in one database transaction.
- Personal data your policy names is masked before the prompt leaves, and restored in the answer.
- Only then does the call go out.
- Afterwards the reservation is settled at what was billed.
Settlement errs on the side of the cap. A permanent error (a bad request, a model not enabled) means nothing ran, so the reservation is released. A timeout or a transient error may have run and billed, so the reservation is kept as spent. Releasing it wrongly is how a flaky endpoint lets a run spend past its cap one timeout at a time.
Where. The gateway, in your account, with its store in your account.
Caps and kill switches
Section titled “Caps and kill switches”What it is. Spending caps on the tenant, a pack, a task, a person or a run, over a day, a month or a run’s total. A kill switch on any scope, which stops calls at once and can be revived. The engine also stops a scope on its own after repeated screening violations.
Where. The gateway checks them on every call, before the call goes out (step 3 above). A call over a cap or under a kill switch is refused and recorded with its reason; it never reaches the model, so it costs nothing.
Limits set in LightHouse reach your account as signed commands, made by a person, and your deployment’s policy decides whether to accept each kind. Revoking a grant and pulling the kill switch stay available locally at all times, because they only narrow.
Verify. Refused calls appear in LightHouse under Security and context → Safeguards, and by reason in GET /api/v1/refusals. Every stop and revival, with who did it (as a keyed hash, or none where the engine stopped the scope itself), is in GET /api/v1/kills.
curl "https://$AAL_TENANT.atlanticagenticlabs.com/api/v1/kills" \ -H "Authorization: Bearer $AAL_KEY" \ -H "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \ -H "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET"import os
import httpx
headers = { "Authorization": f"Bearer {os.environ['AAL_KEY']}", "CF-Access-Client-Id": os.environ["CF_ACCESS_CLIENT_ID"], "CF-Access-Client-Secret": os.environ["CF_ACCESS_CLIENT_SECRET"],}base = f"https://{os.environ['AAL_TENANT']}.atlanticagenticlabs.com/api/v1"print(httpx.get(f"{base}/kills", headers=headers).json())print(httpx.get(f"{base}/refusals", headers=headers).json())const base = `https://${process.env.AAL_TENANT}.atlanticagenticlabs.com/api/v1`;const headers = { Authorization: `Bearer ${process.env.AAL_KEY}`, "CF-Access-Client-Id": process.env.CF_ACCESS_CLIENT_ID!, "CF-Access-Client-Secret": process.env.CF_ACCESS_CLIENT_SECRET!,};console.log(await (await fetch(`${base}/kills`, { headers })).json());console.log(await (await fetch(`${base}/refusals`, { headers })).json());See the Read reference and Limits and alerts.
Out of the model’s reach
Section titled “Out of the model’s reach”What it is. The caps, the kill switch, the allow list and the tool policy live in your deployment’s policy and the gateway’s store, in your account. No tool an agent can call reads or changes them, and no MCP tool does either. Raising a limit or approving work is a command made by a signed-in person in LightHouse, never by an agent or a key (see API security).
The tool gate
Section titled “The tool gate”What it is. Every tool call is one question, asked in this order. Each step can only narrow.
- The run token. Each run carries a signed token that names the tools it may use and its autonomy. A token from another run, an expired one or a replayed one stops here.
- The tool must be in the token. The depth used (answer, plan, draft, implement) is the shallower of the one asked for and the token’s.
- Cedar must permit it. The engine’s tool policy is written in Cedar, generated from the agent cards themselves, validated against a schema, and proven by a self-test that checks, for every card at every depth, that exactly the tools the card grants are allowed and nothing else. Cedar denies anything not permitted. A tool that acts in the world is forbidden unless approved, and forbidden outright for an agent that may only draft. A pack’s denied tools are forbidden for every agent, whatever a card says.
- Scopes. Any scope asked for must be declared on the card for that tool and carried by the token.
- The person’s own credential. A tool that acts in the world gets the delegated credential of the person the run acts for, for exactly those scopes, or the call is refused. A scheduled run has nobody to act for, so its acting tools are refused. A shared service account is never the fallback: it would have more reach than the person.
Where. The engine, in your account. The same policy runs in process on every cloud and on premises.
Verify. Each refusal is counted by reason (token, tool, cedar, scope, person, credential) in the engine’s tool_denials_total metric, in your account. Under NDA we will run the self-test against your cards with you.
No long-lived keys
Section titled “No long-lived keys”What it is. Nothing in the stack depends on a long-lived cloud access key.
| Path | How it authenticates |
|---|---|
| Your pipeline applying the stack | Federated with your CI’s OIDC tokens (GitLab’s in the shipped bootstrap). A read-only plan role, and an apply role assumable only from the protected default branch in a protected environment, held inside a permission boundary so it can never grant itself more |
| The gateway to Amazon Bedrock | An IAM role, assumed |
| The gateway to the Claude API | Workload identity: the gateway’s role obtains a short-lived signed token (at most 15 minutes) for Anthropic’s audience only, and Anthropic’s federation rule matches that role alone |
| The gateway to Azure OpenAI | The gateway’s managed identity |
| The gateway to Vertex AI | The gateway’s service account |
| Your deployment to LightHouse | An Ed25519 key pair generated for the deployment; the private key stays in your secret store. Up to two keys may be registered at once, so rotation never interrupts telemetry |
Where. Your cloud’s identity service, configured by the stack.
Verify. Read the roles and policies the stack creates in your account; there is no IAM user and no access key among them. See AWS, Azure and Google Cloud for each cloud’s list.
Personal data masked before inference
Section titled “Personal data masked before inference”What it is. For every data class your policy lists, the gateway replaces emails, phone numbers, IBANs, card numbers and IP addresses with stable placeholders before the prompt leaves, and restores them in the answer. Names are not attempted: pattern-matching names is wrong often enough to give false comfort. A model-based recogniser is the adapter for that, configured per data class.
Where. The gateway, so no agent can forget to do it.
Answers checked against their sources
Section titled “Answers checked against their sources”What it is. Where your policy turns it on, the gateway checks an answer against the material it was given, and annotates or refuses an answer that is not grounded. Whether a cited passage was ever retrieved is checked as set membership in code, not by a model. Off by default.
Verify. GET /api/v1/grounding reports outcomes and counts of ungrounded claims. See Evidence.