Embed the Collector
The collector is a JavaScript file you serve from your own site. You start it when the page loads, and you wait for it when the user acts. This guide assumes your edge runs at https://octet.example.com. See Deploy the Edge.
1. Install the collector
Every Octet Browser file ships as an asset of a release of the public repository octetproof/octet-browser. No registry account or access token is needed. Pin a version in everything you install. This guide uses v1.2.0.
As a script tag
Download the script and its Subresource Integrity hash from the 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
Serve octet-collector.js from your own origin, and put the contents of octet-collector.js.sri in the integrity attribute:
<script src="/static/octet-collector.js"
integrity="sha384-REPLACE_WITH_THE_CONTENTS_OF_octet-collector.js.sri"
crossorigin="anonymous"></script>
Serving the file yourself keeps the collector first-party and covered by your own script-src. Don't point the src at the GitHub release URL. GitHub serves release assets as downloads, without the headers a browser needs to run a cross-origin script with an integrity check, so the script won't load.
The script defines a global octet object with the functions octet.start(), octet.ready() and octet.verify().
With npm or a bundler
Install the package straight from the release:
npm install https://github.com/octetproof/octet-browser/releases/download/v1.2.0/octetproof-collector-1.2.0.tgz
npm records the URL in your package.json, so every install gets the same version. Then import the functions:
import { start, ready } from '@octetproof/collector';
The package includes TypeScript types.
2. Create a sessionRef on your backend
Each page view needs its own sessionRef. Your backend fetches the verdict with it, so it must be unguessable and must never be reused. Generate at least 128 random bits and encode them URL-safe:
// Node.js
import { randomBytes } from 'node:crypto';
const sessionRef = randomBytes(32).toString('base64url');
Store the sessionRef in the user's server-side session, and render it into the page. The rules for session references, and why they matter, are in The session reference.
3. Start at page load, wait at the moment of action
Call start() as soon as the page loads, so the result is ready by the time the user acts. Call ready() when the user acts:
<script>
octet.start({
apiUrl: 'https://octet.example.com',
sessionRef: 'SESSION_REF_FROM_YOUR_BACKEND',
});
document.querySelector('#withdraw-form').addEventListener('submit', async (event) => {
event.preventDefault();
try {
await octet.ready();
} catch (err) {
// Collection failed. Submit anyway, and your backend will get a 404 when it fetches the verdict.
console.warn('octet', err);
}
event.target.submit();
});
</script>
ready() resolves once your edge has passed the session to Octet, which means your backend can now fetch the verdict. Fetch it within 30 seconds. See Fetch the Verdict.
If ready() is called more than 90 seconds after the last collection finished, it collects again before it resolves. If a run fails, the next ready() call runs again. To start over with a new sessionRef, call start() again.
ready() resolves with { response, issuedAtMs, expiresAtMs, elapsedMs }. response is { "ok": true }. It never contains the verdict. See Collector API.
4. Choose a mode
The mode option sets how much network measurement the collector does. The default is full.
| Mode | What it does | Network requests beyond your edge | Typical time to ready() |
|---|---|---|---|
full |
Everything, for the strongest results | HTTPS and UDP 3478 to three Octet network hosts | Well under a second, up to about 1.5 s on long-distance or tunnelled connections |
lite |
Skips the HTTPS timing requests | UDP 3478 to three Octet network hosts | Well under a second |
passive |
No network measurement and no WebRTC | None | Fastest |
In every mode the collector sends one POST to your edge. In full and lite it also opens a WebSocket to your edge. No mode contacts a host that Octet or you don't run.
lite and passive give weaker results. They give Octet less evidence that a connection is masked, so fewer masked sessions reach medium or high.
octet.start({ apiUrl: 'https://octet.example.com', sessionRef, mode: 'lite' });
Each mode's hosts, ports and CSP entries are in Network and CSP.
5. Rules the collector enforces
apiUrlmust behttps://. On any other scheme,start()throws andverify()rejects at once. The only exception ishttp://localhost,http://127.0.0.1orhttp://[::1], for local development.- The WebSocket URL follows
apiUrl. It iswss://on the same host, at/v1/ws. - Nothing blocks the page. A measurement that fails or is blocked is left out, and the collector sends what it has. Of the collector's requests, only a failed POST to your edge makes
ready()orverify()reject.
Cancel a collection
Pass an AbortSignal to stop a collection, for example when the user leaves the page:
const controller = new AbortController();
octet.start({ apiUrl: 'https://octet.example.com', sessionRef, signal: controller.signal });
// later
controller.abort();
Aborting stops the collection and cancels the POST to your edge if it has started. ready() and verify() then reject with an AbortError. If the abort comes before the POST reaches your edge, no verdict is produced for that run.
One-shot collection
verify() runs one collection and resolves with your edge's reply. Use it where there is no separate moment of action, for example on a sign-in page:
await octet.verify({ apiUrl: 'https://octet.example.com', sessionRef });
verify() takes the same options as start().