DIY Edge Core — Option B#
Self-host verification so custody of your keys stays with you. You download the exact same Rust/WebAssembly core that powers the hosted gateway, run it on your own edge, and supply your master key from your own secret store. Because it is the same compiled core, verdicts never diverge from hosted (or from the Android app's read-back) — that parity is a guarantee, not a coincidence.
This path is for developers who need on-premise, air-gapped, or bring-your-own-cloud verification. It requires the perpetual Enterprise Edge license.
Prerequisites#
- An account holding an Enterprise Edge license (
$749, one-time, perpetual). This flips your account's DIY entitlement on, independent of any hosted subscription — a Sandbox account can hold an Edge license. - A hosted System created in the DIY custody tier (
POST /api/keys { "tier": "diy" }), which returns your master key once. - Node 18+ (or a Workers/Vercel/Lambda project) to run the sample.
Note
Draft: the Edge license is purchased through Stripe-hosted Checkout (
price_edge_license, one-time payment). The Stripe product wiring is still being finalized — until it is live, contact us to enable the DIY entitlement on your account. The download and verification steps below are fully functional today against an entitled account.
Step 1 — Create a DIY System and capture your master key#
DIY custody returns the master key exactly once and never persists the bytes server-side:
curl -X POST https://edgenfc.com/api/keys \
-H "authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{ "tier": "diy" }'Response:
{ "key_id": "sys_01H…", "tier": "diy",
"key": "00112233445566778899aabbccddeeff",
"key_version": 1, "warning": "shown once; not stored server-side" }Store that key in your secret store (Workers secret, Vercel env, AWS Secrets Manager, an HSM — your choice). It never goes into the DIY package, your repo, or any export.
Warning
00112233445566778899aabbccddeeffis a test key used throughout these docs. Never commit a real master key to source control or paste it anywhere. If you require the Edge license before it is enabled for your account, contact us.
Step 2 — Download the DIY package (no master key)#
Fetch the package for your System. It contains the core Wasm, a runnable verify template, and public config — and no master key:
curl https://edgenfc.com/api/download/diy?sys=sys_01H… \
-H "authorization: Bearer $TOKEN" -o diy-package.jsonThe payload advertises exactly that:
{ "system_id": "sys_01H…",
"contains_master_key": false,
"core": { "wasm_base64": "…", "js_glue": "…", "pinned_version": "0.1.0" },
"verify_template": "// supply YOUR master key; call verify_mirror_js(master, uid, ctr, mac, lastCtr).",
"config": { "sdm_mode": "mirror_plain" } }The pinned_version identifies the exact core build. Every verifier in the ecosystem — hosted, the Android app, and your DIY edge — runs a core pinned to the same version, which is what makes the parity guarantee hold. A core bump is announced in the changelog with a new DIY pin.
Decode core.wasm_base64 to edgenfc_core_bg.wasm and write core.js_glue to edgenfc_core.js next to it.
Step 3 — Verify on your edge#
The core exposes verify_mirror_js(master, uid, ctr, mac, lastCtr) (and verify_encrypted_js(…) for encrypted-PICC tags). It returns a JSON string with the same verdict shape as the hosted /verify. You pass your master key; the core diversifies the per-tag key internally, checks the MAC in constant time, and applies your replay policy against lastCtr.
Cloudflare Workers#
import init, { verify_mirror_js } from './edgenfc_core.js';
import wasm from './edgenfc_core_bg.wasm';
let ready;
export default {
async fetch(request, env) {
ready ??= init(wasm);
await ready;
const u = new URL(request.url);
const master = env.EDGENFC_MASTER; // your Workers secret — never shipped in the package
const verdict = JSON.parse(verify_mirror_js(
master,
u.searchParams.get('uid'),
u.searchParams.get('ctr'),
u.searchParams.get('mac'),
undefined, // supply your own last-seen counter to enforce replay
));
return Response.json(verdict);
},
};Vercel Edge Functions#
import init, { verify_mirror_js } from './edgenfc_core.js';
import wasm from './edgenfc_core_bg.wasm?module';
export const config = { runtime: 'edge' };
let ready;
export default async function handler(req) {
ready ??= init(wasm);
await ready;
const u = new URL(req.url);
const master = process.env.EDGENFC_MASTER;
const verdict = JSON.parse(verify_mirror_js(
master, u.searchParams.get('uid'), u.searchParams.get('ctr'), u.searchParams.get('mac'), undefined,
));
return Response.json(verdict);
}AWS Lambda (Node)#
import init, { verify_mirror_js } from './edgenfc_core.js';
import { readFileSync } from 'node:fs';
const wasm = readFileSync(new URL('./edgenfc_core_bg.wasm', import.meta.url));
let ready;
export const handler = async (event) => {
ready ??= init(wasm);
await ready;
const q = event.queryStringParameters ?? {};
const master = process.env.EDGENFC_MASTER; // from AWS Secrets Manager, not the package
const verdict = JSON.parse(verify_mirror_js(master, q.uid, q.ctr, q.mac, undefined));
return { statusCode: 200, body: JSON.stringify(verdict) };
};Note
The Vercel and Lambda Wasm import forms depend on your bundler/runtime version; the
verify_mirror_jscall is identical everywhere. Enforcing replay (passing a reallastCtrand persisting the highest counter you have seen per UID) is the DIY host's responsibility — the core reports monotonicity but stores nothing.
Verify it worked#
Confirm parity offline with a published sample tap. Save this as verify.mjs next to the two core files and run node verify.mjs:
import init, { verify_mirror_js } from './edgenfc_core.js';
import { readFileSync } from 'node:fs';
await init(readFileSync(new URL('./edgenfc_core_bg.wasm', import.meta.url)));
// Published sample vector (test key — clearly not a production key):
const master = '000102030405060708090a0b0c0d0e0f';
const genuine = JSON.parse(verify_mirror_js(master, '04112233445566', '000005', 'b86db160fc43a6be', 4));
console.log(genuine);
// → { "authentic": true, "uid": "04112233445566", "read_ctr": 5, "replay": "ok" }
const forged = JSON.parse(verify_mirror_js(master, '04112233445566', '000005', 'ffffffffffffffff', 4));
console.log(forged);
// → { "authentic": false, … }The genuine vector returns authentic: true with read_ctr: 5; flipping the MAC to ffffffffffffffff returns authentic: false. This is the same core the hosted service and the Android app run, so the verdict you just computed on your own machine matches what hosted would return byte for byte. That is the parity guarantee — and your master key never left your control.
Next steps#
- Key management & security — diversification, custody, and rotation.
- Custom web app — wire verification into your own product.
- REST API reference — verdict shapes and management routes.
EdgeNFC