Under the hood

You don't have to trust us. That's the whole point.

Every camera company promises privacy. SecuraCV is built so the promise is math you can check yourself — with the same math the device uses, right here in your browser. Nothing leaves this page. New word? The glossary defines each one in a sentence.

Three ideas do all the work

Start with the kernel
01what the kernel does

A normal camera records everything. This one is built to refuse.

At the center of SecuraCV is the witness kernel — the software you install at home, and the gatekeeper every detection passes through before anything is stored or sent. It isn't a camera app that tries to be private; the invasive features are left out, not switched off.

In plain terms: think of the kernel as a one-way mail slot bolted onto a locked room. The camera and its raw video live inside the room. The kernel is the only slot out — and it's shaped so only a short, hand-written sentence can fit through it (“someone is at the front door”), never the picture itself. There's no bigger slot, because a bigger slot was never built.

The path a moment takes through the kernel

watch it

The raw frame is marked private — it physically stays in the room. Only a signed, coarse claim is allowed to leave.

private
Camera
sees the real scene
private
RawFrame
actual pixels — never leaves
InferenceView
a look with no pixels in it
Module
decides: is this worth a claim?
presence/detected { occupants: 1, range_m: 2.4 }
↑ a sentence, signed & hash-chained — the only thing that leaves the room

Why that's different — and useful to you: on an ordinary camera, “don't record faces” is a setting, and settings can be flipped — by an update, an admin, a breach, or a subpoena. Here, the face-recognition code was never written, and sealed video leaves only when a quorum of trustees approves. There is no switch to flip.

02privacy by absence

Privacy by absence, not by setting

What the kernel emits

  • Motion & object events — "Package detected at front door" (the default build detects motion only; object classes come from Frigate or the opt-in ONNX backend)

What has no code at all

  • Face, plate & gait recognition — No code for this exists
  • Cross-camera tracking — No code for this exists
  • Solo video export — Requires multiple trustees to approve
03design

Three intentional constraints

Events only, not video

The output is a log of what happened. There's no API to stream or export continuous video.

Rules apply forward only

A new detection rule can't be run over past records — no retroactive surveillance.

Multi-party video access

Raw footage requires approval from multiple trustees; one approval alone doesn't release it.

04how a signature works

The one sentence that leaves is signed. Try to fake it.

When a Canary lets a claim out, it stamps it with a digital signature. Below is a real one, made in your browser this second with the same method (Ed25519) the device uses. Watch it verify, then rewrite the record and watch the stamp catch you.

In plain terms: a signature is a wax seal that only one secret ring can press, but that anyone can recognize. The device keeps the ring (its private key) on its own chip and never shares it. It hands out only a picture of the seal (its public key), so anyone can check its stamps and no one else can press one. Not even us.

A live claim, signed on this page

real Ed25519

This device's public key fingerprint — the thing you'd check a stamp against:

public key
generating…
signature
—

The claim the witness is making. Drag to rewrite what it says — as an attacker would:

1
2.4
checking…

public key · 32 bytes anyone can hold this
signature · 64 bytes the stamp over this exact record

These are the real bytes generated on this page — not a picture of bytes. Forge the record above and the signature can't follow it.

the exact code running on this pageJavaScript · in your browser
// build the same message the device signs, then sign + verify it
function canonical(c, hash) {
  return "securacv-canary-sig|v1|presence|" + id +
         "|occupants=" + c.occupants +
         "|range_m=" + c.range_m + "|" + hash;
}
sig = await crypto.subtle.sign("Ed25519", privKey, msg);
ok  = await crypto.subtle.verify("Ed25519", pubKey, sig, msg);
what the device firmware does — same messageC++ · on the Canary
// firmware/projects/canary-display/src/trust.cpp
// rebuild the locked canonical, then verify the stamp
snprintf(canonical, sizeof(canonical),
         "%s|v%d|chain|%s|%lu|%s",
         SIG_PREFIX, SCHEMA_V, device_id, length, latest_hash_hex);

const bool ok = Ed25519::verify(sig, pubkey,
                    (const uint8_t*)canonical, n);

Same handshake, both sides. The browser and the chip build the byte-for-byte identical message (securacv-canary-sig|v1|…) before they sign or check it — so anyone can re-check a real Canary's claim without us.

For makers: what “Ed25519” actually is — and the one-character avalanche

Ed25519 is a specific, well-tested recipe for that seal. Four facts make it trustworthy.

stays on the device
Private key
one big secret number the device picks at random
easy ✗ can't reverse
shared with the world
Public key
computed from the secret — but you can't work backwards to it
to sign:  message + 🔒 private → signature
to check:  message + signature + 🌐 public → ✓ or ✗
0 bytes
the public key — small enough to fit anywhere
0 bytes
every signature — tiny, so it rides along with each claim
  1. 1It's a one-way street. Going from the secret private key to the public key is easy; going back is, as far as anyone knows, out of reach. That gap is the entire foundation.
  2. 2A signature is welded to the exact message. It's not a sticker — it's a number computed from the secret and every byte of the claim. Flip one character and the only valid signature changes completely. (That's what you just did to break it.)
  3. 3Checking needs only the public half. Anyone with the public key can confirm a signature could only have come from the matching private key, over that exact message — without ever learning the secret. Anyone can check it; producing one takes the private key.
  4. 4Why this recipe. Small keys and signatures, quick on a cheap chip, and deterministic — it never gambles on fresh randomness when it signs, a mistake that has leaked real keys elsewhere. It's the same standard behind SSH logins and Signal.

The one-character avalanche — why there's no “almost right”

Before it's signed, every claim is boiled down to a 32-byte fingerprint (a SHA-256 hash). Edit the record below by a single character and watch its fingerprint — a real hash, recomputed as you type. The blue tiles are the bytes that changed.

its 32-byte fingerprint unchanged

Why that's different — and useful to you: the secret that makes the stamp never leaves the device. We can't sign a fake “nobody was home” in your camera's name, and neither can a hacker who breaks into a server we don't have. You can trust the record without trusting us.

05how the record stays honest

Every record remembers the one before it. Break one, break the rest.

One signed claim is good; a history you can't secretly edit is better. The kernel writes each event into a hash chain: every new record carries a fingerprint of the one before it. Change any past record, even by one character, and every record after it stops adding up. Try it.

In plain terms: imagine a ledger where each new page begins by copying a summary of the previous page. Tear out or edit page 3, and pages 4, 5 and 6 no longer match the summaries they promised to carry. You can't quietly fix the past — the math tattles.

A real hash chain — edit any record

real SHA-256

Each fingerprint below is a genuine SHA-256, computed in your browser as you type. Change a payload and watch the breakage cascade downstream.

the chain fold — one functionRust · the kernel (src/log/mod.rs)
/// Hashes a log entry with the previous chain hash.
pub fn hash_entry(prev_hash: &[u8; 32], payload: &[u8]) -> [u8; 32] {
    let mut hasher = Sha256::new();
    hasher.update(prev_hash);  // ← the fold: last hash…
    hasher.update(payload);    // …mixed with this record
    hasher.finalize().into()
}
writing one event = read head → hash → signRust · the kernel (src/storage.rs)
let prev_hash  = store.last_event_hash_or_checkpoint_head()?;
let entry_hash = hash_entry(&prev_hash, payload_json.as_bytes());
let signature  = sign_entry(keys, &entry_hash, DOMAIN_SEALED_LOG_ENTRY)?;
// the new record is now both chained AND signed — three lines.

That's the whole trick. Each hash folds in the one before it, so the newest fingerprint vouches for the entire history. The demo runs the same SHA-256(previous + record) the kernel does.

Why that's different — and useful to you: quietly deleting the event where they walked past your door, or backdating one that never happened, breaks the chain — for an intruder, for us, even for you. Anyone can re-run the check and see the chain is whole. That's what “tamper-evident” actually means.

06real vs. pretend, honestly

We emulate a lot. Here's exactly what's real.

The Test bench, the Lab and the Scenes run in a browser — and the cryptography in them is completely real. The radar, the chip's secret key and the sealed storage can't exist in a web page, so those are simulated; the signature checks in them are not.

Real, byte-for-byte

  • The Ed25519 signing & verifying you did above
  • The SHA-256 hash chain you just broke
  • The exact securacv-canary-sig|v1|… message format
  • The actual device firmware — compiled to run in the browser (WebAssembly)

Simulated (can't exist in a browser)

  • The 60 GHz radar and camera sensors
  • The device's real private key, stored on its chip
  • Power, the network, and other Canaries nearby
  • The encrypted vault where sealed footage would rest

The emulator's own README says it plainly: it “talks to the firmware only through the same boundaries silicon would.” Where your browser can do the real math, it does — so a “verified ✓” in a demo is a real signature check, not decoration.

07don't take our word for it

Three ways to check, from “click a button” to “audit the source.”

Trust should be optional. Pick how deep you want to dig.

Level 1 · anyone

Play with it here

You already did: you tried to forge a signature and broke a hash chain, with real cryptography.

Level 2 · the curious

Read the actual code

Every snippet on this page is quoted from the open-source repo. Read the real thing — the device signer, the kernel's chain, the browser demos.

Level 3 · the thorough

Run the verifier yourself

Point the open-source verifier at a real device's log. If a single event was edited or reordered, it fails — loudly. No trust in us required.

Not “trust our privacy policy.” Trust the math.

A promise can change the day after you buy the camera. A signed, chained record can't change without showing it — and you can check it whenever you like.