# API, SDK and SIEM export

## The API description

[`/openapi.json`](/openapi.json) is OpenAPI 3.1 and is generated, never hand-maintained. `server/openapi.test.js` fails the build when it drifts:

- every route the operator serves is described;
- every described route answers;
- security matches the operator's own public set;
- the file is fresh.

Two things in it are unusual:

- **The raw-data wall is in the schema.** Every request body forbids the fields the operator refuses: value, unit count, band, table, salt, grade, document salt and the rest. So a generated client cannot even be asked to send one.
- **The pushes are described.** The top-level `webhooks` section documents the signed deliveries, their headers and the retry contract.

## The JavaScript client (tested)

The reference client ships inside [`/mirror-kit.js`](/mirror-kit.js) (`MirrorKit.client`), next to the kernels it verifies with. It runs in Node 20+ and in the browser.

```js
const { createClient } = require("./mirror-kit.js").client;       // browser: MirrorKit.client
const mirror = createClient({ baseUrl: "https://mirror-operator.vasileburuian94.workers.dev" });
await mirror.login(kid, (canonicalText) => signWithYourKeyOrHsm(canonicalText));
const { record } = await mirror.reconcile({ shipment_ref, legs, policy });    // already verified
const clock = await mirror.clock({ budget_ms: 1000 });                        // within_budget | out_of_budget | undetermined
```

- **Every signed answer is verified before you see it,** against `/jwks`, with the same kernel the operator signs with. A tampered answer throws `unverified`.
- **It refuses to send raw trade data.** The same fields the operator refuses are refused before a byte leaves, at any depth and in any case (`raw_leaked`).
- **It never holds your key.** `login()` takes a signing function, so the key can stay in the HSM.
- **Duplicate financing.** `mirror.exposure.commit(claim)` posts a claim made inside the bank by `mirror-bander exposure-claim`, and refuses anything that is not a locator commitment, so a document number cannot be posted by mistake. `mirror.exposure.collisions(commitment)` asks about one document you committed, and `mirror.exposure.check(claim)` about all of a claim's. A collision record is verified before it is returned. An amount, a borrower or a commodity riding with an exposure is refused before a request is made. See [duplicate financing](/double-financing.md).
- Tested against a running operator by `server/sdk.test.js`.

## For AI agents (MCP)

`sdk/mirror-mcp.js` wraps this client as a Model Context Protocol server over standard input and output, so an agent can call Mirror's checks with the same verification and the same raw-data wall. It has ten tools, holds no key, and has no tool that bands, scores or files. See [Mirror for AI agents](/ai-agents.md).

## Python, Go, Java and .NET

Four more SDKs do the same job in a bank's own language. Each ships as source in the repository, has no dependency outside its standard library (Go adds `golang.org/x/text` for Unicode normalisation), and contains two parts.

**The checks a bank's code runs:**

- canonical JSON;
- the key id (`kid`) of a public key;
- ES256 verification with the key selected by `proof.kid`, and the low-S rule for the signature's form;
- the raw-data scan, with the same key normalisation as the operator: NFKC, invisible characters, look-alike letters and camelCase;
- webhook subscription and delivery ids, and delivery verification;
- the residency check (`meets`), described in [data residency](/data-residency.md).

**A client that verifies every signed answer before returning it:**

- reads: `/jwks`, `/cannot`, `/roadmap`, `/residency`, and a nonce-bound `/time`;
- sign-in through a signing function you supply, so your key stays in your HSM;
- webhook subscriptions;
- generic `get` / `post`, where `post` refuses a raw field before anything is sent.

| Language | Where | Tested with |
|---|---|---|
| Python | `sdk/python/mirror_sdk` (standard library only; P-256 is implemented in the package) | Python 3.14.7 |
| Go | `sdk/go` (module `mirror/sdk`) | Go 1.27.1 |
| Java | `sdk/java/src/dev/mirror/sdk` (JDK only) | Temurin JDK 21.0.12 |
| .NET | `sdk/dotnet/Mirror.Sdk` (`net8.0`, no packages) | .NET SDK 8.0.425 |

```python
from mirror_sdk import Client
mirror = Client("https://mirror-operator.vasileburuian94.workers.dev")
mirror.login(kid, lambda text, challenge: your_hsm_sign(text))    # base64url ES256, made by your key holder
refusals = mirror.cannot()                                       # raises MirrorError("unverified") if tampered
where = mirror.residency("eu")                                    # {"record": ..., "meets": {"ok": True, "reasons": []}}
```

### How they are held to the same answers

[`/sdk-vectors.json`](/sdk-vectors.json) holds 100 cases generated from the JavaScript kernels by `server/build-sdk-vectors.js`. They include:

- canonical text, including number formatting and string escapes;
- kids;
- valid, tampered, wrong-key and high-S signatures;
- disguised raw fields;
- webhook ids and deliveries;
- residency answers.

The checks:

- `server/sdk-vectors.test.js` holds the JavaScript kit to the vectors, and fails if the file is stale.
- `server/sdk-polyglot.test.js` runs each language's own vector runner. It then runs each client against a running operator that the test starts in production posture. It checks sign-in through the bander, verified reads, a changed answer refused, raw data refused before sending, and the webhook round trip. Current counts, vectors and client checks together: Python 113, Go 112, Java 112, .NET 112.

### What they are not

- **Not published to a package registry.** Nothing is on PyPI, Maven Central, NuGet or the Go module proxy, and no package names are reserved. Build or vendor them from source.
- **Not generated from `/openapi.json`.** They are written by hand and held to the vectors. For endpoints they do not wrap, use `get` / `post`, or generate an HTTP layer from the OpenAPI document with your usual generator.
- **Independent of the specification text?** No. They are ports of the kernels. The separate evidence that SPEC §8.2 is enough on its own is `conformance/reference-impl.js`: it was written from the spec text without importing the kernels, and reproduces [`/conformance-vectors.json`](/conformance-vectors.json) byte for byte.

## SIEM

`POST /audit/export` (member session; a desk key needs `read`) returns **your own** signed records as JSON lines:

- verdicts where your leg authenticated;
- governance events you signed.

Each line names the record by digest and signer, and on request carries the signed record itself. The batch is signed, and `lines_digest` covers the lines. Pull on a schedule with the returned `cursor`, which pages through every line exactly once. Mirror assigns no severity and no score.

The operator stores **no SIEM credential**: your SIEM pulls, or a relay inside the bank forwards verified webhook deliveries with the SIEM's token.
