# Field Note 001 lab: checking an agent's export request

The scenario, identities, records, and runbook are fictional. A fixed JSON
proposal stands in for an LLM following the injected instructions. The code
runs the gateway checks and records exports in memory. It does not call an
LLM, an MCP server, or an external service.

## Run the lab

Use Node.js 22.12+ and put these four files in one directory:
`lab.mjs`, `lab.test.mjs`, `attack.json`, and `runbook.md`.
You do not need to install packages or set up credentials.

```sh
node lab.mjs
node lab.test.mjs
```

The second command runs 19 tests using Node's built-in test API. It exits with
a nonzero status if a test fails. From the repository root, you can also use
`npm test` and `npm run lab:agent`.

Each test creates its own host instance. Separate cases check an unassigned
record in the caller's tenant and a record in another tenant that an inconsistent
session fixture lists as assigned. Both must be denied.

The demo should produce these results:

- Without a policy check, the attack records one export.
- With the gateway, the attack returns `destination_denied` and records no export.
- An allowed internal review returns `allowed` and records one export.

Exports are stored in arrays. Even the unsafe baseline cannot send data anywhere.
`collector.example` is a reserved example name. The lab never contacts it.

## Files and evidence

- `runbook.md` contains the retrieved document and injected instructions.
- `attack.json` contains the fixed proposal chosen for the test. It is not model output.
- `lab.mjs` holds the fictional records, session fixtures, retrieval access check,
  tool policy, audit records, and export arrays. It also includes the unsafe baseline.
- `lab.test.mjs` checks denied and allowed calls, retrieval access, and stored state.
- `expected-output.json` contains output captured from `node lab.mjs` on
  Node v24.19.0, 2026-10-09. Compare it with your own run. Test timings are omitted.
- `ADR-001.md` explains the control choice, alternatives, and remaining risks.

An export is allowed only for `export_case`, a caller with the support role,
an assigned case in their tenant, and the configured destination `support-review`.
The gateway rejects extra fields and malformed inputs. The proposal cannot set
the caller's identity, role, assignments, destination policy, or case payload.
Audit records contain the decision without the document text or case payload.

## Limits

All components run in one process. The closures keep state private within the
code, but they are not an OS sandbox. `createHost('maya')` selects a trusted
session fixture. It does not authenticate a user. A real system must not let
an untrusted caller select their identity this way.

The unsafe baseline is exported so you can compare it with the gateway. It must
never be connected to a real tool dispatcher. These tests do not validate real
credentials, MCP transport, OAuth, network egress, model susceptibility, prompt
instructions, concurrency, durable logs, rate limits, or deployment security.
They do not establish production readiness or enterprise validation.
