Meta's ships facial recognition on smart glasses
Hacker News Grade 9 8d ago

Meta's ships facial recognition on smart glasses

Comments

Meta's smart glasses companion app ships a complete, dormant face-recognition pipeline on a stock account. Stella is the companion app for Meta's smart glasses. Inspecting version 273.0.0.21 of the Android build (com.facebook.stella ), I found the entire computational and storage stack for on-device facial recognition: three face models, a local database schema, a cosine-similarity vector index dimensioned to match the models, a write path that stages biometric records to disk, a fully wired notification surface, and a user-facing "Connections" widget. I want to be precise about what that does and does not mean, because the gap between the two is important. What I can demonstrate: the machinery is present, it is wired together. Several facial extraction and facial fingerprinting models are present and I was able run the recognition pipeline end-to-end on a test image and it detected a face, generate a 2048-dimension biometric embedding, searched a local index, and on a match fired an Android notification stating to the user "Person Recognized". To get the pipeline to run I invoked its existing handler directly with a test photo. What I cannot demonstrate: that any of this is active for ordinary users. On a stock, unenrolled account the user-facing UI does not appear, and the screen the recognition notification deep-links to is missing from the build. I also did not observe Meta server-pushing identity data to the relevant database on my test account. So this is not "Meta is secretly identifying the people you look at." It is: the complete apparatus to do exactly that is sitting on the device, assembled and functional, gated by Meta. All findings below are reproducible against com.facebook.stella v273.0.0.21. Three face-recognition models ship on the device (~100 MB) Three ExecuTorch (.pte ) models arrive on the device via NMLML, Meta's asset-delivery system, downloaded from Meta. | Asset name (Meta's naming) | File | Size | Function | |---|---|---|---| android_facerec_scrfd | SCRFD.pte | 3.4 MB | Detects faces in an image | android_facerec_kps_aligner | KPSAligner.pte | 117 KB | Crops and aligns each detected face | android_facerec_sface | SFace.pte | 96 MB | Converts a face into a 2048-number embedding (the biometric fingerprint) | These map onto open-source architectures, the same model families that other apps and academic projects already use: - SCRFD Sample and Computation Redistribution for Efficient Face Detection (InsightFace, ICLR 2022). Reference implementation: github.com/deepinsight/insightface . - SFace Sigmoid-Constrained Hypersphere Loss for Face Recognition (Zhong et al., 2021). Reference: github.com/zhongyy/SFace - KPSAligner keypoint-based alignment, standard practice since 2015 (MTCNN, dlib, InsightFace). Meta's SFace variant seems to be scaled larger than the public reference (96 MB vs. ~40 MB; 2048-dimension output vs. the reference's 128–512). Worth stating plainly: shipping detection and embedding models is not, by itself, evidence of recognition. Plenty of apps run on-device face detection for framing or autofocus. A cosine-similarity face index, dimensioned exactly to the on-device fingerprinter The recognition pipeline that actually runs and reads into this database: /data/user/0/com.facebook.stella/files/rldrive/person_profiles/objects.db This lives under RLDrive, Meta's cross-device sync framework, in a person_profiles namespace designed to be populated remotely. I did not directly observe Meta pushing data to person_profiles specifically on my test account. I want to be clear that I'm describing the channel's existence, not an observed transmission. The schema: CREATE TABLE person ( nodeid INTEGER PRIMARY KEY, name TEXT, uri TEXT, blob BLOB, deleted INTEGER, version BLOB ); CREATE TABLE face ( nodeid INTEGER PRIMARY KEY, mediaPath TEXT, -- the face_id used in the deep link personUri TEXT, -- soft reference back to person.uri blob BLOB, deleted INTEGER, uri TEXT, version BLOB ); CREATE VIRTUAL TABLE face_mediaPath_vec USING vec0(mediaPath float[2048] distance_metric=cosine); -- 2048-float biometric fingerprint per face, cosine-distance search -- (uses the sqlite-vec extension) Each face row points at a person via personUri . Each face.mediaPath is the primary key into face_mediaPath_vec , which stores the 2048-number embedding. Recognition is a cosine-similarity query against that index, followed by a join into person.name for the notification text. A few things line up: vec0 is the open-source sqlite-vec extension, which turns SQLite into a vector-similarity engine.- The dimension float[2048] is the exact output shape of the SFace embedder shipped on the app. - The cosine metric is the standard choice for comparing face embeddings. The schema permits multiple face rows per personUri (no UNIQUE constraint), but whether a production deployment uses one-to-one or one-to-many is not visible from a non-enrolled device. End-to-end test confirms both branches and isolates where writes go. I SHA-256-snapshotted and row-counted the database, then ran the full recognition pipeline twice: once against an empty index (no-match), once against an index pre-loaded with a single embedding (match): - No match (empty face_mediaPath_vec ): one(uuid.jpg, uuid.emb) pair was written toNameTagsPending/ . No notification. - Match: an Android notification fired through the production nametags_recognition channel - title "Person recognized", body "Recognized Michel Foucault". Nothing was added toNameTagsPending/ . Unrecognized faces are staged to disk: crop plus fingerprint in NameTagsPending/ When the device sees a face that the local index does not match, Stella writes it to: /data/user/0/com.facebook.stella/files/NameTagsPending/ Each unrecognized face produces a pair of files named with a fresh UUID: - a .jpg — the cropped, aligned face, the output of SCRFD + KPSAligner; and - an .emb — the 2048-number SFace fingerprint. The directory is mode 0700 and survives reboots. Writes happen only on the no-match branch; matched faces go to a notification and leave no on-disk trace. I verified the embedding's structure directly: File: NameTagsPending/1566ab46-[...].emb Size: 8,192 bytes (2048 × float32, big-endian) L2 norm: 0.999999 ← canonical L2-normalized face embedding Min/max: −0.092110 / +0.098950 Mean: +0.000292 Together, (uuid.jpg, uuid.emb) is a complete, indexable biometric record of one face — the same shape and encoding the cosine index in person_profiles/objects.db is built to match against. The name NameTagsPending most literal reading is "faces pending a name" — biometrically encoded, awaiting a label. I'll note the structural fact and let it carry its own weight: a face image and its fingerprint, stored side by side in plaintext, mode 0700 , surviving reboots, is precisely the dataset you would assemble if you intended to retroactively identify faces once a label arrives. The notification surface is fully wired Stella defines a dedicated Android notification channel NotificationChannel{ id = "nametags_recognition" name = "NameTags recognition" description = "Notifications for recognized NameTags connections" importance = IMPORTANCE_HIGH (heads-up + sound + badge) sound = system notification sound } The notification template is hardcoded in the recognition handler. Title is always "Person recognized"; body is always "Recognized " + name , where name comes from the person table in person_profiles/objects.db : NotificationCompat.Builder(ctx, "nametags_recognition") .setContentTitle("Person recognized") .setContentText("Recognized " + matched_name) .setAutoCancel(true) .setContentIntent( PendingIntent.getActivity( ctx, matched_name.hashCode(), Intent.ACTION_VIEW with Uri "fb-viewapp://name_tags?face_id=" + face_id, FLAG_IMMUTABLE | FLAG_UPDATE_CURRENT)) .build() NotificationManagerCompat.notify(matched_name.hashCode(), notification) The notification is tappable: its contentIntent is a deep link of the form fb-viewapp://name_tags?face_id= , a Meta-authored URL scheme meant to open a person-profile screen inside Stella. One honest caveat: in v273, I could not find that destination screen. Tapping the notification routes Stella to its default tab, because the target Compose destination is absent from the navigation graph. The notification fires; the screen it points at isn't built into this release. A user-facing "Connections" entry point exists in the APK Stella v273 contains a widget rendering a card under a section header titled "Connections", with the text "See your connections" / "Remember the people you met and make new connections." Both strings are hardcoded literals in the APK not server-pushed. On a stock, unenrolled account, the card does not appear on the Glasses tab at all. It became visible during testing. In normal use, a user would not see this. What this adds up to - The full on-device face-recognition stack: detection, alignment, embedding, vector index, storage, write path, and notification surface is present and assembled in Stella v273. - It is functional. Run end-to-end, it recognizes a known face and names it in a notification, and it stages unknown faces (crop + fingerprint) to disk. - The index dimension, embedding shape, and storage schema are mutually consistent, this is a coherent system, not stray dead code. - The pieces a user would actually touch: the "Connections" card and the profile screen the notification opens are either absent from the build or buried deeper. - The database the live pipeline uses sits in a sync namespace Meta populates server-side, alongside other namespaces it already populates, but I did not observe a push to the face namespace on my account. What I am not claiming: that Meta is identifying strangers for users today, that enrollment data is flowing, or that any of this is enabled in production. What's hard to wave away: building, shipping, and wiring this much apparatus down to an 2048-dimension facial fingerprinting and a hardcoded "Person recogni

Comments

No comments yet. Start the discussion.