feat: in-house IC-native personhood verification (#9072) - #9081
feat: in-house IC-native personhood verification (#9072)#9081julianjelfs wants to merge 53 commits into
Conversation
Phase 0 hard gate for #9072: measures wasm instruction counts for each on-chain pipeline stage (JPEG decode, face detect, landmarks, embedding, uniqueness scan) under tract-onnx before any real build. Models are downloaded (hash-pinned), not committed; tract crates get opt-level 3 since "z" penalizes compute-bound inference. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 0 of #9072: session/challenge/status types in openchat-shared, worker plumbing, and a scripted MockVerifierClient (used while no verifier canister is configured) so the capture UX can be built and device-tested with zero backend dependency. OC_VERIFIER_CANISTER is wired through config but nothing selects a real client yet. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Largest single inference 5.5B instructions with SIMD (7x headroom under the 40B DTS ceiling); a full 8-frame verification is ~30B including decode and a 1M-user scan. getrandom needs a registered custom impl on wasm32 since tract pulls rand. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Shared headless state machine (consent -> camera -> pose challenge -> upload -> processing -> result) drives new VerificationModal components in the desktop and mobile trees, replacing the dormant DecideAI VerifyHumanity modals behind the existing verify_humanity events. - @mediapipe/tasks-vision (UX only: framing/pose guidance/auto-capture); assets self-hosted under /assets/verification, populated at build time by copy-verification-assets.sh, lazy-loaded when the flow opens - detector load failure degrades to manual capture; canister re-verifies everything so security never depends on the client - session starts only after camera + detector succeed, protecting the rate-limited attempts - pose thresholds tunable via debug overlay (localStorage openchat_verification_debug=true) Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Without the check, a component unmounting mid-capture let uploads, step advancement and polling continue after destroy(). Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 1 of #9072, end-to-end with a deterministic stub engine: - new personhood_verifier canister: session machine with pose challenges, chunked frame upload (heap-only, frames structurally excluded from upgrade serialization), processing queue + timer job, embedding store with brute-force cosine scan, three-band threshold policy with one stricter retry round - test-mode engine fabricates embeddings from a marker byte so duplicate/gray-zone/retry paths are testable without real ML - UniquePersonProof gains OpenChat provider + model_version (additive) - user_index c2c_notify_personhood_verified (guarded) records the proof and reuses the existing NotifyUniquePersonProof fan-out untouched - personhood_verifier_canister_id threaded openchat_installer -> user_index with serde defaults for upgrade safety - registration: workspace, dfx.json, deploy/upgrade scripts, typebox script, integration test env + PocketIC coverage (happy path, duplicate, gray-zone retry, rate limit) Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Plain xorshift from nearby seeds produced correlated vectors, making unrelated markers falsely similar; use splitmix64 per element (unit test pins the similarity bands). The PocketIC env is shared across tests so each test gets its own marker group. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Local deployments are now testable from the UI end-to-end: the agent uses the real client when OC_VERIFIER_CANISTER is set (sourced from canister_ids.json for local dev) and falls back to the mock otherwise. - typebox additions are appended to the committed typebox.ts rather than fully regenerated: a full regen pulls in unrelated backend type drift (e.g. HistoryDeleted) the frontend hasn't absorbed yet - test-mode stub treats real JPEGs (which all start 0xFF, previously colliding with the ChallengeFailed marker) as unique faces hashed from the first frame, so real camera captures verify locally - domain response types aligned with the canister API (invalid_challenge_index/total_bytes_exceeded/invalid_image, user_not_found/internal_error, not_submitted) Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Re-enables the desktop profile Verification section (uniquePersonGate binding) and the mobile SparkleBox verify prompt so the new capture flow is reachable. Gate bindings and badges stay stripped until the cutover phase. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MediaPipe's face landmarker compiles self-hosted wasm, which script-src blocks without 'wasm-unsafe-eval', forcing the capture flow into manual mode. 'wasm-unsafe-eval' permits only WebAssembly compilation (from sources already allowed by the policy), not JS eval. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Device testing showed every pose instruction only passed when the user did the opposite: MediaPipe's transformation-matrix rotation signs are inverted relative to what the capture machine's thresholds assume, on both axes. Negate yaw/pitch at extraction so "turn left" means the subject's left. The on-chain challenge verification (Phase 2) must use the same convention. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Svelte's bind:this sets the ref to null when the video unmounts (camera view -> processing view), which crashed attachVideo and froze the modal at "Verifying". attachVideo now treats a null/undefined element as a detach: stop the detection loop and clear the reference. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 2 of #9072. The pipeline runs when models are committed and the deterministic stub otherwise (integration tests, fresh local envs): - model weights chunk-uploaded (inert, governance-guarded) and activated by hash-pinned commit_model; committing an embedding model bumps the model version, so stub-era enrollments (version 0) stop matching real ones - inference: JPEG decode -> SCRFD-500M detection + 5 keypoints -> pose-challenge verification from keypoint geometry (same sign convention as the frontend) -> 5-point similarity alignment to the ArcFace template -> w600k_mbf 512-dim embedding; intra-session same-face check, mean + L2 + i8 quantization - SCRFD + w600k_mbf are the insightface buffalo_sc pairing, replacing the 3-model lineup from the design (the 2d106det pose model is redundant given SCRFD's keypoints) - DTS budgeting: one frame per timer execution (~10B instructions with SIMD), finalize + uniqueness scan separately; frames dropped the moment their frame is processed - fails closed if models are committed but engines cannot build - never silently degrades to the stub - wasm builds with +simd128 (generate-wasm.sh special-cases the verifier); tract requires a registered getrandom backend on wasm32 - verification_model_uploader tool + upload-verification-models.sh for local/test envs; production commits go via SNS proposals Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Envs upgraded from the stub era already sit at model version 1, so the default commit version is rejected; pass a higher one as the third arg. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Envs whose verifier predates the governance_principals init arg have an empty set after upgrade, locking model uploads out entirely. Controllers can already reinstall the canister, so trusting them here grants nothing extra; in production the only controller is SNS root. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Frontend: the framing oval turns green while the live pose satisfies the current step, and each capture fires a green flash plus a short synthesised two-tone blip (no audio asset; AudioContext created after the consent click). Canister: raise the geometric pose gains - the nose tip projects only ~0.5-0.7 of the eye distance, so the previous gains understated real angles and rejected genuine captures. Per-frame yaw/pitch telemetry is logged in test mode for calibration against real attempts. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tract miscompiles SCRFD's graph: outputs are nondeterministic (garbage scores/coordinates varying across identical runs) and the unoptimized reference plan fails shape inference on Add_109. Detection now uses UltraFace RFB-320 (proven with tract in DFINITY's face-recognition example) with insightface 2d106det supplying landmarks: - SSD prior decode for 320x240 (4420 anchors, variances 0.1/0.2) - UltraFace's coarse prior level double-fires a giant box around the same face which union-IoU NMS keeps; a containment merge drops the larger duplicate while preserving genuine multi-face rejection - 5 alignment points from the 106-landmark set (eye ring centres 38/88, nose 86, mouth corners 52/61) - indices derived empirically and pinned by a native end-to-end test over a real portrait fixture, which also asserts pipeline determinism (the SCRFD failure mode) - 2d106det takes raw 0-255 input; crop is 1.5x the detection box Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Genuine Center frames read pitch +10 to +14 degrees at the previous neutral ratio (webcams sit below eye level), leaving almost no margin under the Center threshold. 0.60 centres the observed distribution; Up poses measured ~+50 so classification margins are unaffected. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Absolute pitch on genuine frontal frames varied +4..+20 degrees across sessions with camera elevation and posture, so absolute thresholds rejected real users. The challenge always opens with a Center step: its measured pose (accepted within a broad plausibility window) now anchors the session baseline and every later step is classified by its delta, cancelling the bias. Observed genuine deltas - Up +45, Left -52, Right +71, closing Center -4 - clear the 12-degree bands with wide margins. Telemetry now logs deltas alongside absolute angles. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 3 of #9072, release-order coupled with the frontend un-strip: - proof-removal fan-out (user_index -> local user indexes -> user canisters): proofs can now be revoked; user canisters report is_unique_person false transitions and keep earned achievements/CHIT - stateless idempotent sweep job wipes all legacy DecideAI-provider proofs and, after an embedding model upgrade, lapses OpenChat proofs of superseded model versions at the announced deadline - verifier announces model upgrades to user_index (90-day window) and purges superseded embeddings when the window closes - DecideAI mint paths removed: submit_proof_of_unique_personhood endpoint deleted, local_user_index no longer accepts unique-person credential JWTs, proof_of_unique_personhood library retired - unique person gate checks enforced for real (previously stubbed to always pass); composite-gate suspension filtering removed - account deletion now erases the user's embedding in the verifier - uniqueness-scan similarity telemetry in test mode for calibration Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 3 of #9072, release-order coupled with the backend cutover: - reverts the sunset display-strips in both component trees: verified badge, unique person gate in the gate builder, prize uniquePersonOnly, gate icons/summaries, Learn-to-Earn achievement, profile sections; doesUserMeetAccessGate checks isUniquePerson for real again - UniqueHumanGateEvaluator rewritten in both trees to run the capture flow inline via a shared VerificationFlow component (extracted from the verification modal); the gate is satisfied by the on-chain verification itself so no credential is produced or submitted - DecideAI path deleted: submitProofOfUniquePersonhood across client/worker/agent, the dormant VerifyHumanity components, the uniquePersonCredentialGate constant, and the DecideAI i18n copy Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- unique person gates genuinely block unverified users and admit them after on-chain verification - account deletion erases the verifier embedding (the same face re-enrolls cleanly instead of failing as a duplicate) - this test caught c2c_delete_user_embedding being a candid endpoint while both callers target the _msgpack method, so deletion silently never landed; the endpoint is now msgpack - unit coverage for the proof sweep: DecideAI wipe and model-version lapse semantics Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The real pipeline shipped with UltraFace RFB-320 + 2d106det, not SCRFD (tract miscompiled SCRFD); update the not-yet-released entry to match. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two SNS-proposal-driven controls that make a safe staged cutover possible (canisters release one proposal at a time, not atomically): - set_personhood_verifier_canister_id: on upgrade the id defaults to anonymous (init args do not re-run), which would leave a live user_index rejecting the verifier's proof notifications and skipping embedding deletion. Governance sets it after the verifier is registered. - wipe_legacy_unique_person_proofs: the DecideAI wipe is now a deliberate one-shot governance action instead of firing on every upgrade, so the rollout controls exactly when legacy badges disappear. The removal job only arms when there is real work (a requested wipe or a due model-version lapse) rather than rescanning on every upgrade. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The face ML pipeline lived inside the personhood_verifier canister (cdylib, so nothing could depend on it). Extracted the pure pipeline - JPEG decode, UltraFace detection, 2d106det landmarks, ArcFace alignment, w600k_mbf embedding, quantized cosine - into a shared face_pipeline library with an owned Engines struct (no thread-local, no IC deps). The canister's engine/real.rs is now a thin wrapper holding the engines as canister state and mapping errors; the challenge-pose classification (baseline deltas) stays canister-side. Behaviour is unchanged - the canister and lib tests (real-portrait end-to-end, determinism, pose signs) pass identically. threshold_calibration is a native tool that runs that exact pipeline over a labelled face dataset (LFW-style pairs) and reports the genuine/impostor similarity distributions + ROC, so the SNS picks the on-chain T_dup/T_clear bands from real false-match/false-non-match numbers. It reports the scale-adjusted collision probability at N=100k/1M - a small per-comparison FMR compounds when every enrolment is scanned against the whole store, which is the number that actually gates enforcement. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One command to fetch LFW and convert its pairs.txt into the threshold_calibration input format. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The umass.edu LFW host frequently fails to resolve; scikit-learn mirrors the dataset on figshare with correct checksums and lands the JPEGs on disk, which the converter then points the calibration tool at. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Homebrew Python is PEP 668 externally-managed, so a system-wide pip install fails. The script now creates a self-contained venv under .calibration and installs scikit-learn there; system Python is untouched. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Calibration on LFW exposed that ~29% of clean faces were rejected - all as "multiple faces", none as "no face". UltraFace fires several confident, overlapping/nested boxes on a single face; the old post-processing (IoU-NMS + a keep-the-tighter containment merge) left them separate, so the multi-face guard rejected the image, and when it did pass it aligned off the tightest sub-box (a too-small crop that also depressed genuine similarity). cluster_faces now collapses all overlapping/nested boxes into one representative per face (highest score wins) and drops boxes lying largely outside the frame (UltraFace's coarse localization artifacts). The multi-face guard fires only on a spatially separate second face. LFW detection recall rose 75% -> 91% (the residual are real bystanders in press photos, correctly rejected). Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Embedding now runs one engine per worker thread (no shared state, no new deps): a full LFW run drops from ~40 min to ~6 min. Added a --rgb-embed/BGR experiment path and used it to settle the channel order: BGR is consistently worse on LFW (genuine median 0.580 vs 0.607), so this w600k_mbf ONNX export takes RGB, as shipped. Refs #9072 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two security findings from review of #9081. Stub-in-prod: process_one_step chose the engine only on models.all_committed(), so during the rollout window (verifier installed, models not yet committed) the deterministic stub granted a genuine UniquePersonProof to every capture - and since each real JPEG hashes to a distinct "face", one person could mint unlimited proofs. start_verification now returns NotReady when !test_mode && !all_committed(), and process_one_step parks rather than ever running the stub outside test_mode. Oversized-frame DoS: decode_jpeg used the 16384x16384 default cap, so a highly-compressible large image within the frame byte limit blew the DTS budget and trapped the timer message; the trap rolled back the TIMER_ID reset, permanently wedging the global queue. decode_jpeg now caps dimensions at 2048 (zune bails at the SOF header, cheaply), and upload_frame rejects oversized frames via a new jpeg_dimensions SOF parser before they enter the queue. Degenerate <2px dims rejected to avoid the sample_bilinear width-1 underflow. Also: cosine_similarity asserts equal length (fail closed vs scoring the overlap into the uniqueness decision); upload_frame byte accounting uses saturating_sub. Refs #9072
Addresses the non-critical findings from review of #9081. commit_model: build the engines (and roll the commit back on failure) before bumping the version, starting the lapse window and notifying user_index, so a build failure can't leave persisted state announcing an upgrade the verifier can't serve. Proof notifications are now durable: a verified user is recorded in a persisted pending set and retried by prune_sessions until user_index acknowledges, so a transiently-unavailable user_index (e.g. an upgrade) can't strand a user as enrolled-but-proofless. Cleared on ack and on account deletion. cosine_similarity asserts equal length; byte accounting uses saturating_sub. Frontend: MockVerifierClient throws on a production build with no verifier configured (was a silent mock); the verification debug overlay is gated on import.meta.env.DEV in both trees. Refs #9072
Resolve conflicts from frontend consolidation (#9063): - adopt @client/@shared import aliases across personhood files - move @mediapipe/tasks-vision dep to consolidated frontend/package.json - port OC_VERIFIER_CANISTER define + copy-verification-assets.sh into restructured rollup.config.mjs - keep VerifyHumanity.svelte deletions (replaced by verification flow) - take master's EditableImageWrapper cropper fix (same fix both sides) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Missed from merge commit - npm install ran after staging. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
First PR CI run surfaced pre-existing issues: - cargo fmt over personhood files (long lines wrapped) - clippy: needless_range_loop + assertions_on_constants in face_pipeline, manual_range_contains + collapsible_if + stale too_many_arguments expect in personhood_verifier, collapsible_if in user_index, unused variable in local_user_index (DecideAI removal leftover), unused mut in threshold_calibration - exclude personhood_spike from CI clippy/test jobs: it include_bytes!s onnx models which are gitignored (fetched via download script) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add upload_model_chunks_whitelist so dev principals can upload (inert) model chunks in production; activation remains proposal-only via the hash-pinned commit_model - Convert commit_model, set_uniqueness_thresholds, set_personhood_verifier_canister_id and wipe_legacy_unique_person_proofs to #[proposal] endpoints, generating the _validate queries required to register them as SNS generic functions - Add proposal scripts (function ids 1016/1017/11000/11001, verified free against the live SNS registry) and a --skip-commit uploader mode for the production upload flow - Document the full ordered rollout in PRODUCTION_ROLLOUT.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- OC_UNIQUE_PERSON_REQUIREMENTS_ENABLED build-time flag: phase A ships the ability to verify while the unique-person gate option and the prize message restriction stay unavailable; phase B is a website rebuild with the flag set - no code change or revert - Decouple the profile Verification section from the gate binding so users can verify in phase A while the gate is not yet offered - Translate the new verification strings into all 14 locales and drop the stale DecideAI strings everywhere - Rework the rollout runbook for the two-phase website deploy Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Right to erasure: a new user_index remove_unique_person_proof endpoint removes the caller's unique person proof (fanned out to the local user indexes and their user canister) and deletes their face embedding and attempt history from the personhood verifier, reusing the account-deletion machinery. The user can re-verify at any time and their face is then treated as brand new - which also makes repeated end-to-end testing possible without hitting duplicate-face rejections. The profile Verification section gains a Remove verification button behind an AreYouSure confirmation that explains exactly what is deleted, with strings in all 15 locales. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Removing (or gaining) verification only updated the in-memory stores, so a page refresh rehydrated the old status from the IndexedDB current-user and user-summary caches until the next server round-trip. Follow the diamond membership pattern: patch both caches when verification is removed or succeeds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR revives “unique personhood verification” as an in-house, on-chain (SNS-governed) system by introducing a new personhood_verifier canister, wiring its proof lifecycle into user_index/user propagation, and re-enabling unique-person UX (verification flow, badge, and gates) with a two-phase rollout flag.
Changes:
- Add the
personhood_verifiercanister (model chunk upload + hash-pinned commits, on-chain verification sessions, uniqueness scan, proof notifications, and embedding erasure paths). - Re-enable unique-person UI/gating and add a new human verification flow (web + mobile), guarded for “requirements” via
OC_UNIQUE_PERSON_REQUIREMENTS_ENABLED. - Remove legacy DecideAI proof submission plumbing and add governance proposal scripts for cutover/operations.
Reviewed changes
Copilot reviewed 243 out of 248 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| scripts/upload-verification-models.sh | Local helper to download and upload/commit verifier models. |
| scripts/upgrade-canister.sh | Wires personhood_verifier canister id into upgrade tooling. |
| scripts/proposals/wipe_legacy_unique_person_proofs.sh | Proposal script to wipe DecideAI proofs during cutover. |
| scripts/proposals/set_uniqueness_thresholds.sh | Proposal script to tune on-chain uniqueness thresholds. |
| scripts/proposals/set_personhood_verifier_canister_id.sh | Proposal script to set verifier id on user_index. |
| scripts/proposals/register_personhood_verifier_functions.sh | Registers required SNS generic functions for governance. |
| scripts/proposals/commit_personhood_model.sh | Proposal script to commit hash-pinned model uploads. |
| scripts/generate-wasm.sh | Enables SIMD for personhood_verifier builds. |
| scripts/generate-typebox-types.sh | Includes personhood_verifier in type generation. |
| scripts/generate-all-canister-wasms.sh | Includes personhood_verifier in wasm build-all script. |
| scripts/download-personhood-spike-models.sh | Downloads feasibility-spike ONNX models (hash checked). |
| scripts/deploy.sh | Threads personhood_verifier into deploy tooling. |
| scripts/deploy-local.sh | Adds personhood_verifier to local deployment set. |
| scripts/copy-verification-assets.sh | Copies self-hosted MediaPipe assets for verification UX. |
| frontend/vite-env.d.ts | Adds env typing for rollout flag (and should type verifier canister env). |
| frontend/package.json | Adds MediaPipe tasks-vision dependency for capture UX. |
| frontend/package-lock.json | Locks MediaPipe dependency. |
| frontend/openchat-worker/src/worker.ts | Adds worker actions for start/upload/submit/status + removal/caching. |
| frontend/openchat-shared/src/domain/worker.ts | Adds worker request/response types for verification flow. |
| frontend/openchat-shared/src/domain/verification.ts | Adds shared domain types mirroring verifier canister API. |
| frontend/openchat-shared/src/domain/user/user.ts | Removes DecideAI proof submission response type. |
| frontend/openchat-shared/src/domain/index.ts | Re-exports verification domain types. |
| frontend/openchat-shared/src/domain/config.ts | Adds optional verifier canister config to agent config. |
| frontend/openchat-shared/src/domain/access.ts | Re-enables unique_person gate preprocessing; removes “suspended gate” stripping. |
| frontend/openchat-client/src/workerAgent.ts | Passes verifierCanister into agent config. |
| frontend/openchat-client/src/config.ts | Adds optional verifierCanister to client config. |
| frontend/openchat-agent/src/utils/userCache.ts | Adds cache update for isUniquePerson. |
| frontend/openchat-agent/src/utils/chatsDb.ts | Adds cached current-user unique-person flag update. |
| frontend/openchat-agent/src/services/verifier/verifier.client.ts | Adds canister client for personhood_verifier API. |
| frontend/openchat-agent/src/services/userIndex/userIndex.client.ts | Adds removeUniquePersonProof + durable cache sync helper; removes DecideAI submit call. |
| frontend/openchat-agent/src/services/userIndex/mappers.ts | Removes DecideAI proof mapper; trims unused imports. |
| frontend/openchat-agent/src/services/openchatAgent.ts | Routes verification calls to verifier client; removes DecideAI submission; enforces prod config. |
| frontend/openchat-agent/src/config.ts | Adds optional verifierCanister to agent config. |
| frontend/app/vite.config.ts | Runs verification asset copy script at build start. |
| frontend/app/src/utils/preview.svelte.ts | Removes unique-person “suspended gate” stripping in previews. |
| frontend/app/src/utils/humanVerification/sound.ts | Adds synthesized capture tone helper. |
| frontend/app/src/utils/humanVerification/poseDetector.ts | Adds MediaPipe-based pose estimation (UX only, with fallback). |
| frontend/app/src/utils/humanVerification/capture.ts | Adds JPEG capture/size ladder to meet canister budgets. |
| frontend/app/src/utils/featureFlags.ts | Adds build-time flag for phase-B “requirements enabled”. |
| frontend/app/src/utils/access.ts | Re-enables unique-person gate binding under feature flag; restores flattening semantics. |
| frontend/app/src/components/icons/Verified.svelte | Restores rendering of verified badge controlled by prop. |
| frontend/app/src/components/home/VisibilityControl.svelte | Removes suspended-gate stripping logic. |
| frontend/app/src/components/home/verification/VerificationModal.svelte | Adds verification modal wrapper (web). |
| frontend/app/src/components/home/verification/VerificationDebugOverlay.svelte | Adds optional debug overlay (web). |
| frontend/app/src/components/home/profile/UserProfileCard.svelte | Minor import/style adjustments. |
| frontend/app/src/components/home/profile/UserProfile.svelte | Adds verification section + “remove verification” flow. |
| frontend/app/src/components/home/profile/LearnToEarn.svelte | Re-adds proved_unique_personhood achievement display. |
| frontend/app/src/components/home/profile/Badges.svelte | Displays verified badge when uniquePerson is true. |
| frontend/app/src/components/home/PrizeContentBuilder.svelte | Adds unique-person-only restriction UI under phase-B flag. |
| frontend/app/src/components/home/PrizeContent.svelte | Enforces unique-person eligibility and shows restriction badge/CTA. |
| frontend/app/src/components/home/HumanityConfirmation.svelte | Removes old DecideAI confirmation component. |
| frontend/app/src/components/home/Home.svelte | Switches “verify humanity” modal to new verification modal. |
| frontend/app/src/components/home/CurrentChatMessages.svelte | Removes suspended-gate stripping from preview gating logic. |
| frontend/app/src/components/home/communities/PreviewWrapper.svelte | Removes suspended-gate stripping from join preview logic. |
| frontend/app/src/components/home/access/LeafGateBuilder.svelte | Adds info text for unique-person gate. |
| frontend/app/src/components/home/access/AccessGateSummary.svelte | Stops stripping unique-person gates in summary/edit flow. |
| frontend/app/src/components/home/access/AccessGateIconsForChat.svelte | Updates merged gate icon rendering logic. |
| frontend/app/src/components/home/access/AccessGateEvaluator.svelte | Re-enables unique-person gate evaluation with verification flow. |
| frontend/app/src/components/home/access/AccessGateControl.svelte | Removes stripping in “bypass warning” logic. |
| frontend/app/src/components/home/access/AccessGateBuilder.svelte | Re-enables unique-person gate editing/validation paths. |
| frontend/app/src/components/App.svelte | Injects verifier canister env var into config. |
| frontend/app/src/components_mobile/icons/Verified.svelte | Restores verified badge (mobile). |
| frontend/app/src/components_mobile/home/verification/VerificationModal.svelte | Adds verification modal wrapper (mobile). |
| frontend/app/src/components_mobile/home/verification/VerificationDebugOverlay.svelte | Adds optional debug overlay (mobile). |
| frontend/app/src/components_mobile/home/user_profile/Verify.svelte | Removes DecideAI confirmation gating and updates consent text. |
| frontend/app/src/components_mobile/home/user_profile/UserProfileSummaryCard.svelte | Shows verified account pill when unique-person verified. |
| frontend/app/src/components_mobile/home/user_profile/UserProfileSummary.svelte | Adds verify CTA when user is not verified. |
| frontend/app/src/components_mobile/home/profile/LearnToEarn.svelte | Re-adds proved_unique_personhood achievement display (mobile). |
| frontend/app/src/components_mobile/home/profile/Badges.svelte | Displays verified badge when uniquePerson is true (mobile). |
| frontend/app/src/components_mobile/home/PrizeContentBuilder.svelte | Adds unique-person restriction chip under phase-B flag (mobile). |
| frontend/app/src/components_mobile/home/PrizeContent.svelte | Enforces unique-person eligibility and displays restriction (mobile). |
| frontend/app/src/components_mobile/home/PreviewFooter.svelte | Re-enables unique-person gates in flattened join flow gates. |
| frontend/app/src/components_mobile/home/HumanityConfirmation.svelte | Removes old DecideAI confirmation component (mobile). |
| frontend/app/src/components_mobile/home/Home.svelte | Switches “verify humanity” modal to new verification modal (mobile). |
| frontend/app/src/components_mobile/home/CurrentChatMessages.svelte | Removes suspended-gate stripping from preview gating (mobile). |
| frontend/app/src/components_mobile/home/communities/explore/Explore.svelte | Refactors selected community card binding (mobile). |
| frontend/app/src/components_mobile/home/access/AccessGateEvaluator.svelte | Re-enables unique-person gate evaluation via verification flow (mobile). |
| frontend/app/src/components_mobile/home/access_gates/AccessGates.svelte | Removes suspended-gate stripping from gate presence logic (mobile). |
| frontend/app/src/components_mobile/App.svelte | Injects verifier canister env var into config (mobile). |
| frontend/app/rollup.extras.mjs | Sets OC_VERIFIER_CANISTER env (optional) and logs it. |
| frontend/app/rollup.config.mjs | Defines import.meta.env.OC_VERIFIER_CANISTER at build time. |
| frontend/app/public/assets/verification/.gitignore | Ignores copied/downloaded verification assets in repo. |
| dfx.json | Adds personhood_verifier canister entry. |
| Cargo.toml | Adds new canister crates, tools, spike crate, and face_pipeline lib; adjusts release opts for tract/zune. |
| backend/tools/verification_model_uploader/Cargo.toml | Adds model uploader tool crate. |
| backend/tools/threshold_calibration/README.md | Documents offline calibration tool and semantics. |
| backend/tools/threshold_calibration/Cargo.toml | Adds calibration tool crate. |
| backend/tools/canister_upgrader/src/main.rs | Adds CLI flag and routing for upgrading personhood_verifier. |
| backend/tools/canister_upgrader/src/lib.rs | Adds upgrade function for personhood_verifier. |
| backend/tools/canister_upgrader/Cargo.toml | Adds personhood_verifier api dependency. |
| backend/tools/canister_installer/src/main.rs | Adds installer CLI flag for personhood_verifier. |
| backend/tools/canister_installer/src/lib.rs | Installs personhood_verifier with init args; threads canister id. |
| backend/tools/canister_installer/Cargo.toml | Adds personhood_verifier api dependency. |
| backend/personhood_spike/src/scan.rs | Adds benchmark for i8 uniqueness scan cost. |
| backend/personhood_spike/src/main.rs | Adds feasibility spike harness and deterministic wasm rand. |
| backend/personhood_spike/src/jpeg.rs | Adds JPEG decode benchmark. |
| backend/personhood_spike/README.md | Documents feasibility results and instruction counts. |
| backend/personhood_spike/models/.gitignore | Ignores downloaded ONNX models. |
| backend/personhood_spike/Cargo.toml | Adds spike crate dependencies and pinned tract version. |
| backend/personhood_spike/canbench.yml | Adds canbench build/run config for spike. |
| backend/personhood_spike/canbench_results.yml | Stores measured bench results. |
| backend/libraries/types/src/proof_of_uniqueness.rs | Adds OpenChat provider + model_version field to unique person proofs. |
| backend/libraries/proof_of_unique_personhood/src/lib.rs | Removes DecideAI credential verification library. |
| backend/libraries/proof_of_unique_personhood/Cargo.toml | Removes DecideAI credential verification crate manifest. |
| backend/libraries/gated_groups/src/lib.rs | Re-enables unique-person gate enforcement (no longer always pass/filter). |
| backend/libraries/face_pipeline/Cargo.toml | Adds shared inference pipeline crate manifest. |
| backend/libraries/canister_agent_utils/src/lib.rs | Adds personhood_verifier to canister id plumbing. |
| backend/integration_tests/src/wasms.rs | Adds personhood_verifier wasm to test harness. |
| backend/integration_tests/src/setup.rs | Installs personhood_verifier canister in PocketIC setup. |
| backend/integration_tests/src/lib.rs | Adds personhood verification test module and canister ids field. |
| backend/integration_tests/src/client/user_index.rs | Adds client call for remove_unique_person_proof. |
| backend/integration_tests/src/client/personhood_verifier.rs | Adds verifier client calls and happy-path helpers. |
| backend/integration_tests/src/client/mod.rs | Exposes verifier client module. |
| backend/integration_tests/Cargo.toml | Adds personhood_verifier deps and serde_bytes. |
| backend/canisters/user/impl/src/updates/c2c_local_user_index.rs | Handles unique-person proof removal event locally. |
| backend/canisters/user/impl/src/queries/updates.rs | Ensures unique-person status updates propagate after removal. |
| backend/canisters/user/impl/src/lib.rs | Stores unique_person_proof_removed_at for update propagation. |
| backend/canisters/user/CHANGELOG.md | Notes unique person proof removal handling. |
| backend/canisters/user/api/src/lib.rs | Adds NotifyUniquePersonProofRemoved event variant. |
| backend/canisters/user_index/impl/src/updates/wipe_legacy_unique_person_proofs.rs | Adds governance proposal endpoint to trigger legacy wipe. |
| backend/canisters/user_index/impl/src/updates/submit_proof_of_unique_personhood.rs | Removes DecideAI proof submission endpoint implementation. |
| backend/canisters/user_index/impl/src/updates/set_personhood_verifier_canister_id.rs | Adds governance proposal endpoint to set verifier id. |
| backend/canisters/user_index/impl/src/updates/remove_unique_person_proof.rs | Adds user endpoint to revoke proof + delete embedding/attempt history. |
| backend/canisters/user_index/impl/src/updates/mod.rs | Wires new personhood update modules; removes DecideAI submission module. |
| backend/canisters/user_index/impl/src/updates/c2c_notify_personhood_verified.rs | Records OpenChat-provider unique person proofs from verifier canister. |
| backend/canisters/user_index/impl/src/updates/c2c_notify_model_upgraded.rs | Records model upgrade + lapse deadline and restarts lapse job. |
| backend/canisters/user_index/impl/src/model/user_map.rs | Adds method to remove unique person proof from user map. |
| backend/canisters/user_index/impl/src/lifecycle/inspect_message.rs | Allows new endpoints in inspect_message (user/governance guarded). |
| backend/canisters/user_index/impl/src/lifecycle/init.rs | Threads personhood_verifier_canister_id into init. |
| backend/canisters/user_index/impl/src/jobs/mod.rs | Starts lapse-removal job. |
| backend/canisters/user_index/impl/src/guards.rs | Adds guard for personhood_verifier caller. |
| backend/canisters/user_index/impl/Cargo.toml | Adds personhood_verifier dep; removes DecideAI proof dep. |
| backend/canisters/user_index/CHANGELOG.md | Documents new governance endpoints + DecideAI removal. |
| backend/canisters/user_index/c2c_client/src/lib.rs | Adds c2c calls for model upgrade + verification notify. |
| backend/canisters/user_index/api/src/updates/wipe_legacy_unique_person_proofs.rs | Adds candid/TS types for wipe proposal endpoint. |
| backend/canisters/user_index/api/src/updates/submit_proof_of_unique_personhood.rs | Removes DecideAI submission API. |
| backend/canisters/user_index/api/src/updates/set_personhood_verifier_canister_id.rs | Adds API types for setting verifier id (proposal). |
| backend/canisters/user_index/api/src/updates/remove_unique_person_proof.rs | Adds API types for user-initiated proof removal. |
| backend/canisters/user_index/api/src/updates/mod.rs | Wires new update APIs; removes DecideAI submission API. |
| backend/canisters/user_index/api/src/updates/c2c_notify_personhood_verified.rs | Adds API types for verifier→user_index proof notification. |
| backend/canisters/user_index/api/src/updates/c2c_notify_model_upgraded.rs | Adds API types for verifier→user_index model upgrade notify. |
| backend/canisters/user_index/api/src/main.rs | Generates TS methods for new endpoints; removes DecideAI endpoint. |
| backend/canisters/user_index/api/src/lifecycle/init.rs | Adds init arg for personhood_verifier_canister_id. |
| backend/canisters/personhood_verifier/impl/src/updates/upload_model_chunk.rs | Adds chunk upload endpoint guarded by whitelist. |
| backend/canisters/personhood_verifier/impl/src/updates/upload_frame.rs | Adds frame upload endpoint with byte + dimension checks. |
| backend/canisters/personhood_verifier/impl/src/updates/submit_verification.rs | Adds submission endpoint that queues session. |
| backend/canisters/personhood_verifier/impl/src/updates/set_uniqueness_thresholds.rs | Adds governance endpoint to tune live thresholds. |
| backend/canisters/personhood_verifier/impl/src/updates/mod.rs | Wires verifier canister updates. |
| backend/canisters/personhood_verifier/impl/src/updates/c2c_delete_user_embedding.rs | Adds user_index→verifier embedding deletion endpoint. |
| backend/canisters/personhood_verifier/impl/src/queries/verification_status.rs | Adds status query with queued position. |
| backend/canisters/personhood_verifier/impl/src/queries/model_info.rs | Adds query for model version + enrollment count. |
| backend/canisters/personhood_verifier/impl/src/queries/mod.rs | Wires verifier queries. |
| backend/canisters/personhood_verifier/impl/src/queries/http_request.rs | Adds http_request for logs/metrics endpoints. |
| backend/canisters/personhood_verifier/impl/src/model/sessions.rs | Adds heap-only session model with raw-frame exclusion. |
| backend/canisters/personhood_verifier/impl/src/model/mod.rs | Wires model submodules. |
| backend/canisters/personhood_verifier/impl/src/model/embeddings.rs | Adds embedding store + scan logic and cosine helper. |
| backend/canisters/personhood_verifier/impl/src/model/attempts.rs | Adds attempt history windowing + retry allowance. |
| backend/canisters/personhood_verifier/impl/src/memory.rs | Adds stable memory manager for upgrades/model chunks. |
| backend/canisters/personhood_verifier/impl/src/lifecycle/pre_upgrade.rs | Serializes stable state excluding raw frames. |
| backend/canisters/personhood_verifier/impl/src/lifecycle/post_upgrade.rs | Restores state/logs and re-inits env/cycles client. |
| backend/canisters/personhood_verifier/impl/src/lifecycle/mod.rs | Wires init/upgrade lifecycle and RNG reseed. |
| backend/canisters/personhood_verifier/impl/src/lifecycle/init.rs | Initializes verifier canister state and cycles client. |
| backend/canisters/personhood_verifier/impl/src/jobs/purge_lapsed_embeddings.rs | Purges superseded-model embeddings after lapse window. |
| backend/canisters/personhood_verifier/impl/src/jobs/prune_sessions.rs | Prunes sessions and retries pending proof notifications. |
| backend/canisters/personhood_verifier/impl/src/jobs/mod.rs | Starts verifier jobs. |
| backend/canisters/personhood_verifier/impl/src/guards.rs | Adds guards for user_index/governance/whitelist callers. |
| backend/canisters/personhood_verifier/impl/src/engine/mod.rs | Defines thresholds and stub embedding generator entrypoint. |
| backend/canisters/personhood_verifier/impl/Cargo.toml | Adds verifier canister implementation crate dependencies. |
| backend/canisters/personhood_verifier/CHANGELOG.md | Documents verifier canister functionality and rollout. |
| backend/canisters/personhood_verifier/c2c_client/src/lib.rs | Adds generated c2c client for embedding deletion. |
| backend/canisters/personhood_verifier/c2c_client/Cargo.toml | Adds c2c client crate manifest. |
| backend/canisters/personhood_verifier/api/src/updates/upload_model_chunk.rs | Adds upload_model_chunk API types. |
| backend/canisters/personhood_verifier/api/src/updates/upload_frame.rs | Adds upload_frame API types. |
| backend/canisters/personhood_verifier/api/src/updates/submit_verification.rs | Adds submit_verification API types. |
| backend/canisters/personhood_verifier/api/src/updates/start_verification.rs | Adds start_verification API types and NotReady semantics. |
| backend/canisters/personhood_verifier/api/src/updates/set_uniqueness_thresholds.rs | Adds set_uniqueness_thresholds API types. |
| backend/canisters/personhood_verifier/api/src/updates/mod.rs | Wires verifier update APIs. |
| backend/canisters/personhood_verifier/api/src/updates/commit_model.rs | Adds commit_model API types (hash pinned). |
| backend/canisters/personhood_verifier/api/src/updates/c2c_delete_user_embedding.rs | Adds c2c_delete_user_embedding API types. |
| backend/canisters/personhood_verifier/api/src/queries/verification_status.rs | Adds verification_status API types. |
| backend/canisters/personhood_verifier/api/src/queries/model_info.rs | Adds model_info API types. |
| backend/canisters/personhood_verifier/api/src/queries/mod.rs | Wires verifier query APIs. |
| backend/canisters/personhood_verifier/api/src/main.rs | Generates TS bindings for verifier canister API. |
| backend/canisters/personhood_verifier/api/src/lifecycle/post_upgrade.rs | Adds post-upgrade args API types. |
| backend/canisters/personhood_verifier/api/src/lifecycle/mod.rs | Wires lifecycle API modules. |
| backend/canisters/personhood_verifier/api/src/lifecycle/init.rs | Adds init args API types (whitelist + governance). |
| backend/canisters/personhood_verifier/api/src/lib.rs | Defines public verifier API types and enums. |
| backend/canisters/personhood_verifier/api/Cargo.toml | Adds verifier canister API crate manifest. |
| backend/canisters/openchat_installer/impl/src/updates/install_canisters.rs | Threads verifier canister id into user_index init args. |
| backend/canisters/openchat_installer/impl/src/lifecycle/init.rs | Threads verifier canister id into installer init. |
| backend/canisters/openchat_installer/impl/src/lib.rs | Adds verifier canister id storage/defaulting and exposes it. |
| backend/canisters/openchat_installer/CHANGELOG.md | Notes installer threading of verifier canister id. |
| backend/canisters/openchat_installer/api/src/lifecycle/init.rs | Adds verifier canister id to installer init args API. |
| backend/canisters/local_user_index/impl/src/updates/c2c_notify_user_index_events.rs | Propagates proof removal event and updates global map. |
| backend/canisters/local_user_index/impl/src/model/global_user_map.rs | Adds unique-person proof removal from global map. |
| backend/canisters/local_user_index/impl/src/lib.rs | Stops accepting DecideAI unique-person JWTs. |
| backend/canisters/local_user_index/impl/Cargo.toml | Removes DecideAI proof lib dependency. |
| backend/canisters/local_user_index/CHANGELOG.md | Notes DecideAI JWT removal and proof removal handling. |
| backend/canisters/local_user_index/api/src/lib.rs | Adds user index event variant for proof removal. |
| backend/canisters/group/CHANGELOG.md | Notes unique-person gate enforcement restored. |
| backend/canisters/community/CHANGELOG.md | Notes unique-person gate enforcement restored. |
| .gitignore | Adds .calibration/ ignore for calibration artifacts. |
| .github/workflows/backend.yaml | Excludes personhood_spike from clippy/tests in CI. |
Files not reviewed (1)
- frontend/package-lock.json: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| translations_canister_id: CanisterId, | ||
| website_canister_id: CanisterId, | ||
| #[serde(default = "anonymous_principal")] | ||
| personhood_verifier_canister_id: CanisterId, |
There was a problem hiding this comment.
We should create a new canister and then hardcode the canister id here
There was a problem hiding this comment.
Done — the canister now exists (wji62-oiaaa-aaaaf-bsc7a-cai) and is hardcoded as PERSONHOOD_VERIFIER_CANISTER_ID in the constants crate (baf77fe). The init-arg plumbing through the installer and user_index is gone; local deployments and the integration tests create the canister with that exact id (specified-id creation), so the same constant is correct in every environment.
| // dev team in production, mirroring openchat_installer's | ||
| // upload_wasm_chunks_whitelist. Chunks only activate via a hash-pinned | ||
| // commit_model proposal, so this grants no control over the live models. | ||
| #[serde(default)] |
There was a problem hiding this comment.
Don't give it a serde default - let's set the correct principals when we install
There was a problem hiding this comment.
Done — default removed in 09f5f35; installs must state the whitelist explicitly.
| // on upgrade. | ||
| #[ts_export(user_index, wipe_legacy_unique_person_proofs)] | ||
| #[derive(CandidType, Serialize, Deserialize, Clone, Debug, HumanReadable)] | ||
| pub struct Args {} |
There was a problem hiding this comment.
Sems a bit OTT - I would have thought doing on user_index upgrade would be fine
There was a problem hiding this comment.
Agreed — endpoint removed in 09f5f35. The wipe is now a post_upgrade one-liner to ship in a designated user_index release once the verification UI has baked in production (it mustn't ride the first upgrade carrying this code, because users need a working way to re-verify before their badge disappears). The rollout runbook documents this, and voters see it in that release's changelog.
| pub translations_canister_id: CanisterId, | ||
| pub registry_canister_id: CanisterId, | ||
| #[serde(default = "anonymous_principal")] | ||
| pub personhood_verifier_canister_id: CanisterId, |
There was a problem hiding this comment.
As per a previous comment - let's create a canister ahead of time and hardcode the canister id here. And no need to have a new endpoint to set it.
There was a problem hiding this comment.
Done in baf77fe — the endpoint is removed along with its SNS function registration and script; the user_index authenticates the verifier against the hardcoded constant, so there is nothing to set.
|
Should the personhood_spike be committed? Hasn't that done its job? |
I asked the same - apparently it's still doing a job, but I will ask it to rename to something less suspicious. |
The crate outlived its Phase 0 feasibility-spike origin: it is the canbench instruction-count harness to re-run before any model or tract upgrade, and its models/ directory is the canonical location used by the model uploader, threshold calibration and face_pipeline's end-to-end tests. The old name confused people into thinking it was dead code. The download script loses its -spike- too, and the README keeps the Phase 0 verdict as the historical record. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| // i8-quantized L2-normalized embeddings keyed by (model version, user). | ||
| // Heap-backed for the skeleton phase; the design moves this to a | ||
| // StableBTreeMap before real scale (100k users ~ 51MB serialized). | ||
| #[derive(Serialize, Deserialize, Default)] |
There was a problem hiding this comment.
Why not start with a StableBTreeMap?
There was a problem hiding this comment.
It now starts with one (09f5f35): a StableBTreeMap is the source of truth — nothing (de)serializes through upgrades however large the enrolled population gets — with a heap cache mirroring it for the scan path, rebuilt in post_upgrade. The cache exists because every verification brute-force scans all enrolled embeddings, and a contiguous heap scan benched ~247M instructions per 100k users (see personhood_bench), where deserializing every entry out of stable memory per probe would cost roughly an order of magnitude more.
- Embeddings: StableBTreeMap becomes the source of truth (keyed by version-prefixed user principal, nothing serialized through upgrades) with a heap scan cache rebuilt in post_upgrade, keeping the hot 1:N uniqueness scan on contiguous memory. Pre-hybrid heap embeddings are drained into the stable map on first upgrade. - Replace the wipe_legacy_unique_person_proofs governance endpoint with a post_upgrade one-liner in a designated post-phase-A release; voters review it via the upgrade proposal changelog. SNS function 1017 and its scripts are gone. - Drop the serde default from upload_model_chunks_whitelist - installs must state the whitelist explicitly. - Guard AccessGateIconsForChat on merged.length so an empty merge result doesn't render an empty icons container. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The production canister now exists (wji62-oiaaa-aaaaf-bsc7a-cai), so per review the id becomes a constant and all the wiring machinery disappears: the set_personhood_verifier_canister_id endpoint, its SNS function registration and script, and the init-arg plumbing through openchat_installer and user_index. The user_index authenticates the verifier against the constant. Local deployments (local_canister_creator + dfx.json specified_id) and the integration tests (create_canister_with_id) create the canister with that exact id, so the constant is correct in every environment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds Section H to the OpenChat Terms (desktop + mobile pages) covering the unique personhood verification service: what the camera challenge does, exactly what biometric data is processed (in-memory frames, retained face embedding, attempt history), Internet Computer node-provider visibility during processing, purpose limitation, retention and the three deletion triggers, consent withdrawal via Remove verification, and the automated nature of the uniqueness decision. The verification consent screen now links to Section H (/terms?section=12) and the consent checkbox text references it explicitly, in all 15 locales, so consent is demonstrably informed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Daily user_index job purges the unique person proof, face embedding and attempt history of users inactive for ~3 years (35 months, keeping the daily sweep inside the statutory 3-year destruction deadline). Cheap heap-timestamp candidate filter, confirmed against the online_users canister's real last-online data so active lurkers are never purged. - The consent step now discloses purpose and full retention duration (consentPoint2, all 15 locales). - Terms Section H6 gains the inactivity trigger and explicitly constitutes the written retention schedule and destruction guidelines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tion # Conflicts: # frontend/package.json
…ests Creating the personhood_verifier at its mainnet canister id makes PocketIC spin up a third application subnet covering that id's range. The test setup was then expanding OpenChat onto all three app subnets instead of the two created by the PocketIcBuilder, changing user/community placement in every test env and making tick-count-sensitive tests flaky (e.g. leave_community_succeeds, channel_marked_as_read_after_joining). Also stop returning the env to the shared pool after the biometric purge test advances its clock by 3 years, so later tests can't inherit it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
I think we will probably end up not doing this since it's a bit controversial and really cannot be made to work well enough to satisfy the stated requirement. |
Revives unique personhood verification (#9072) as an in-house, on-chain, SNS-controlled system, replacing the sunsetted third-party DecideAI/AWS integration.
What this is (and isn't)
A new
personhood_verifiercanister runs the whole face-uniqueness pipeline on-chain under DAO governance. It is a probabilistic sybil-resistance layer at parity with what DecideAI provided — the same face-embedding primitive — but fully on the IC and SNS-controlled, with no third-party dependency and no biometric data leaving the chain.It is not a cryptographic 1:N uniqueness guarantee at million scale. Face embeddings carry ~40–60 bits of effective separating entropy, so
P(collision) = 1-(1-FMR)^Ncompounds — no single threshold is both strongly sybil-resistant and low-false-reject past low tens of thousands of users. This is an information-theoretic ceiling, not an implementation gap, and it applies equally to the DecideAI system this replaces. Positioned and tuned accordingly.How it works
remove_unique_person_proof), which revokes the proof and erases the embedding; the user can re-verify whenever they like.On-chain feasibility (measured)
tract-onnxon wasm32 with SIMD. r50 embed = 24.76B instructions; the full Center-frame step (decode + detect + landmarks + embed) = 27.19B, under the 40B DTS per-message ceiling (~32% headroom). Deterministic across runs. Confirmed end-to-end on a local deployment.Thresholds
r50-calibrated on LFW and SNS-governable at runtime (
set_uniqueness_thresholds, governance-guarded, invariantclear ≤ retry ≤ duplicate, surfaced in/metrics) — the right value tracks the enrolled population, which grows. Launch defaults lean to low false-reject:T_dup 0.55 / clear 0.45 / retry 0.50(~1% innocent rejection at ~100k enrolled). Raise by proposal as N grows.Governance / lifecycle
commit_modelSNS proposal.UniquePersongroup/community gates are enforced against the new proofs.Scope
Phases 0–3 (feasibility gate, backend skeleton, real on-chain pipeline, DecideAI cutover + UI) are complete. Phase 4 (production rollout) is the ordered SNS-proposal sequence, now fully documented in
PRODUCTION_ROLLOUT.md(supersedes the earlier rollout comment on #9072). This PR also carries everything the rollout needs:upload_model_chunks_whitelistinit arg — dev principals upload (inert) model chunks in production, mirroringopenchat_installer's wasm-chunk whitelist; activation stays proposal-only via the hash-pinnedcommit_model(~180×1MB chunk calls for the r50 model made proposal-per-chunk a non-starter).commit_model,set_uniqueness_thresholds,set_personhood_verifier_canister_idandwipe_legacy_unique_person_proofsare now#[proposal]endpoints, exposing the_validatequeries SNS generic-function registration requires.scripts/proposals/: one-shot registration of the SNS functions (commit_model11000,set_uniqueness_thresholds11001 — verified unused against the live registry) plus an execution script per function. Per review, the verifier canister id is hardcoded (wji62-oiaaa-aaaaf-bsc7a-cai, created ahead of time) and the DecideAI wipe rides a designated upgrade'spost_upgrade, so no wiring/wipe functions are needed.verification_model_uploader --skip-commit— the production upload mode; prints the exact hash-pinned commit commands for the proposals.OC_UNIQUE_PERSON_REQUIREMENTS_ENABLED): phase A ships the ability to verify (flow, badges, gate evaluator) while creating unique-person gates and prize restrictions stays unavailable; phase B — once real-world verification data looks healthy — is a website rebuild with the flag set, no code change. Verification strings are translated into all 14 locales.Tests
personhood_verification7/7 green.gated_groupanddelete_usersuites cover gate enforcement and embedding deletion.Notes for reviewers
@mediapipe/tasks-vision— client-side face tracking for the capture UX only (self-hosted assets, degraded manual-capture fallback if it fails to load). Not in the trust path; the canister re-derives everything on-chain.script-srcnow includes'wasm-unsafe-eval'so the MediaPipe wasm can instantiate. This is a deliberately widened surface — flagged here explicitly.instruction_counterlogging inprocess_verificationsistest_mode-gated (inert in production) — kept as a measurement aid for the r50 cost work; say if you'd prefer it stripped.backend/personhood_bench/models/test_face.jpgis a committed determinism fixture (a public-figure portrait); model weights and wasms are gitignored.Review response (addressed in follow-up commits)
start_verificationreturnsNotReadyandprocess_one_stepparks rather than ever running the stub outsidetest_mode.upload_framerejects oversized dims via a new SOF parser before they enter the queue.commit_modelbuilds engines before the version-bump/notify and rolls back on failure; durable retry for unacknowledged proof notifications (persisted, retried byprune_sessions);cosine_similarityasserts equal length;MockVerifierClientthrows on a prod misconfiguration; debug overlay gated onimport.meta.env.DEV;saturating_subon byte accounting.test_modein the integration harness → prod-mode/oversized-frame tests; the remaining behavioural frontend items (getUserMedia timeout, poll clock,NotReadyUX); session-cap split; i18n nits.Refs #9072