Blog · Guide
Passive vs active vs continuous liveness — an engineer's guide
The three shapes of liveness detection, the attacks each one actually stops, the friction each one costs, and the injection problem that none of them solves on its own.
Start from the threat model, not the product names
“Liveness detection” is one name for three different products, and vendors rarely tell you which one they’re selling. The honest way to pick between them is to start from what you’re defending against.
A presentation attack is anything shown to a real camera: a printed photo, a face on a phone screen, a replayed video, a mask. A replay attack is the presentation attack that scales — one leaked selfie video, used everywhere. An injection attack skips the camera entirely: a virtual camera device or tampered client feeds pre-recorded or synthetic frames straight into the capture path. Deepfakes are a payload, not a category — they arrive by either route.
Passive, active and continuous liveness are three answers to these threats with three very different friction bills. Here is what each one actually does, using our own API as the concrete example — the endpoints are documented, so every claim here is checkable.
Passive: one frame, zero ceremony
Passive liveness takes a single frame and classifies it — live face or artifact — from texture, spectral and quality signals. No blinking, no head turns; the user never knows the check happened. Ours runs MiniFASNet-family anti-spoof models plus spectral screen-replay forensics, and the signals (high_freq_ratio, spectral_peak) come back in the response instead of being swallowed into a bare verdict.
POST /v1/face/face-match
{
"selfie": "<base64>",
"card": "<base64>"
}
# -> match score + liveness verdict + spoof/mask/quality
# signals in one signed responseTwo things to know. First, the friction win is real: this is the only liveness shape with literally zero user ceremony, which is why it belongs on every high-volume onboarding path. Second, the shape of the deployment matters as much as the model: ours runs over bare REST with no mandatory capture SDK — send a frame from any client you like — which globally is a rare way to sell passive PAD, and in India is undocumented among the incumbent vendors we surveyed.
The limits are just as concrete. A single frame carries no temporal information, so passive PAD is probabilistic by nature, and it is judged on two numbers that must be quoted together: APCER (attacks accepted) and BPCER (real users rejected). Any vendor quoting one without the other is hiding the trade — we keep both defined, with our evaluation methodology, on the benchmarks page. And a passive check inherently cannot prove the frame came from a camera at all — injection is out of scope by construction.
Active: challenges a recording can't answer
Active liveness makes the user do something unpredictable. The server opens a session, issues randomized challenges — turn your head, blink — and verifies the response frames. Because the challenge is generated per session and is single-use, a pre-recorded video of the victim cannot answer it; the recording doesn’t know what will be asked.
POST /v1/session/start
# -> { session_id, challenges: ["turn_left", "blink"] }
# randomized per session, single-use
POST /v1/session/{id}/verify
# submit the response frames
# -> per-challenge results + liveness verdict, signedThe design detail that carries the security is randomized, one-time challenge state on the server. Fixed challenge sets — every session asks for the same two gestures — can be pre-recorded once and replayed forever, and gesture liveness with a fixed script is exactly what a chunk of the market ships. Randomization plus single-use session state is what actually buys the replay resistance.
The cost is friction and completion rate: every instruction you add loses real users, and blink detection on low-end front cameras has a genuine BPCER cost. Use active liveness as a step-up — triggered by a low passive score, a risky device signal or a high-value action — rather than as the default gate for every user.
Continuous: liveness as a property of a stream
Continuous liveness scores an entire live video session rather than a moment. Frames stream in over a WebSocket and a temporal model holds four properties for the whole call: sustained frame-level liveness, physically real motion, identity continuity (the same face, within face-match threshold, from first frame to last) and in-stream replay detection — with a randomized challenge script woven into the same stream.
POST /v1/stream/start # open session + challenge script
GET /v1/stream/{id} # WebSocket frame ingest
# temporal liveness + identity continuity + replay
# signals scored across the whole sessionThis is the shape India’s video-KYC process actually calls for, and it’s different in kind from running a passive check on sampled frames — the failure modes of that shortcut are the subject of our V-CIP post. The cost side is equally plain: continuous is the most expensive shape per check (a stream session is our priciest call at 5 credits, versus 1–2 for the others), needs a streaming client, and is overkill for a simple selfie gate. It earns its cost where a session, not a snapshot, is the thing being trusted.
Choosing: the table
| Passive | Active | Continuous | |
|---|---|---|---|
| Input | one frame | challenge session | live stream (WS) |
| User friction | none | follows instructions | on a video call anyway |
| Stops prints / screens | yes (probabilistic) | yes | yes |
| Stops pre-recorded replay | partially (forensics) | yes — random one-time challenge | yes — temporal + challenge |
| Stops mid-session face swap | n/a | no | yes — identity continuity |
| Stops injection alone | no | no | helps, not sufficient |
| Where it fits | every onboarding | step-up on risk | video KYC / V-CIP |
“Stops” means raises the attack’s cost above the casual tier — nothing in this table is absolute, and anyone selling absolutes is selling.
The uncomfortable part: injection cuts across all three
A sufficiently good injected stream — a virtual camera feeding a real-time deepfake — can present frames that are individually live-looking, answer challenges, and maintain one identity. Liveness classification alone, of any shape, is not the answer to that; capture integrity is. It’s a separate layer that asks not “does this look live?” but “did these bytes actually come from a camera, now?”
Ours stacks several signals: exact-replay hashing (the same submission bytes seen before), EXIF and capture-metadata forensics (editor fingerprints, missing camera metadata), C2PA Content-Credentials verification when a manifest is present, Ed25519-signed capture payloads from clients that support it, and hardware attestation — offline App Attest verification and Play Integrity — shipped in v0.2.9. None of these is individually decisive either; the point is that an attacker has to beat all of them and the liveness model in the same submission.
Honesty note
No injection defence available today — ours included — is externally certified. The relevant test standard (CEN/TS 18099) is on our roadmap and our current status is stated plainly on the trust page. If a vendor tells you injection is a solved problem, ask which lab said so.
The practical recipe, then: passive everywhere, active as step-up, continuous when the session is the product — and capture-integrity signals underneath all three. Terms from this post that deserve precise definitions live in the glossary.