Screening Appliance docs
Updates & Synchronization
How the appliance stays current β the versioned manifest, incremental per-dataset deltas, checksum self-healing, the epoch mechanism, atomic swap β and the air-gap side-load procedure.
Last updated 2026-08-02
Updates & Synchronization
The appliance ships with a baked-in data snapshot and, from then on, stays
current by periodically syncing from UPDATE_URL
(https://www.proofaml.com/updates/v1 by default). This page explains how
that sync works, and how to run entirely without it for air-gapped
deployments.
In plain language
Every SYNC_INTERVAL, the appliance:
- Downloads a small manifest describing the current state of every dataset β not the data itself.
- Compares it to the manifest it applied last time, and downloads only the deltas (changes) for datasets that moved β not a full re-download of everything.
- Verifies each downloaded file's checksum before touching anything on disk.
- Applies the deltas to a working copy, then verifies the result against the manifest's stated entity counts and content checksums.
- Only once that verification passes does it atomically swap the
working copy in as the live dataset. Until that swap happens,
/v1/searchkeeps serving the previous, already-verified data β a sync in progress, or a sync that fails partway, never produces a window where the API serves partial or corrupted data.
The manifest
curl -s https://www.proofaml.com/updates/v1/manifest.json
{
"manifestVersion": "2026-08-02T03:00:00Z",
"epoch": 4,
"datasets": [
{
"id": "us_ofac_sdn",
"entityCount": 17842,
"contentSha256": "9f2c...b71a",
"deltaUrl": "https://www.proofaml.com/updates/v1/us_ofac_sdn/2026-08-02.jsonl.gz",
"deltaSha256": "3ab0...e94f"
},
{
"id": "un_sc_sanctions",
"entityCount": 941,
"contentSha256": "4d61...20cc",
"deltaUrl": "https://www.proofaml.com/updates/v1/un_sc_sanctions/2026-08-02.jsonl.gz",
"deltaSha256": "77e1...0b3d"
}
]
}
The manifest is what dataVersion in /healthz and /v1/status
identifies, and what /v1/status's datasets[].entityCount is checked
against.
Delta files
Each dataset's deltaUrl points at a gzipped JSONL file β one JSON
object per line, each an upsert or delete operation:
{"op": "upsert", "id": "NK-a1b2c3d4", "entity": { "β¦": "β¦" }}
{"op": "upsert", "id": "NK-b2c3d4e5", "entity": { "β¦": "β¦" }}
{"op": "delete", "id": "NK-c3d4e5f6"}
Shipping only the changed records β not a full dataset re-export β is what keeps a daily sync small: a routine day's sync for a dataset is typically a handful of upserts/deletes, not the whole list.
Checksum verification and self-healing
Every sync verifies two things, at two different points:
- On download: each fetched delta file's SHA-256 must match
deltaSha256in the manifest before it's applied at all. A mismatch aborts that file's application (the previous data for that dataset stays live) and retries on the next sync interval. - After applying: once deltas are applied to a working copy, the
resulting per-dataset entity count and content checksum are compared
against the manifest's
entityCountandcontentSha256. If either doesn't match β for example, a missed delta from an earlier sync left the dataset in a state the incremental deltas can't reconcile β the appliance self-heals: it discards the incremental result and does a clean full re-download of that dataset instead of leaving it silently inconsistent.
This means an operator never needs to manually diagnose "my incremental deltas drifted" β the appliance detects and corrects it on its own on the next sync.
Epoch
epoch is a single integer in the manifest. Every dataset a given epoch
covers is described relative to that epoch's full snapshot; incremental
deltas only ever apply within the same epoch.
When ProofAML makes a change that incremental deltas can't safely express β
a schema change or a resolver change that affects how records are matched
or merged β it bumps the epoch. On seeing a new epoch, the appliance
invalidates all currently-fielded data for the affected datasets and
performs a fresh full snapshot download, rather than trying to apply deltas
against data built under different rules. This is why /healthz and
/v1/status surface epoch alongside dataVersion: a sudden epoch bump
in your monitoring is the appliance telling you a full resync is happening
(or is due), not an error state.
Atomic swap
The appliance never modifies the live, currently-served dataset in place.
It builds the new state in a working copy, fully verifies it (checksums +
counts, above), and only then swaps it in as the version /v1/search,
/v1/batch, and /v1/monitor/* read from. A container restart, a crash
mid-sync, or a failed verification all leave the previously-verified
dataset serving traffic, untouched.
Air-gapped operation
Set OFFLINE=true and the appliance never contacts UPDATE_URL β no
outbound connection is made, ever, on any schedule. This is the correct
setting for a fully air-gapped network.
To update an offline appliance, side-load the manifest and delta files into its volume manually:
- On a machine with network access (not the air-gapped appliance
itself), fetch the manifest and the delta files for the datasets you
want to update:
curl -s -o manifest.json \ https://www.proofaml.com/updates/v1/manifest.json curl -s -o us_ofac_sdn-2026-08-02.jsonl.gz \ https://www.proofaml.com/updates/v1/us_ofac_sdn/2026-08-02.jsonl.gz - Transfer
manifest.jsonand the delta files across your air gap by your organization's approved offline-media process, into a staging directory inside the appliance's volume:/var/lib/proofaml/updates/incoming/. - Trigger the appliance to apply them from local disk instead of the
network:
Withcurl -s -X POST http://localhost:8400/v1/admin/syncOFFLINE=true,/v1/admin/syncreads fromupdates/incoming/on the volume instead ofUPDATE_URL. The same checksum verification, count verification, and atomic swap described above apply identically to a side-loaded update β offline mode changes where the bytes come from, not how rigorously they're verified before going live. - Confirm the new version landed:
curl -s http://localhost:8400/v1/status
Admin endpoints
| Endpoint | Effect |
|---|---|
POST /v1/admin/sync | Trigger a sync immediately (from UPDATE_URL, or from the local staging directory if OFFLINE=true), instead of waiting for the next SYNC_INTERVAL. |
POST /v1/admin/resync | Force a full re-download of every loaded dataset, bypassing incremental deltas β the same self-healing path a checksum/count mismatch triggers automatically, run on demand. Use this if you suspect the on-disk state is inconsistent for any reason. |
See also
- Configuration reference β
UPDATE_URL,SYNC_INTERVAL,OFFLINE,DATA_DIR. - Search API reference β
/v1/status'slastSyncAt/nextSyncAt/updateUrlfields. - Troubleshooting / FAQ β stale data and checksum-mismatch scenarios.