Quickstart
This quickstart takes you from an approved license to a verdict on your backend. It uses a small Node.js server with no dependencies, so you can see every step working before you change your own app. Each step links to the guide with the full detail.
What you need
- Your license token, emailed by Octet when your application was approved. To apply, see Credentials.
- A read token, minted in the Octet portal's Read tokens tab.
- A Linux host for the edge, with a DNS name such as
octet.example.com. - Node.js 18 or later on the machine that runs the example server.
1. Run the edge
Follow Deploy the Edge on your Linux host. For this quickstart, set ALLOWED_ORIGIN to include the example server's origin:
ALLOWED_ORIGIN=https://www.example.com,http://localhost:3000
Check that the edge is up and that Octet accepts your license:
curl -s https://octet.example.com/health
curl -s -X POST https://octet.example.com/v1/signals -H 'content-type: application/json' -d '{}'
The first returns {"ok":true,"role":"octet-edge"}. The second returns "reason":"missing_fields", which means your license was accepted.
2. Get the collector
In a new directory, download octet-collector.js and octet-collector.js.sri from the v1.2.0 release:
curl -fLO https://github.com/octetproof/octet-browser/releases/download/v1.2.0/octet-collector.js
curl -fLO https://github.com/octetproof/octet-browser/releases/download/v1.2.0/octet-collector.js.sri
3. Create the example server
Save this as server.mjs in the same directory:
// server.mjs: a minimal Octet Browser integration. Node.js 18 or later.
import { createServer } from 'node:http';
import { randomBytes } from 'node:crypto';
import { readFileSync } from 'node:fs';
const EDGE_URL = process.env.OCTET_EDGE_URL; // for example https://octet.example.com
const READ_TOKEN = process.env.OCTET_READ_TOKEN;
const collector = readFileSync('octet-collector.js');
const sri = readFileSync('octet-collector.js.sri', 'utf8').trim();
const sessionRefs = new Map(); // browser session id -> sessionRef. Use your own session store.
const page = (sessionRef) => `<!doctype html>
<script src="/octet-collector.js" integrity="${sri}" crossorigin="anonymous"></script>
<form id="withdraw" method="post" action="/withdraw"><button>Withdraw</button></form>
<script>
octet.start({ apiUrl: ${JSON.stringify(EDGE_URL)}, sessionRef: ${JSON.stringify(sessionRef)} });
document.getElementById('withdraw').addEventListener('submit', async (event) => {
event.preventDefault();
try { await octet.ready(); } catch (err) { console.warn('octet', err); }
event.target.submit();
});
</script>`;
async function fetchVerdict(sessionRef) {
const url = `https://geo.octetproof.com/v1/verdict/${encodeURIComponent(sessionRef)}?waitMs=5000`;
const res = await fetch(url, {
headers: { Authorization: `Bearer ${READ_TOKEN}` },
signal: AbortSignal.timeout(8000),
});
if (res.status === 404) return null;
if (!res.ok) throw new Error(`verdict fetch failed: ${res.status} ${await res.text()}`);
return res.json();
}
createServer(async (req, res) => {
if (req.method === 'GET' && req.url === '/octet-collector.js') {
res.writeHead(200, { 'content-type': 'text/javascript' });
return res.end(collector);
}
if (req.method === 'GET' && req.url === '/') {
const sid = randomBytes(16).toString('hex');
const sessionRef = randomBytes(32).toString('base64url');
sessionRefs.set(sid, sessionRef);
res.writeHead(200, {
'content-type': 'text/html; charset=utf-8',
'set-cookie': `sid=${sid}; HttpOnly; SameSite=Lax; Path=/`,
});
return res.end(page(sessionRef));
}
if (req.method === 'POST' && req.url === '/withdraw') {
const sid = /(?:^|;\s*)sid=([0-9a-f]+)/.exec(req.headers.cookie ?? '')?.[1];
const sessionRef = sessionRefs.get(sid);
if (!sessionRef) {
res.writeHead(400);
return res.end('no session');
}
const verdict = await fetchVerdict(sessionRef);
const masked = verdict?.alarm === 'medium' || verdict?.alarm === 'high';
const decision = !verdict || masked ? 'require KYC' : 'allow';
res.writeHead(200, { 'content-type': 'text/plain; charset=utf-8' });
return res.end(`${decision}\n\n${JSON.stringify(verdict, null, 2)}\n`);
}
res.writeHead(404);
res.end();
}).listen(3000, () => console.log('http://localhost:3000'));
The server gives each page view a new sessionRef, keeps it server-side against a cookie, and fetches the verdict when the form is submitted. Its policy requires KYC when there is no verdict or when alarm is medium or above.
4. Run it
OCTET_EDGE_URL=https://octet.example.com OCTET_READ_TOKEN=octet_read_REPLACE_WITH_YOUR_READ_TOKEN node server.mjs
Open http://localhost:3000 and select Withdraw. The page shows the decision and the verdict:
allow
{
"country": "GB",
"confidence": 0.9,
"alarm": "none",
"token": "eyJhbGciOiJFZERTQSIs..."
}
Next
- Move each piece into your app: Embed the Collector and Fetch the Verdict.
- Decide your policy: Verdicts.
- Verify and store the
token: Verify the Signed Token. - Before launch, remove
http://localhost:3000fromALLOWED_ORIGINand work through the Go-Live Checklist.