# Security model

Infrapilot lets an AI agent change production infrastructure with nobody reviewing each change first. This page says exactly what makes that safe, and where the limits of those guarantees are.

## The agent proposes; an engine decides

Pilot is a language model in a loop with tools. Its tools read state and telemetry, and submit **transactions**. It has no shell, no cloud SDK and no credentials of its own to call a cloud API with. The only way anything changes is a transaction, and every transaction goes through an engine that is ordinary code:

1. **Capability**: Pilot may touch only this fleet's resources, and never the intents or the boundary.
2. **Rules**, on the graph the transaction would produce: the provider's schema for the resource type; the spec's bounds, cost ceiling and boundary; controller ownership; at most half of any capacity removed per change; Pilot deletes only what it created; irreversible operations need a named person.
3. **Preconditions**: the change must cite the version of every resource it read. If anything moved since, it is refused.
4. **Change budget**: a token bucket that refills only while objectives are met, so a run of bad ideas runs out of budget.
5. **Health gate**: after the change is applied the resource is read back and checked. On failure the stored inverse runs automatically.

None of this is an instruction in a prompt. A model that is wrong, confused or manipulated still cannot get a change past the engine, and cannot do in your cloud what the role you created does not allow.

## The ledger

Every commit, refusal, health check, undo, outside change, request and answer is an entry in an append-only ledger, each carrying the SHA-256 hash of the entry before it. `GET /api/workspaces/<id>/ledger/verify` recomputes the chain and replays the ledger into the State Graph; the two must agree. The ledger is yours: `GET /api/workspaces/<id>/export` returns all of it.

## One sandbox per workspace

Each workspace's agent runs in its own container:

- read-only root filesystem; the workspace's state volume is the only writable mount
- non-root user, every Linux capability dropped, no privilege escalation
- memory, CPU and process limits
- nothing listens inside it; it only makes outbound calls, to your cloud's APIs and to Infrapilot's model gateway
- its environment holds that workspace's cloud credentials and nothing else

It does not hold another customer's data, Infrapilot's database, the key that encrypts stored credentials, or Infrapilot's model API key. Model calls go through a gateway with a token that works only for that workspace's model calls and is revoked when the agent stops.

## Cloud credentials

| Cloud | How Pilot gets access | What Infrapilot stores |
| --- | --- | --- |
| AWS | assumes an IAM role you created, with an external id unique to your workspace, for one hour at a time | the role's ARN. No keys. |
| Google Cloud | impersonates a service account you created, for one hour at a time | the service account's email. No keys. (A key file is supported if you prefer; it is then stored encrypted.) |
| Azure | a service principal's client secret | the secret, encrypted at rest; in the clear only inside your sandbox |

Stored secrets are encrypted with a key held by the control plane. Tokens (sign-in links, sessions, setup and API tokens, answer links) are stored only as hashes.

To cut Pilot off, delete the role, service account or service principal. Nothing else is needed, and everything it managed keeps running as it was left.

## Payments

You pay on Stripe's own page. Card details never reach Infrapilot; we keep Stripe's customer and subscription identifiers and the subscription's status. If a subscription ends, Pilot stops making changes and keeps watching and recording. Nothing in your cloud account is changed or removed for non-payment.

## People

- Sign-in is by emailed link; sessions are HttpOnly cookies, and state-changing requests must come from Infrapilot's own pages.
- Only people change intents and boundaries. Pilot's capability denies those nodes, and an approval is recorded under the approver's name.
- Answer links in email are personal and expire after a week. Anyone holding one can answer that one request as that recipient, so treat a forwarded request email as you would a forwarded approval.
- Pausing the agent takes effect at its next tick: it keeps observing and recording, and decides nothing.

## What this does not protect against

- **A role that allows too much.** The engine enforces the spec; your cloud's IAM enforces the role. Grant what the provider page lists and no more.
- **A spec that asks for the wrong thing.** Pilot keeps what you declared true. A cost ceiling set too high will be respected as written.
- **Changes made outside Infrapilot.** They are detected, attributed and recorded, and Pilot keeps or reverts them against your intents, but it cannot prevent someone with console access from making them.
- **Types without a cost model.** A resource type Pilot has no profile for counts as zero toward a cost ceiling. Such types are observe-only unless you set `boundaries.manage: all`.
- **Secrets kept in plain configuration.** Pilot's record of a resource is its configuration as your provider returns it, and that record is in the ledger, on the dashboard and in what the model reads. Where a provider returns plain-text environment variables as part of the configuration (a Cloud Run service, an Azure Container App, an ECS task definition), their values are in the record. AWS Lambda environment variables are left out. Values a provider treats as secrets and does not return (Container App secrets, Secret Manager and Key Vault references) are never recorded. Keep secrets in your provider's secret manager and reference them.
- **Secrets in a workload's `spec`.** Do not pass a secret value in the spec; it would be recorded. Pass a reference (a Key Vault secret URL, a Secret Manager name). On Azure a change that carries a Container App secret's value is refused outright.
