Shoal started as a walled garden: one family, talking to itself. But families aren’t islands. There are cousins, godparents, the grandparents on the other side. So we built Connections — a way to link two families into a shared conversation — and it turned out to be the hardest cryptography we’ve done, precisely because we refused to soften either family’s boundary to make it easy.

The problem

Inside a family, everything is keyed to that family. Conversation keys, profile pictures, even the display names people use for each other — all of it sits behind that family’s boundary, and our servers hold it only as ciphertext. A shared conversation between two families breaks the neat assumption that one boundary covers everyone in the room. Now there are two boundaries, and a message has to be readable across both without our servers becoming the place where the two meet in the clear.

How a Connection works

When two families agree to connect, the shared conversation gets its own key, and that key is wrapped separately for each family — an envelope each side can open with what it already holds, and neither side’s everyday family key is ever handed over. A message sent into that conversation is encrypted once and made openable by both families, with the server only ever shuffling sealed envelopes between them.

The fiddly parts were the things that aren’t the message. A profile picture, for instance: a cousin in the other family needs to see your child’s avatar, but our servers must never decrypt the image to make that happen. So every avatar now has its own key, generated on the device when the picture is set, and it’s that key — not the picture — that gets wrapped for each family allowed to see it. We closed an earlier version that briefly decrypted and re-encrypted the image inside our servers; now the server never touches the picture again. Display names work the same way: the name your family uses for someone is encrypted for each family entitled to read it, so a connection shows the right names without us ever holding them in plaintext.

Honest about the seams

There is one operational seam worth naming. At the instant you accept a connection, our server momentarily holds both families’ key material in order to wrap the new shared conversation key for each side. It’s a brief, necessary window during link setup, and it’s the same window we reuse to wrap the avatar keys. We’d rather describe it plainly than pretend the handshake is free of it. The broader architectural limits of the model are on the privacy page.

Connections are also deliberate and reversible. Creating one is an explicit act on both sides, and revoking one asks you to confirm — because quietly dropping a grandparent out of the family chat is not the kind of thing that should happen on a mis-tap.

How this fits with everything else

A connected conversation behaves like any other: admin oversight still applies to the children in your family, moderation and active hours still gate them, and the shared thread shows up in the conversation list alongside the rest. The security page describes the key model.

Try Shoal, link up the wider family, and tell us how it holds up at [email protected].