Every submission through the hosted runner is signed with a SHA-256 hash. That hash is computed from the licence ID, the pathway, a canonical (sorted) JSON of the payload, and the server-stamped timestamp. It's tamper-evident, it's reproducible, and it's defensible.
STEPS- 01
What goes into the hash
The SHA-256 input is the JSON string of `{license_id, pathway, payload, submitted_at}`. Field order is fixed. Whitespace is canonicalised. Two identical submissions produce identical hashes.
audit_hash = sha256(JSON.stringify({ license_id, pathway, payload, submitted_at })) - 02
Where it appears
On the candidate's success screen. In the database (`submissions.audit_hash`). In the submissions CSV (`audit_hash` column). On the PDF generated from a recorded submission (Phase 2 — currently the field is in the footer).
- 03
What it proves
That this exact payload, against this exact licence, at this exact second, hasn't been altered since submission. If the database changes, the hash will not match a recomputation.
- 04
How an inspector verifies
Open the audit log row in the dashboard. The hash and the canonical payload are both visible. The inspector can recompute SHA-256 of the canonical payload using any tool (e.g. `shasum -a 256`) and confirm the match.
echo -n '<canonical-json>' | shasum -a 256
- 05
What it doesn't prove
It doesn't prove the submitter is who they say they are — that's the right-to-work + DBS check. It doesn't prove a specific person filled it out — that's the signature block on the PDF. It proves the data hasn't been changed since it was captured.
Is SHA-256 enough?
Yes. SHA-256 is the standard for tamper-evidence in regulatory contexts. NHS Digital, GOV.UK Verify, and HMRC all use it.
What about the printed PDF — does it have an audit hash?
The PDF you download for blank-form filling has a licence slug + generated date in the footer (the audit footer is the trail). The hash is generated only when a real submission lands through the hosted runner.
Can I include the hash in my own audit logs?
Yes — pull the submissions CSV and store `audit_hash` alongside your own audit metadata.