Carlo is built on a simple, slightly stubborn promise: the data you put into it is yours, and the company that runs it can’t read it. Your records are locked in a way that leaves us holding nothing but sealed boxes.
That promise gets interesting the moment two people want to share. CoCarlo lets a couple keep a shared financial life together, and the obvious question is: if you and your partner can both open the shared data, and it all lives on our server, how is it still true that we can’t open it?
Here’s the whole answer, first in plain language, then, for the people who want to see the actual mechanism, in technical terms.
The plain version: one key, two safes
Picture a shared house. Everything you and your partner track together lives inside it, and there’s a single key that opens it.
Both of you need a copy of that key. The trick is getting a copy to each of you without ever leaving one lying around where someone else could grab it.
So each person has their own personal safe. A safe with a one-way slot on top: anyone can drop something in, but the only way to take something out is to open the safe with your own private combination, which only you know.
Sharing works like this:
- Alice’s phone creates one shared key. That key locks and unlocks all of the shared data.
- It makes two copies.
- It drops one copy through the slot in Alice’s safe, and one copy through the slot in Bob’s safe.
- Neither copy can be pulled back out through the slot. The only way out is to open the safe.
- Alice opens her safe with her private combination and takes out the key. Bob does the same with his.
- Now both of them can open and update the same shared data.
The server stores both locked safes and the locked shared data. It doesn’t know either private combination, so it can’t open the safes, and it can’t read the data.
That last part is the whole point. The server holds everything and can open nothing. A break-in, a subpoena, or a curious employee gets a pile of locked safes and a pile of locked data, and no way into any of it.
One more thing that falls out of this design for free: because each person has their own safe, one person leaving never locks the other out. Your copy is yours. And adding a third person later is just one more safe, no need to disturb anything that already exists.
If that’s the level of detail you wanted, you’re done. You now understand how CoCarlo sharing keeps your data private. The rest of this post is for the people who want to see what the safes and combinations actually are.
The technical version: per-recipient key wrapping
Everything above is a metaphor for a standard, well-understood pattern in cryptography. Here’s the translation.
What the metaphor represents
The shared room key is the shared CoCarlo DEK (data encryption key): a symmetric key used to encrypt and decrypt the shared data.
The shared room is the encrypted CoCarlo dataset: transactions, journal entries, photos, and any other records belonging to the shared account.
The personal safes are not containers. Each safe is a separately encrypted copy of the shared DEK. There is one shared DEK, but the server stores two wrapped versions of it:
shared_dek_wrapped_for_alice
shared_dek_wrapped_for_bob
Both wrap the same underlying DEK. They’re encrypted differently because each is intended for a different person.
The one-way slot is a public key
A safe’s one-way slot is a user’s public key.
Alice can use Bob’s public key to encrypt the shared DEK for Bob. She never needs Bob’s private key to do it:
bob_wrapped_dek = encrypt_with_bobs_public_key(shared_dek)
Bob’s public key can sit on the server and be handed to anyone. Knowing it lets you encrypt something for Bob; it does not let you decrypt anything meant for Bob. That’s exactly why the slot is public: anyone can put something securely into Bob’s safe, and no one but Bob can take it out.
The private combination is a private key
Bob’s private combination is his private key. Only Bob’s device has it. It lives in the device’s secure keystore and is never sent to the server. His device uses it to unwrap his copy of the DEK:
shared_dek = decrypt_with_bobs_private_key(bob_wrapped_dek)
Alice has her own independent keypair and does the equivalent with her own wrapped copy.
Why there are two safes
A DEK wrapped for Alice can’t be decrypted by Bob, because they hold different private keys. So the shared DEK is wrapped once per member:
alice_wrapped_dek = encrypt_with_alices_public_key(shared_dek)
bob_wrapped_dek = encrypt_with_bobs_public_key(shared_dek)
Same DEK inside both. Only the outer encryption differs. This pattern is usually called per-recipient key wrapping, or envelope encryption.
What the server stores, and doesn’t
The server stores:
encrypted_shared_data
alice_wrapped_dek
bob_wrapped_dek
alice_public_key
bob_public_key
The server does not store:
the raw shared DEK
Alice's private key
Bob's private key
Because it holds neither private key, it can’t unwrap either copy of the DEK. Because it can’t obtain the DEK, it can’t decrypt the shared data. The inability is structural, not a policy we promise to follow.
What actually happens when Alice opens CoCarlo
- Her device downloads her wrapped DEK.
- It retrieves her private key from the secure keystore.
- It unwraps the shared DEK locally.
- It decrypts the shared data locally.
- When she changes something, the device re-encrypts it with the same shared DEK before sending it back.
Bob does the same, independently, with his own wrapped DEK and private key. Neither person depends on the other being online.
Adding another member
To add Charlie, an already-authorized device gets Charlie’s public key and creates one more wrapped copy:
charlie_wrapped_dek = encrypt_with_charlies_public_key(shared_dek)
The shared data is never re-encrypted. The system just adds one more authorized route to the same DEK.
The whole architecture in three lines
one shared symmetric key
+ one asymmetric wrapper per member
+ zero server-side decryption capability
CoCarlo encrypts shared data with a single symmetric DEK, then stores a separately wrapped copy of that DEK for each authorized member, encrypted to that member’s public key. Each member unwraps their copy locally with their private key. The server is a place that stores sealed things and can open none of them.
That’s the design. The metaphor and the mechanism are the same idea at two resolutions, and both are true: one shared key, one personal safe per person, and a server that holds everything and opens nothing.