This page is the detail behind one sentence in the privacy policy: that we cannot hear or see what passes through our server. It is written for people who want to check that claim rather than take it. Nothing here is a promise about what we collect; that is the policy's job.
Pairing, without ever sending the PIN
The nursery phone shows a four-digit PIN and a QR code. A parent phone joins by scanning the code, or by typing the PIN. The QR code carries only the PIN and a short tag that names the nursery; it does not carry the room key. Scanning and typing then take exactly the same road.
Pairing works only on the home Wi-Fi, and only while the nursery phone is showing its code; with the code card closed, or the nursery screen asleep for the night, it refuses every pairing attempt. Every attempt runs a password-authenticated key exchange (SRP-6a, RFC 5054, 2048-bit group, SHA-256). The PIN never travels, in any form: a device listening on your Wi-Fi learns nothing it could guess from later, and each guess costs the attacker one whole attempt against the nursery. The nursery counts those attempts, started ones included. Five wrong PINs from one address lock pairing for a minute; keep going and the PIN is thrown away and replaced, which ends the attack for good.
A correct PIN is still not enough. The nursery phone shows who is asking and waits for the person holding it to tap Let in; nothing is handed over before that, and no answer within a minute is a refusal. Only then does the parent receive the room key: 128 random bits generated on the nursery, handed over inside the encrypted exchange. On its first connection after that, inside the encrypted session, the nursery also gives that phone its own parent key, another 128 random bits that only the nursery and that one phone know. Everything below is built on these two.
A nursery without Sova Pro accepts one parent phone. The check happens after the PIN has been proven and before anyone is asked to let the phone in.
Signing keys, one way
The room key and the parent keys stay on your phones. Our server never receives them. What it receives are signing keys, one for the nursery and one for each parent phone, sent by the nursery itself:
nursery signing key = HMAC-SHA-256(room key, "sova-alert-auth")
parent signing key = HMAC-SHA-256(parent key, "sova-alert-auth")
HMAC runs one way. A signing key is enough for the server to verify that a heartbeat, an event, a status request or a socket really comes from one of your phones, and it cannot be turned back into the key it came from. Since every key that encrypts alerts, sound and video is derived from the room key, the server cannot compute any of them.
Disconnecting one phone. The nursery keeps the list of parent signing keys on our server. Remove a phone on the nursery, and its key leaves the list: from then on the server refuses that phone's requests and sockets, drops its notification address, and closes its open connections, while the other parent phones go on as before. Once every parent phone in a room has its own key, the nursery switches the room to parent keys only: the room key alone, including a copy kept on an old phone, then opens nothing on our server or on your Wi-Fi.
Your room is named by the hash of the signing key, so that registering a key under someone else's room name is rejected. The name identifies nothing about you: not your phone, not your network, not you.
At home: the two phones talk directly
The parent phone finds the nursery over mDNS and a UDP beacon, then opens a direct TCP connection. The broadcast carries a tag that changes every hour, never the PIN and never the nursery's name, so a listener on the same network cannot tell which room is which, nor link one hour to the next. The name, and a fixed tag that the QR code points to, appear only while the pairing card is open on the nursery.
tag = HMAC-SHA-256(room key, "sova-rr|" + hour)
Each connection starts with an ephemeral ECDH key agreement on P-256, authenticated by HMAC tags computed from that phone's own parent key (from the room key for a phone that has none yet), with separate labels for the two ends. Both sides then derive separate keys per direction with HKDF, and every frame after that, sound, video, status and commands alike, is sealed with AES-256-GCM under a counter nonce. A device on your Wi-Fi can see that two phones are talking and nothing more. It cannot read a frame, replay one, or inject one.
Away from home: through us, but closed to us
When your phone is not on the nursery's Wi-Fi, both phones open a WebSocket to our server, each request signed with that phone's own signing key. The server's whole job is to copy bytes from one socket to the others in the same room. It holds each message in memory for the instant that takes and writes none of them anywhere.
Every relayed message carries its own header: a 16-byte sender nonce, an 8-byte counter, then the ciphertext. The key is derived per sender and per direction:
key = HKDF(room key, nonce, "sova-relay-n2c") nursery to parent
key = HKDF(room key, nonce, "sova-relay-c2n") your voice back
AES-256-GCM again, a fresh key per session, and the receiver derives it from the header of the message in front of it, so two nursery sockets overlapping for a moment cannot confuse one another. The server has no room key, so it has no key here at all. Audio is AAC-LC at 32 kbit/s, video is H.264; both are encrypted before they reach the socket and decrypted only on your phone.
Alert traffic uses TLS on top of all this, and every request carries a timestamp inside the signature, so a captured request cannot be replayed later.
Alerts: sealed for your phones
What the nursery reports, whether it is crying, overheating, the end of crying, or its battery, temperature, signal and the crying of the last four hours, is sealed on the nursery before it leaves:
key = HKDF(room key, "sova-state-v1")
AES-256-GCM, 12-byte random nonce, associated data = room name | "hb" or "ev"
The server stores the sealed status as it is and passes sealed events on to Google, who deliver them to your phones. Neither can read them. What they do see is that the nursery checked in or sent something, and when: without that the server could not wake you when the nursery goes silent, and the notification could not be delivered. A sealed event also carries the nursery's own time inside, and your phone throws away one that is far from the time it was delivered, so an old event cannot be replayed as a new one.
For as long as some parent phone in a room still runs an older version of the app, the server tells the nursery so, and the nursery adds a readable copy for that phone only. When the last old phone is updated or removed, the readable copies stop.
What this does not protect you from
An honest list is worth more than a long one.
- Someone holding an unlocked phone. Either phone in the pair can see and hear the nursery. The app's security begins where the phone's own lock ends.
- Someone who can read the nursery's screen and reach the nursery phone. The PIN and QR code are on it, on purpose, because that is what makes pairing a ten-second job. A photo of the screen alone is not enough to pair: the pairing has to happen on your Wi-Fi, and the nursery phone asks whoever is holding it before it lets a new phone in. Someone who can do both can pair a phone. Give the room a new code afterwards, and the one they paired is out.
- Us, if you stop trusting the app itself. Everything above is done by code we wrote and ship. It means that our server cannot read your stream, which is a real and checkable property. It does not mean that an app can prove its own good behaviour; no app can.
- Traffic analysis. We can see that a room is active, when the nursery sends an event, and how much data moves through it. We cannot see what is in it.
Found a problem?
Write to contact@forgelabs.systems. Security reports are welcome and answered, and nothing here is a trap: the protocol is described above precisely so that it can be checked.