# VeriProof reference compliance receiver

Try a Data Access import before connecting your compliance application. This receiver retrieves publications through the public API and saves them to SQLite, along with their controls, original files, and import receipts.

Open the local browser view to inspect what arrived. You can then adapt the import transaction to your own application and keep its existing review workflow.

## Requirements

- Node.js 22.13 or later with `node:sqlite`. No package installation or database server is needed. Node 22 labels its SQLite API experimental; choose your organization's tested runtime when adapting the example.
- A Data Access integration configured in the customer portal, with the applications, datasets, and frameworks you want to retrieve.
- The publisher's public verification key, obtained through a separately trusted channel.
- A private local directory for the evidence and database. Apply your organization's file permissions and retention policy. This example stores SQLite data without encryption.

Keep the downloaded scripts together with this guide and `openapi.json`.

## Run the first import

### Set up access

In **Settings → Integrations → Data Access**, register an integration and choose its records. Check **Initial publications** for prepared files, or prepare a publication manually.

Supply credentials through your process environment or secret manager. Choose one method:

| Method | Environment variables |
| --- | --- |
| Signing key | `VERIPROOF_CLIENT_ID`, `VERIPROOF_SIGNING_KEY_ID`, `VERIPROOF_ASSERTION_AUDIENCE`, `VERIPROOF_PRIVATE_KEY_PATH` |
| Client secret | `VERIPROOF_CLIENT_ID`, `VERIPROOF_CLIENT_SECRET` |

For a signing key, register the P-256 public key in the portal and keep its private key in your system. Use the exact audience displayed in the portal. Keep secrets out of command arguments and source control.

### Import publications

Run this command from the extracted folder, replacing the origin and paths with your settings:

```text
node reference-receiver.mjs https://YOUR-API-ORIGIN ./receiver-data ./trusted-publisher.pem
```

Each run processes up to 100 publication records. Run it again while `hasMore` is true, then schedule it at your chosen polling interval. A verified availability notification can also start the command. You'll need your own webhook endpoint to receive those notifications.

The result shows the processed count and total committed revisions. Exit code `0` means the page completed, `1` means processing failed, and `2` means required arguments are missing. When a record fails, the polling position stays before that record so the next run can retry it.

Read requests retry HTTP 429 and 503 up to twice. They honor `Retry-After` delays of up to 60 seconds. If the delay is longer or failures continue, schedule another run later. Authentication failures stop immediately; the client doesn't replay assertions.

### Inspect the results

Start the read-only view:

```text
node receiver-view.mjs ./receiver-data
```

Open **http://127.0.0.1:3108**. Filter by dataset, choose a publication, and compare its control results with the original files. Refresh after another import. Press Ctrl+C to stop the view.

The view is for use on your computer. It listens only on loopback, rejects unexpected Host headers, and has no user accounts. Keep it local when adapting the receiver.

## How records are stored

The destination is `compliance.sqlite`. Leave its tables unchanged while imports run.

| Table | Contents |
| --- | --- |
| `receipts` | One source revision per API origin, integration ID, and publication ID, with original files, source identity, and import time. |
| `controls` | Saved control results, evaluation IDs, and each complete source control record. Missing and unavailable values are preserved. |
| `availability` | The last observed availability and check time, including withdrawal status. |

Inventory and oversight records stay in their original JSON structure. Assessment controls are also extracted to show how a field mapping can work. Historical revisions and correction references remain available for review.

Before saving anything, the receiver verifies the signature and file hashes, checks current availability, and enforces the signed retrieval expiry. It saves the receipt, controls, files, and availability in one transaction, then advances the polling position.

If a run stops after the commit, its retry finds the existing receipt. Controls and files aren't duplicated. A different payload claiming the same publication ID is rejected, as are imports whose current access check fails.

Keep polling after imports to pick up withdrawals. Availability can change after the receiver's last check.

## Recover an interrupted run

| Situation | Action |
| --- | --- |
| A process stopped unexpectedly | Confirm that the process recorded in `poll.lock` has stopped, remove that lock file, and run the command again. |
| A `.pending` directory remains | Leave those unfinished files out of manual imports. Resume through the receiver. |
| Access changed or a cursor expired | Start a new polling directory and reconcile records against existing destination receipts in your adapted importer. A new receiver directory creates a separate SQLite database. |
| A publication was withdrawn | Keep its history, but exclude it from evidence used for new decisions. |
| Retrieval expired | Stop new imports of that publication. Apply your evidence policy to assessments already imported. |
| Credentials were revoked | Update credentials before retrying. Existing local records remain subject to your retention and deletion policy. |

Run one poller per directory. When backing up SQLite, preserve its WAL files during operation. Stop the importer and local view before copying the database, or use SQLite's supported online backup mechanism.

## Connect your application

Replace `ComplianceReceiver.importPublication` with your destination transaction. In `syncReceiver`, return from the callback only after that transaction commits. Use the API origin, integration ID, and publication ID together to recognize retries.

Keep the original JSON, including fields you don't yet map. Preserve assessment revisions and evaluation IDs so reviewers can trace a result to its source. Let your application's review process decide how imported evidence supports a control.

Use your existing authentication, permissions, secret storage, monitoring, backups, and retention controls when deploying the importer. The pull interface uses HTTPS, JSON, and OAuth, so your implementation can use another language or database.

Check these cases before connecting the receiving workflow:

1. Import an assessment and compare its control results, evaluation IDs, periods, and file hashes with the source.
2. Interrupt the process after commit, then restart it. Confirm that no duplicate records appear.
3. Reject a corrupted file or revoked access without saving a partial import.
4. Import a later assessment revision and process a withdrawal. Confirm that history remains available and your evidence-selection rules use the right records.
5. Run two independent receivers to check that each can retrieve the same publications.

## Run the included checks

`reference-receiver.test.mjs` uses generated signing keys and temporary SQLite files. It checks rollback, restart after commit, duplicate prevention, source isolation, tampering, denied reads, and withdrawal history.

```text
node --test reference-receiver.test.mjs
```

Add checks for your application's field mapping and review workflow as you adapt the import.
