---
title: "Exports and signed packages"
---

# Exports and signed packages

Export the audit log and vendor AI events as signed files, check an export's manifest yourself, read the context evidence chain, and what the compliance audit package contains.

## Audit log exports

An export writes one organisation's audit entries for a time window, oldest first, to an NDJSON or CSV file, and records a manifest signed with the audit signing key. Anyone holding the file, the manifest and the signature can check that the file is the export Govern produced.

### Request an export

```http
POST /api/v1/audit-logs/export
Content-Type: application/json

{"format": "ndjson", "from": "2026-07-01T00:00:00Z", "to": "2026-10-01T00:00:00Z",
 "action_prefix": "context.", "outcome": "", "resource_type": "", "project_id": ""}
```

- Needs `audit:create`. `from` and `to` (RFC 3339) are required; `format` is `ndjson` (default) or `csv`; `pdf` and `stix` are refused.
- Optional filters: `action`, `action_prefix` (not both), `resource_type`, `outcome`, `project_id`. Each row carries `project_id` (the last CSV column).
- The answer is `202` with the export. A worker writes it in the background.
- Exports read both the hot and cold tiers.
- An export is bounded (`NI_AUTH_AUDIT_EXPORT_MAX_ROWS`, default 1,000,000): a window with more rows fails with a message to narrow it, never a truncated file.

### Follow and download

| Route | Permission | What |
|---|---|---|
| `GET /api/v1/audit-logs/exports` | `audit:read` | The organisation's exports |
| `GET /api/v1/audit-logs/exports/{exportID}` | `audit:read` | Status and, once completed, the signed manifest |
| `GET /api/v1/audit-logs/exports/{exportID}/download` | `audit:read` | The file, checked against its manifest before it is sent; the download is audited first |

A stored file that no longer matches its manifest is never delivered (`409 export_integrity`). Files are kept for the account plan's export retention on the hosted service, else for `NI_AUTH_AUDIT_EXPORT_RETENTION_DAYS` (default 7, 1 to 365), and then deleted; keep your downloaded copy and its manifest.

Export files live in the shared object store (an S3 or S3-compatible bucket, or a directory every server replica mounts), so any replica can run an export and any can serve its download.

### The manifest

| Field | Meaning |
|---|---|
| `version` | Manifest format, `1` |
| `export_id`, `organization_id` | The export and its organisation |
| `source` | Set only for vendor event exports (`vendor_events`) |
| `format` | `ndjson` or `csv` |
| `from`, `to`, `filters` | The window and filters requested |
| `row_count` | Entries in the file |
| `first_entry_id`, `last_entry_id`, `first_entry_hash`, `last_entry_hash` | Where the file sits in the organisation's chain |
| `file_sha256`, `file_size` | The file itself |
| `generated_at` | When the manifest was made |

The signature is Ed25519 over the bytes `keystone-audit-export/v1\n` followed by the manifest exactly as stored. To check an export yourself:

1. Compute the SHA-256 of the downloaded file and compare it with `file_sha256`, and its size with `file_size`.
2. Fetch `/.well-known/keystone-audit-keys.json` and take the public key named by the signature.
3. Verify the signature over `keystone-audit-export/v1\n` + manifest bytes.
4. Compare `first_entry_hash` and `last_entry_hash` with the first and last rows of the file, and the row count.

For proof of the chain itself, rather than of one export, use [`keystone-audit-verify`](https://neuralinverse.com/docs/evidence/verify-offline/).

## Vendor AI event exports

Vendor events from the [connectors](https://neuralinverse.com/docs/evidence/vendor-connectors/) are exported the same way, with the manifest's `source` set to `vendor_events`:

```http
POST /api/v1/vendor-audit/events/export
{"format": "csv", "from": "2026-09-01", "to": "2026-09-30", "vendor": "anthropic", "user": "", "action": ""}
```

- Needs `audit:create` and `audit.vendor_content:view` (the payloads can hold chat content). Status and download use the audit export routes above; downloading a vendor export also needs `audit.vendor_content:view`.
- Without dates, the last 30 days; at most 366 days.
- `GET /api/v1/vendor-audit/events/exports` lists the organisation's vendor exports.
- Refusals of vendor content are audited.

## The context evidence chain

Retrieval attributions, model-call links and agent-file session records are kept in a separate HMAC chain per organisation, keyed with `NI_OBSERVABILITY_SIGNING_KEY` and verified every day at 05:00.

| Route | What |
|---|---|
| `GET /api/v1/observability/compliance-evidence?org_id=&from=&to=&category=&limit=&offset=` | Records of the organisation (`from` and `to` required, RFC 3339); answers `{"records", "total", "limit", "offset"}` |
| `GET /api/v1/observability/chain/verify?org_id=&category=&from=&to=` | Verifies one category's chain from its first record through the last record before `to` |

A broken chain is reported as an audit event and the `observability.chain.broken` webhook event. The console's attributions page shows the evidence chain for a date range, with JSON export and chain verification.

## Compliance audit package

`GET /api/v1/compliance/audit-package?framework=<pack name>&from=&to=` (`compliance:read`) returns the organisation's findings and evidence records for a framework as one JSON document with a `hash` field.

State plainly what this package is before you hand it to an assessor:

- The `hash` is a SHA-256 over the findings and evidence. It is **not a signature**: anyone can recompute it, so it shows the package is internally consistent, not who produced it. For signed evidence, use an audit log export.
- `from` and `to` are recorded in the package, but the findings and evidence in it are not filtered by them.
- Each evidence record carries `signed_hash`, also a plain SHA-256 of its content.

See [Compliance rule packs](https://neuralinverse.com/docs/evidence/compliance/) for what the findings mean.
