Sandbox

The sandbox is a self-contained mirror of the worker-facing internal API and the public certificate check, hosted at https://api.averase.com — a Cloudflare Workers app (Hono + D1) you can hit directly to exercise device payloads, token decision paths, and webhook deliveries without touching the real stack. You can also run your own copy locally.

What it is

The sandbox reimplements the /internal/* endpoints that the engine Worker forwards to the web application — token verification, pre-wipe check-in, asset reports, wipe results, chassis maps — plus the public certificate verification endpoint, backed by its own D1 database instead of the production one. Around that mirror it adds a test harness: endpoints to mint tokens, flip kill switches, inspect what the mirror stored, and manage webhook subscriptions.

It serves five surfaces:

  • /internal/* — the internal-API mirror, with optional HMAC enforcement. The endpoint contracts are documented on the Internal API page; sandbox-specific behavior is covered under Endpoints.
  • /sandbox/* — the harness: mint and edit tokens, list stored assets, wipe results, and certificates, manage webhooks, and proxy read-only requests to the real API.
  • /certificates/:id/verify — the public certificate check, exactly as a customer of your customer would call it.
  • /webhooks — an HTML live viewer for webhook subscriptions, deliveries, and sink captures (see Webhook testing).
  • /live — an HTML console for the real web application API: /api/v1/me, accounts, assets with since/serial/page filters, and a raw GET runner.

A Swagger UI is served at https://api.averase.com/ (titled "AVERASE Sandbox API"), so every endpoint below is also browsable and executable from the browser.

What it's for

Use the sandbox to develop and test anything that consumes the device-facing API or its webhooks:

  • Replay realistic device payloads — boot verification, hardware fingerprints, wipe results — and see exactly what gets stored and what fires.
  • Walk every decision path: revoked and suspended tokens, lapsed accounts, exhausted quotas, cross-tenant probes.
  • Build and debug a webhook receiver against real signed deliveries, without deploying anything.

Running your own copy

The hosted sandbox at https://api.averase.com needs no setup — but its database is shared, so for isolated state (or offline work) you can run the same Worker locally. Install dependencies and start the dev server:

SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install
npm run dev    # applies local D1 migrations, then starts wrangler dev on http://localhost:8787
npm test       # run the test suite

A local copy serves the same surfaces on http://localhost:8787, storing everything in a local D1 database — license tokens, assets, wipe results, certificates, chassis maps, webhook subscriptions, deliveries, and sink captures — created by the migrations the dev script applies. Nothing leaves your machine unless you point a webhook at a real URL or use the /live proxy.

Optional environment

Two variables, both optional, go in .dev.vars:

  • Name
    INTERNAL_API_SECRET
    Type
    string
    Description

    Enables HMAC enforcement on /internal/*, matching the real trust boundary: requests must carry X-Averase-Timestamp and X-Averase-Signature: sha256=<hex>, where the signature is a hex HMAC-SHA256 over "{timestamp}.{body}", and the timestamp must be within 300 seconds of the server clock. When unset, the check is skipped entirely — deliberately, so the Swagger UI can call the endpoints unsigned.

  • Name
    WEBAPP_BASE_URL
    Type
    string
    Description

    Base URL of a real web application instance, used for API-key validation and the /live proxy. Defaults to http://localhost:3000.

Binding to a real account

The /sandbox/* endpoints accept an optional Authorization: Bearer <ApiToken> header carrying an API key minted at your AVERASE host's /api_tokens page (see Authentication). The sandbox validates the key live against the real web application — GET {WEBAPP_BASE_URL}/api/v1/accounts and /api/v1/me — and caches the result for 60 seconds.

  • With a valid key, tokens and webhooks you mint default their account_id, account_name, and user_email to your real account, so payloads look like your production traffic.
  • With no header, you're in anonymous sandbox mode: everything still works, you just supply account_id and friends yourself.
  • With a bad key, you get 401 {"error":"invalid API key (webapp rejected it)"}.

In the Swagger UI the key is the webappApiKey security scheme. The /webhooks and /live HTML pages share the same key via browser localStorage, so entering it once covers all three.

End-to-end walkthrough

The whole device lifecycle, from a fresh token to a verifiable certificate, in six requests against https://api.averase.com. To watch webhooks fire along the way, create a subscription first — deliveries land in the built-in sink.

1. Mint a device token

In the real system a token is minted when a customer downloads an ISO. Here you just ask for one:

Request

curl https://api.averase.com/sandbox/tokens \
  -H "Content-Type: application/json" \
  -d '{
    "account_name": "Test Co",
    "account_id": "acct_demo",
    "user_email": "ops@testco.example",
    "product": "engine",
    "quota_limit": 5
  }'

Response

{
  "prefix_id": "ltok_01J4QY7R5N8T2VW9XA3BC4DEFG",
  "jti": "7f3d2a1c-9b4e-4c6d-8a5f-1e2b3c4d5e6f",
  "account_id": "acct_demo",
  "account_name": "Test Co",
  "account_active": true,
  "user_email": "ops@testco.example",
  "product": "engine",
  "status": "active",
  "quota_limit": 5,
  "use_count": 0,
  "last_seen_at": null,
  "created_at": "2026-08-12T16:58:44Z"
}

Save the jti — in the sandbox it is the credential, standing in for the signed device token a real ISO carries.

2. Verify (boot)

What the kiosk does when it comes online. Verification is unmetered — booting isn't wiping — and on allow it only bumps the token's last_seen_at heartbeat:

Request

curl https://api.averase.com/internal/tokens/verify \
  -H "Content-Type: application/json" \
  -d '{
    "jti": "7f3d2a1c-9b4e-4c6d-8a5f-1e2b3c4d5e6f",
    "account_id": "acct_demo"
  }'

Response

{
  "allow": true,
  "status": "active",
  "account": "Test Co",
  "quota_remaining": 5
}

3. Report the hardware

The once-per-boot fingerprint. The sandbox derives the machine's hardware_uid from system.uuid and system.serial and upserts one asset per machine per account:

Request

curl https://api.averase.com/internal/assets \
  -H "Content-Type: application/json" \
  -d '{
    "jti": "7f3d2a1c-9b4e-4c6d-8a5f-1e2b3c4d5e6f",
    "record_id": "CW-20260812-000117",
    "fingerprint": {
      "system": {
        "vendor": "Dell Inc.",
        "model": "Latitude 5540",
        "uuid": "4c4c4544-0034-5a10-8052-b9c04f4b5933",
        "serial": "74HRKY3"
      },
      "cpu": { "model": "13th Gen Intel(R) Core(TM) i5-1345U" },
      "storage": [
        { "model": "SAMSUNG MZVL2512HDJD-00BL2", "serial": "S64FNE2R504071" }
      ]
    }
  }'

Response

{
  "ok": true,
  "asset_id": "asset_01J4QZ8T2M9V6XW3YB5NC7DEFG"
}

You can follow up with POST /internal/assets/notes (operator notes) and POST /internal/assets/tests (hardware test results) against the same record_id.

4. Validate (pre-wipe check-in)

The metered call. It atomically increments the token's use_count, returns the quota after the increment, and issues the job_id the wipe result must carry:

Request

curl https://api.averase.com/internal/tokens/validate \
  -H "Content-Type: application/json" \
  -d '{
    "jti": "7f3d2a1c-9b4e-4c6d-8a5f-1e2b3c4d5e6f",
    "account_id": "acct_demo"
  }'

Response

{
  "allow": true,
  "status": "active",
  "quota_remaining": 4,
  "job_id": "0e6f4a8c-2d1b-4e5f-9a7c-8b6d5e4f3a2b"
}

Note the asymmetry: verify returns account, validate returns job_id — never both.

5. Post the wipe result

One request per drive. If the result is certifiable — wipe_status completed (or absent) and validation approved (or absent) — a certificate is generated on the spot:

Request

curl https://api.averase.com/internal/wipe_results \
  -H "Content-Type: application/json" \
  -d '{
    "jti": "7f3d2a1c-9b4e-4c6d-8a5f-1e2b3c4d5e6f",
    "job_id": "0e6f4a8c-2d1b-4e5f-9a7c-8b6d5e4f3a2b",
    "drive_serial": "S64FNE2R504071",
    "drive_model": "SAMSUNG MZVL2512HDJD-00BL2",
    "drive_capacity_gb": 512,
    "wipe_method": "NIST 800-88 R2 Purge",
    "technique": "NVMe Sanitize — crypto erase",
    "passes_completed": null,
    "wipe_status": "completed",
    "validation": "approved",
    "disposition": "sanitized",
    "markings_removed": true,
    "tool_components": "nvme-cli 2.4",
    "started_at": "2026-08-12T17:03:01Z",
    "completed_at": "2026-08-12T17:40:58Z",
    "verification_result": "Pass — 32/32 fingerprint sectors overwritten",
    "verification_hash": "1f7c9d0a4b2e8f6c3a5d7e9b1c4f8a2d6e0b3c7f9a1d5e8b2c6f0a4d7e9b1c4f"
  }'

Response

{
  "ok": true,
  "wipe_result_id": "wr_01J4R2M8K3P7QS5TV9WX6YZABC",
  "certificate_status": "complete"
}

Wipe results are idempotent on job_id: re-POST the same body and you get 200 (not 201) with the same wipe_result_id, and no webhooks are re-fired.

6. Verify the certificate

List what was issued, then hit the public check with the certificate's id:

Request

curl https://api.averase.com/sandbox/certificates
curl https://api.averase.com/certificates/cert_01J4R2N9X0WQ4T8V2Y6Z1ABCDE/verify

Response

{
  "valid": true,
  "certificate_number": "CW-000001",
  "account": "Test Co",
  "drive_serial": "S64FNE2R504071",
  "drive_model": "SAMSUNG MZVL2512HDJD-00BL2",
  "drive_capacity_gb": 512,
  "wipe_method": "NIST 800-88 R2 Purge",
  "passes_completed": null,
  "completed_at": "2026-08-12T17:40:58Z",
  "signed_at": "2026-08-12T17:41:08Z"
}

References are CW- plus a zero-padded, account-scoped sequence, so your first certificate is always CW-000001.

To see what all of this stored and fired, use the inspection endpoints — GET /sandbox/assets, GET /sandbox/wipe_results, GET /sandbox/webhook_deliveries, GET /sandbox/sink — or open the live viewer at /webhooks. To walk the deny paths (revoked, suspended, lapsed account, exhausted quota, cross-tenant), see Endpoints.

Differences from the real system

The sandbox is honest about being a stand-in. Where it diverges from production:

  1. Tokens are plain database rows minted via POST /sandbox/tokens — no Ed25519 JWS, no /averase.key slot; the jti is the credential.
  2. Internal-API HMAC is implemented but off by default — set INTERNAL_API_SECRET to enforce it.
  3. Certificates are generated synchronously — no job queue, no PDF rendering, no object-storage upload.
  4. Account state is a boolean account_active flag; the real system derives it from the Stripe subscription.
  5. Webhook delivery is one immediate attempt with no retry/backoff queue (the failure budget of 5 is kept, but it counts single attempts).
  6. pdf_url and certificate_url are always null.
  7. The operator field carries the token's user_email; the web application sends the user's name.
  8. certificate_generated fires inline with the wipe result; the web application fires it only after the PDF upload job finishes.
  9. Drive-to-machine matching scans stored fingerprints; the web application uses a promoted storage-serials column.
  10. Webhook URLs may be http:// or sandbox://sink; production accepts HTTPS only.
  11. Token minting and status flips are API calls; in the real system tokens are minted by ISO downloads and controlled from the ISO control column on the Downloads page (plus a staff-only kill switch).
  12. The /sandbox/* listing endpoints are not account-scoped — they return every row in the local database.

Was this page helpful?