Vauchi Documentation

Contacts that never go stale.

Swap details once, in person — and they keep themselves current, forever. New number, new job, new city: the people who matter just know, without you ever having to send another "my number changed" text.


What is Vauchi?

Vauchi is a living contact card you exchange face to face, in a single moment — a kind of digital handshake. From then on, whenever your details change, everyone who holds your card sees the update automatically. No feeds, no accounts, no company sitting in the middle of your relationships.

The relationship is yours and theirs. No one else is in the room.

  • Meet once, stay in touch — exchange in person; it keeps itself current forever
  • No sign-up, no phone number — your device is your identity
  • Yours alone — end-to-end encrypted, so only you and your contacts can ever read it
  • Open source — verify every claim yourself
👤 Getting StartedSet up and exchange
FAQCommon questions
🔒 SecurityData protection
💜 Our PrinciplesWhy we built it

For Developers

Get the App

  • Desktop: Coming soon (macOS, Windows, Linux)
  • CLI/TUI: Coming soon
  • iOS: Coming soon
  • Android: Coming soon

Source Code

GitLab · GitHub Mirror


Exchange once. Stay in touch — whatever changes.

For Users

A paper business card is a small act of optimism. You hand someone a rectangle of card and quietly hope that, months later, when they finally need you, the ink still means something. Usually it doesn't. By then you've changed job, number, or city, and your carefully chosen typeface is a fossil in a drawer.

Vauchi starts from a different premise. The valuable part of meeting someone is the meeting — the fact that you chose to be in the same room. The data is just the residue. So Vauchi keeps the human bit exactly as it is — you swap details face to face, on purpose — and quietly fixes the part that always rots. When your number changes, theirs updates. No re-sending, no "hi, new phone!" group text, no fossils.

This section is the owner's manual. It is shorter than you fear.

Start here

New to Vauchi? Two pages get you running:

  • Getting Started — create your identity and make your first exchange (about five minutes)
  • FAQ — the questions everyone asks, answered plainly

What it does

FeatureIn one line
Contact ExchangeSwap cards in person — a deliberate, private handshake
Automatic UpdatesChange your details once; everyone's copy follows
Privacy ControlsDecide, per person, what they're allowed to see
End-to-End EncryptionWhy nobody in the middle can read any of it
Backup & RecoverySurvive a lost phone without surrendering to a cloud
Multi-Device SupportOne identity, every screen you own

How to do things

GuideWhat you'll learn
Exchanging ContactsAdd someone, step by step
Setting VisibilityShow your mobile to friends, not to the conference
Device RecoveryGet back in after losing a device
Multi-Device SetupLink a second device safely

Why this one is different

Most apps want to be the room you live in: a feed to scroll, a follower count to mind, a profile to perform. Vauchi wants to be the handshake, and then get out of the way. There is no feed, no audience, no number going up.

The whole thing rests on one slightly unfashionable idea — that giving someone your real details, in person, because you decided to, is worth more for being scarce, not less. A contact you met is a contact you meant. Everything in the app is built to protect that.

Need help?

Getting Started

Most apps begin by asking for an email, a password, and a small fraction of your soul. Vauchi begins by asking for a name — the one you'd actually like people to call you. That's nearly the whole setup. The rest of this page is mostly reassurance.


Create your identity

When you first open Vauchi:

  1. Enter your display name — how contacts will see you. It needn't be your legal name; it needs to be the name you'd recognise being waved at across a room.
  2. Tap "Create Identity" — Vauchi generates your cryptographic identity on the spot.

Behind that single tap, your device mints:

  • A unique public ID — a fingerprint for you, not a username someone else could have grabbed first
  • The cryptographic keys that keep your exchanges private
  • Your display name

Important

There is no account, no server, no "forgot password" email. Your identity lives only on your device. Your device is your account. That is the point — and the responsibility. Which is why the very next thing you should do is make a backup.


The home screen, briefly

After setup you'll see three things worth knowing by name:

  • Your Card — the details you've chosen to share
  • Fields — the individual pieces (an email, a phone number, a birthday)
  • Navigation — Contacts, Exchange, and Settings

That's the entire map. You won't get lost.


Add your details

A field is one fact about you that you're willing to hand out. You add them one at a time, and — this is the unusual part — you get to decide later who each one is for.

  1. Tap the + button on the home screen
  2. Choose a field type:
    • Email
    • Phone
    • Website
    • Address
    • Birthday
    • Social — profiles on other networks
    • Custom — anything else
  3. Add a label (e.g. "Work", "Personal") — this is how you'll aim it at the right people
  4. Enter the value (e.g. john@example.com)
  5. Tap Add

You don't have to add everything now. A card with one good number beats a card with twelve fields you'll never maintain.


Your first exchange

Here's the short version. You'll do it for real in under a minute:

  1. Meet someone — the genuinely in-person kind
  2. Open the Exchange tab
  3. Show them your QR code
  4. Scan theirs
  5. Done. You're connected, encrypted, and you'll both stay current.

That QR handshake is the simplest of several ways to exchange; the others (a tap, a bump, a shared link) live in the Exchange Contacts guide. Start with QR — it works everywhere.


What to do next

You're set up. Three small habits make Vauchi genuinely useful rather than merely installed:

  • Make a backup now — five minutes today saves a very bad afternoon later
  • Decide who sees what — keep your home address for friends, not for the person you met at a trade show
  • Add a second device — a phone and a tablet means losing one is an inconvenience, not a catastrophe

A few honest tips

On security. Make the backup. Use a passphrase you could tell a friend over the phone but a stranger would never guess — four unrelated words beats P@ssw0rd! every time. For the contacts that truly matter, take ten seconds to verify them in person; it's the one check no attacker can fake.

On privacy. Each time you add a field, glance at who can see it. The default is sensible, but your sense of "private" is the only one that counts. Every so often, look at your card the way a new contact would.

On staying tidy. Label things like you'll need to find them in a hurry ("Work Email", "Personal Cell"), and fix wrong details the moment you notice — because now, when you fix yours, you fix everyone's.


Need help?

Frequently Asked Questions

The questions people actually ask, answered without the hand-waving.


Privacy & Security

Is my data encrypted?

Yes — in every state it can be in:

  • At rest: everything on your device is encrypted with XChaCha20-Poly1305. The key is held in your platform's secure store (iOS Keychain / Android KeyStore), not lying around on disk.
  • In transit: end-to-end encrypted (X25519 key agreement + XChaCha20-Poly1305), so the journey is sealed from end to end.
  • In backups: protected by Argon2id (a deliberately slow, memory-hungry key stretch) plus XChaCha20-Poly1305.

The short version: the only place your data is ever readable is on the screens of the people you chose to share it with.

Can the relay server read my contacts?

No. The relay only ever handles sealed, encrypted blobs. It cannot decrypt content, cannot see your contact list, cannot read a single field, and cannot tie your identity to your data. It is, by design, a courier that has never once been given a key.

What does the relay actually store?

Almost the least it possibly could:

  • Encrypted envelopes (deleted on delivery, or after 120 days)
  • A little connection metadata for rate-limiting (a cryptographic identity hash — not your IP — discarded after 30 minutes idle)

And it never even learns your IP address: requests reach it through an independent Oblivious HTTP gateway, run by a different party. The gateway sees where you are but not what you're asking; the relay sees what you're asking but not where you are. No one holds both halves.

Is Vauchi truly private?

Here's the honest distinction. Most apps ask you to trust that they won't peek. Vauchi tries to make peeking structurally impossible, so trust isn't required:

  • Your data lives on your device, not our servers
  • End-to-end encryption means we couldn't read it if we wanted to — and we don't want the liability of being able to
  • No analytics, no trackers, no ad identifiers, no accounts
  • It's open source, so you don't have to take any of this on faith — you can check

The best privacy policy is the one you can't break even under subpoena. That's the one we aimed for.


Identity & Account

What happens if I lose my device?

You have several ways back in, in rough order of painlessness:

  1. Another linked device — if you have one, you've already lost nothing. Carry on.
  2. A backup — restore from your encrypted backup file.
  3. Social recovery — trusted contacts vouch that you are you.
  4. Start fresh — mint a new identity and re-exchange. Always available, never necessary if you planned ahead.

The lesson hidden in that list: the five minutes you spend making a backup today is what turns a lost phone from a tragedy into an errand.

How does social recovery work?

It borrows the oldest security system humans have: people who know you.

  1. On a new device, you create a "recovery claim"
  2. You share it with contacts you previously marked as trusted
  3. Each one issues a "voucher" confirming they recognise you
  4. Once enough vouchers arrive (a handful by default), your contacts migrate to your new identity

It guards both doors at once: you can't be locked out (your friends can let you in), and you can't be impersonated (a stranger would have to fool several of your friends at the same time). Note that social recovery mints a new cryptographic identity — your old signing keys stay lost, and a few settings like per-contact visibility may need re-tuning. You keep your people; you replace the lock.

Can I change my display name?

Yes — Settings, edit, done. The change rolls out to your other devices and to your contacts automatically.

Do I need an account?

No. There is nothing to sign up for, no email to confirm, no password to forget on our end. Your identity is created on your device and stays there. Refreshingly little, isn't it.


Contacts & Exchange

How do I exchange contacts?

The everyday way:

  1. Meet the person — actually, in person
  2. Open the Exchange screen
  3. Show them your QR code
  4. Scan theirs
  5. You're connected, encrypted, and you'll both stay up to date

Why meet in person?

Because proximity is a security feature you already understand. When you're standing in front of someone, three things become true at once: you know they are who they appear to be, no one in the middle can slip between you, and the trust is anchored in the real world rather than in a username someone could have faked. It's the same instinct that makes a handshake mean more than a friend request.

Can I exchange contacts remotely?

Yes — there's a Link mode for when you can't be in the same room. You share a one-off vauchi://exchange?… link (by message, email, however you like) and the other person opens it; the exchange completes through the relay, asynchronously, within a few days.

The in-person methods are still the default, and for good reason — they give the strongest guarantee against a man-in-the-middle, because physical presence is the one thing an attacker on the network can't forge. Link mode trades a little of that assurance for reach. Use it knowingly, the way you'd post a key versus handing it over.

Can I remove a contact?

Yes:

  1. Go to Contacts
  2. Select the person
  3. Tap Delete / Remove and confirm

That deletes them from your device and cuts off future updates. What they already saw, they keep — you can close the tap, but you can't un-pour the water. Which is simply how sharing works, here as anywhere.


Multi-Device

Can I use Vauchi on multiple devices?

Yes:

  1. Set up Vauchi on your first device
  2. Go to Settings > Devices and generate a device link
  3. Follow the linking steps on the second device
  4. Both now share one identity and stay in sync

Up to 10 per identity — comfortably more than most people own, and few enough to keep the trust circle small.

How do I move to a new phone?

Either method works:

Link it (recommended). On the old phone, Settings > Devices > generate a link; on the new phone, install Vauchi and join. Once synced, retire the old phone.

Restore from backup. On the old phone, create an encrypted backup; on the new phone, install and restore. Handy when the old phone is already gone.


Backup & Restore

How do backups work?

  1. You pick a password
  2. Vauchi encrypts a complete copy of your account under it
  3. You get a backup file to keep wherever you like
  4. To restore: backup file + password = you, again

The password never leaves your head, and the file never leaves your control. That's the whole trick.

What's in a backup?

A full backup carries your whole account:

  • Your identity (the master seed all your keys grow from)
  • Your contacts
  • Your own card
  • Your labels and groups

The one thing it deliberately omits is the per-conversation forward-secrecy state (the Double Ratchet keys) for each contact. Those are meant to be short-lived — that's what gives you forward secrecy — so they simply re-establish themselves the next time you and a contact sync. You get your people back; you don't drag along yesterday's disposable keys.

I forgot my backup password. Can you recover it?

No — and you should be glad. The backup is encrypted so that only the password unlocks it. If we could recover it for you, so could anyone who compromised us, and the whole guarantee would be theatre. The price of a lock no one else can pick is that you have to keep the key. Choose a passphrase you'll remember; write it somewhere safe if you must.


Visibility & Sharing

How do I control what each contact sees?

  1. Open a contact's detail page
  2. Find What They Can See
  3. Toggle individual fields on or off

Do contacts know when I hide a field?

No notification fires. The field simply stops appearing on your card, indistinguishable from your having removed it. Privacy that announces itself isn't privacy.

Can I share different details with different people?

Yes — this is the whole point, not a side feature:

  • Work contacts: work email, no personal mobile
  • Family: everything
  • People you just met: the basics, until they earn more

One card, many faces — the same way you already behave in real life, just finally possible in software.


Technical

What's the relay server for?

Think of it as a post office for sealed envelopes — one that rewrites the address label every night so it can't keep a diary of who writes to whom. It:

  • Routes encrypted messages between your devices and contacts
  • Holds messages briefly when a device is offline, then forwards them
  • Addresses everything by daily-rotating tokens, so it never learns the social graph
  • Cannot read a word of any of it

Does Vauchi work offline?

Partly, and sensibly so:

  • View all your data offline — yes
  • Edit your card offline — yes; changes sync when you reconnect
  • Exchange a new contact — needs the relevant hardware (camera, etc.) and, for remote Link exchanges, a network

What cryptography does Vauchi use?

  • Signing: Ed25519
  • Key agreement: X25519 (Curve25519)
  • Symmetric encryption: XChaCha20-Poly1305
  • Key derivation: HKDF-SHA256 internally; Argon2id for passwords
  • Forward secrecy: the Double Ratchet protocol

All of it is built on well-known, audited Rust libraries implementing IETF-standardised algorithms — nothing home-rolled where a reviewed standard exists. For the full reference, see the Cryptography page.

Is Vauchi open source?

Yes — every line: gitlab.com/vauchi. You can read how your data is handled, check the security claims yourself, contribute, or run your own relay. "Trust us" is a weaker promise than "go and look."


Troubleshooting

My contacts don't see my updates

  1. Check your internet connection
  2. Confirm sync is current (Settings > last-sync time)
  3. Make sure the field is actually visible to that contact
  4. Ask them to refresh manually

The QR scanner won't read the code

  1. Check camera permissions
  2. Add light
  3. Wipe the lens
  4. Adjust your distance to the code
  5. Restart the app

Sync seems stuck

  1. Check connectivity
  2. Trigger a manual sync (pull to refresh, or Settings > Sync)
  3. Confirm the relay is reachable
  4. Restart the app

Still have questions?

Known Issues

Every honest product has a page like this; most hide it. Here is ours, in plain sight. If something you're hitting is listed below, it's a known limitation we're already working on — not something you broke. Check back after updating to see if it's gone.


Exchange

  • BLE exchange may fail on older Android devices — Bluetooth Low Energy exchange requires Android 12+ with BLE 5.0 support. On older devices, use QR exchange instead.
  • Audio proximity verification requires quiet environment — The ultrasonic proximity check can fail in noisy environments. This does not affect the security of the exchange, only the automatic proximity confirmation.

Sync

  • iOS background sync not yet available — On iOS, sync only runs when the app is in the foreground. Open the app periodically to receive contact updates. Android background sync works automatically.

Desktop

  • Linux Qt: some screens not yet implemented — A few secondary screens (backup scheduling, some settings panels) are still being wired on the Qt frontend.
  • Windows: device link dialog not yet available — Device linking on Windows works via QR code but lacks the confirmation dialog.

Reporting Issues

Found something not listed here?

!!! warning "Privacy reminder" Never include QR codes, key material, or contact card content in bug reports — these contain cryptographic data.

Contact Exchange

Adding a contact in Vauchi looks almost too simple — you hold up a code, they scan it, done. The simplicity is the achievement. Underneath that moment sits the thing every messaging app quietly struggles with: how do you know the person you just connected with is really them, and that nobody slipped into the middle? Vauchi's answer is wonderfully low-tech — you were both there.


How it works

The default exchange is a deliberate, two-way act between people in the same place:

 ┌─────┐               ┌─────────┐   
 │ You │               │ Contact │   
 └──┬──┘               └────┬────┘   
    │                       │        
    │     Show QR code      │        
    │───────────────────────▶        
    │                       │        
    │     Scan QR code      │        
    ◀───────────────────────│        
    │                       │        
    │┌────────────────────┐ │        
    ││ Proximity verified │ │        
    │└────────────────────┘ │        
    │                       │        
    │  Scan their QR code   │        
    │───────────────────────▶        
    │                       │        
    │┌────────────────────┐ │        
    ││ Exchange complete! │ │        
    │└────────────────────┘ │        
    │                       │        
┌──────────────────────────────┐     
│ Both have each other's cards │     
└──────────────────────────────┘     
    │                       │        
 ┌──┴──┐               ┌────┴────┐   
 │ You │               │ Contact │   
 └─────┘               └─────────┘   

Why in person?

Presence is a security feature you've trusted your whole life without calling it one. Standing in front of someone does, for free, what elaborate protocols strain to do:

ThreatHow being there defeats it
SpamStrangers can't add you from afar
ImpersonationYou're looking at who you're connecting to
Man-in-the-middleDevices talk directly; there's no middle
Screenshot scrapingProximity is checked, not just a picture

A connection you made in the room is one you meant to make. That intention is exactly what spam, bots, and impostors can't reproduce.

Ways to exchange

QR code — the one to start with

Works on every device, every time:

  1. Open the Exchange tab
  2. Show your QR code
  3. Have them scan it
  4. Scan theirs
  5. Connected

For security, a QR code expires after 5 minutes — long enough to introduce yourselves, short enough that a stale screenshot is worthless.

And, when you need them, others

QR is the dependable default, but it isn't the only door. Depending on your devices you may also exchange by tapping phones together, bumping them, or — when you simply can't be in the same room — by sharing a one-off Link (vauchi://exchange?…) that completes remotely through the relay over the next few days. The in-person methods give the strongest guarantee; Link mode trades a little of that for reach. The full menu lives in the Exchange Contacts guide.

Proximity verification

On iOS, Vauchi confirms you're actually together using sound your ears can't hear:

  • Both phones emit and listen for an ultrasonic handshake (18–20 kHz)
  • Range: roughly 3 metres
  • If it can't hear the other phone, it falls back to manual confirmation
  • This is what stops someone exchanging with a photo of your code instead of you

(Android proximity verification is planned; on desktop and CLI/TUI you simply confirm manually.)

If proximity won't verify (iOS)

  1. Check both phones have working speakers and microphones
  2. Move closer — within 2–3 metres
  3. Quieten the surroundings
  4. Disable anything that hijacks audio
  5. Try again, or just confirm manually when prompted

After the exchange

The moment it completes:

  • The new contact appears in your Contacts list
  • You see the fields they chose to share
  • They see the fields you chose to share
  • From here on, both cards keep themselves up to date

Security properties

PropertyMechanism
Proximity requiredUltrasonic handshake (iOS); manual confirm elsewhere
No man-in-the-middleX3DH key agreement bound to identity keys
Forward secrecyEphemeral keys discarded after exchange
Replay preventionOne-time token, 5-minute expiry
Card authenticityEd25519 signature on every card

Automatic Updates

Here's a small mystery of human behaviour: people rarely keep their contact details current, not because they don't care, but because the effort is all theirs and the benefit is all someone else's. Telling forty people you've changed your number is a chore; suffering one person's outdated entry is mildly annoying. So nobody updates anything, and we all quietly drift out of date.

Vauchi removes the chore entirely. You change your number once, for yourself, and everyone who holds your card simply has the new one. No broadcast, no "please update your records," no awkward text six months later. The address book finally maintains itself.


How it works

 ┌─────┐                   ┌───────┐              ┌───────────┐   ┌───────────┐                            
 │ You │                   │ Relay │              │ Contact 1 │   │ Contact 2 │                            
 └──┬──┘                   └───┬───┘              └─────┬─────┘   └─────┬─────┘                            
    │                          │                        │               │                                  
    ├───┐                      │                        │               │                                  
    │   │ Change phone number  │                        │               │                                  
    ◀───┘                      │                        │               │                                  
    │                          │                        │               │                                  
    │  Send encrypted update   │                        │               │                                  
    │──────────────────────────▶                        │               │                                  
    │                          │                        │               │                                  
    │                          ├───┐                    │               │                                  
    │                          │   │ Store for offline contacts         │                                  
    │                          ◀───┘                    │               │                                  
    │                          │                        │               │                                  
    │                          │  Deliver when online   │               │                                  
    │                          │────────────────────────▶               │                                  
    │                          │                        │               │                                  
    │                          │                        ├───┐           │                                  
    │                          │                        │   │ Decrypt, update your card                    
    │                          │                        ◀───┘           │                                  
    │                          │                        │               │                                  
    │                          │                        │          ┌─────────┐                             
    │                          │                        │          │ Offline │                             
    │                          │                        │          └─────────┘                             
    │                          │                        │               │                                  
    │                          │              Come online               │                                  
    │                          ◀────────────────────────────────────────│                                  
    │                          │                        │               │                                  
    │                          │        Deliver pending update          │                                  
    │                          │────────────────────────────────────────▶                                  
    │                          │                        │               │                                  
    │                          │                        │               ├───┐                              
    │                          │                        │               │   │ Decrypt, update your card    
    │                          │                        │               ◀───┘                              
    │                          │                        │               │                                  
    │                          │                  ┌──────────────────────────┐                             
    │                          │                  │ Both see your new number │                             
    │                          │                  └──────────────────────────┘                             
    │                          │                        │               │                                  
 ┌──┴──┐                   ┌───┴───┐              ┌─────┴─────┐   ┌─────┴─────┐                            
 │ You │                   │ Relay │              │ Contact 1 │   │ Contact 2 │                            
 └─────┘                   └───────┘              └───────────┘   └───────────┘                            

What gets pushed out

You do thisYour contacts see
Add a fieldIt appears (for those allowed to see it)
Edit a fieldThe new value, in place
Remove a fieldIt quietly disappears
Change visibilityIt appears or vanishes, per person

When it lands

When you're online. Updates go out in seconds; a contact sees the change the next time they open the app — near-instant when you're both active.

When a contact's offline. The encrypted update waits on the relay and is delivered the moment they reconnect. Nothing is lost in the gap.

If anyone's impatient. A pull-to-refresh, or Settings > Sync Now, fetches whatever's pending on demand.

Updates stay private

Every update is end-to-end encrypted, and — crucially — encrypted per contact, which is what lets the same change mean different things to different people:

The relay seesThe relay can't see
An encrypted blobThe field names
A rotating routing tokenThe field values
A timestampWho you are
Padded message sizeWhat actually changed

Visibility comes along for the ride

Updates obey your visibility settings automatically. Hide a field from someone and they never receive its updates; reveal it and they start. It's all per-contact, never global — which is the whole point:

ContactVisibilityWhat they get
FamilyPhone visibleYour new number
WorkPhone hiddenNothing at all
FriendPhone visibleYour new number

You changed one number. Three people got exactly what you'd intend each of them to have.

Forward secrecy, even here

Each update rides its own one-time key, derived through the Double Ratchet. Compromise a single key and you've exposed a single update — never the history, never the future.

Troubleshooting

A contact doesn't see my update. Check the field is actually visible to them; check you're online; give it a few seconds; ask them to refresh.

Updates feel slow. Both ends need a connection — confirm theirs as well as yours. Occasionally the relay has a hiccup. A manual sync (Settings > Sync Now) usually settles it.

One update seems stuck. Reopen the app, check connectivity, then edit and re-save the field to nudge it through.

Privacy Controls

You already do this. You give your mobile number to friends and a work email to colleagues; you'd mention your home address to a dinner guest but not to someone you met once at a conference. The same fact carries different value, and different risk, depending on who's asking. We manage this effortlessly in person and then, online, throw it all away — one profile, identical for everyone, take it or leave it.

Vauchi's privacy controls simply let the software catch up to the social intelligence you've had all along. One card, but each person sees the version that fits them.


How it works

Every field can be shown to or hidden from each contact, individually. That's the whole model — granular, and entirely yours.

Your Contact Card
┌────────────────────────────────────────────┐
│  Name: Alice Smith                         │
│                                            │
│  ┌─────────────────┬─────────┬─────────┐   │
│  │ Field           │ Family  │ Work    │   │
│  ├─────────────────┼─────────┼─────────┤   │
│  │ Personal Email  │   ✓     │   ✗     │   │
│  │ Work Email      │   ✓     │   ✓     │   │
│  │ Personal Phone  │   ✓     │   ✗     │   │
│  │ Work Phone      │   ✓     │   ✓     │   │
│  │ Home Address    │   ✓     │   ✗     │   │
│  └─────────────────┴─────────┴─────────┘   │
│                                            │
│  Family sees 5 fields, Work sees 2 fields  │
└────────────────────────────────────────────┘

Per-contact visibility

To change what a particular person sees:

  1. Go to Contacts
  2. Select the contact
  3. Find What They Can See
  4. Toggle fields on or off

Each field is simply Visible or Hidden for that contact. New fields start visible to everyone — a sensible default you can tighten the moment you add them, or any time after.

Labels — visibility without the tedium

Setting every field for every contact by hand would be exhausting, and exhaustion is the enemy of privacy: controls people don't use protect no one. Labels fix that by letting you decide once for a whole category of people.

  1. Create labels like Family, Work, Friends
  2. Assign contacts to them
  3. Set what each label sees
  4. Someone in two labels sees the union of both

Labels are your private organising tool — a shortcut that resolves to ordinary per-contact rules underneath. A worked example:

LabelWhat they see
FamilyEverything
WorkWork email, work phone
FriendsPersonal email, personal phone
AcquaintancesJust your name

Changing many at once

From the home screen, tap the visibility control beside any field:

  • Show to all — visible to every contact
  • Hide from all — hidden from everyone
  • Customise — pick contacts one by one

The quiet details that matter

  • No real-time peeking — changes reach a contact when they next open the app, not the instant you make them
  • No notifications — nobody is told when you hide something
  • It looks like removal — a hidden field is indistinguishable from a deleted one, which is the point
  • No history — contacts only ever see your card as it is now, never as it was

A few worked scenarios

Keep business and personal apart. A "Business" label for professional contacts: show work email, work phone, LinkedIn; hide personal phone and home address. Your two lives stay two.

An inner circle. A "Close Friends" label that sees everything, while everyone else sees a deliberately thinner card. Intimacy, by design.

Lend a detail, then take it back. Reveal a field to one person, finish whatever you needed it for, then hide it again — their access ends immediately. Sharing needn't be permanent to be useful.

Encryption

There's a counterintuitive idea at the heart of Vauchi: the most useful thing we can do with your data is arrange never to be able to read it. Capability you don't have is liability you can't suffer — we can't lose, leak, sell, or be compelled to hand over what we were never able to see. Encryption is how that promise is kept by mathematics rather than by our good intentions.


What's encrypted

Everything. In every state it's ever in:

DataEncrypted?Who can read it
Your contact cardYesYou + your contacts
Messages between devicesYesYour devices only
BackupYesYou only (with your password)
Data at rest (on device)YesYou only
Data in transitYesYou + the recipient only

How it works

Your identity

When you create your identity, your device generates:

  • A master seed — 256 random bits, the root every other key grows from
  • A signing key (Ed25519) — proves a message really came from you
  • An exchange key (X25519) — strikes a shared secret with each contact

None of these ever leave your device in the clear.

Exchanging contacts

When you exchange with someone, your two devices perform a small, ancient piece of cryptographic theatre — agreeing on a secret in the open that only the two of them end up knowing:

  1. You scan their QR code (it carries their public key)
  2. Both devices run X3DH key agreement
  3. A shared secret emerges that only the two of you possess
  4. Everything from then on is encrypted under it
┌─────────────────────────┐     ┌────────────┐
│                         │     │            │
│        Your Keys        │     │ Their Keys │
│                         │     │            │
└────────────┬────────────┘     └──────┬─────┘
             │                         │      
             │                         │      
             ├─────────────────────────┘      
             │                                
             ▼                                
┌─────────────────────────┐                   
│                         │                   
│    X3DH Key Agreement   │                   
│                         │                   
└────────────┬────────────┘                   
             │                                
             │                                
             │                                
             │                                
             ▼                                
┌─────────────────────────┐                   
│                         │                   
│                         │                   
│  Unique encryption key  │                   
│ (known only to you two) │                   
│                         │                   
└─────────────────────────┘                   

Updates between contacts

When you change your card:

  1. The update is encrypted separately for each contact
  2. Different people may receive different updates — that's your visibility settings doing their job
  3. Each message uses a fresh, single-use key (forward secrecy)
  4. The relay, as ever, sees only sealed blobs

Forward secrecy

Vauchi uses the Double Ratchet protocol — the same mechanism behind Signal. The idea is almost paranoid, in the good way:

  • Every message gets its own key
  • Each key is derived, used once, then destroyed
  • Compromise one key and you've compromised exactly one message — never the conversation
  • Today's keys cannot reach back and decrypt yesterday's messages

It treats every key as disposable, so that no single theft is ever worth much.

The algorithms

PurposeAlgorithmNotes
SigningEd25519Identity and authenticity
Key agreementX25519Shared secrets
Symmetric encryptionXChaCha20-Poly1305All data
Key derivationHKDF-SHA256Derives keys from seeds
Password KDFArgon2idProtects backups

Nothing here is home-rolled. It's built on well-known, audited Rust libraries. The signing and key-agreement crates (ed25519-dalek, x25519-dalek) were professionally audited by Trail of Bits; the encryption and KDF crates (chacha20poly1305, argon2) implement IETF-standardised algorithms. In cryptography, boring and reviewed beats clever and new every single time.

What the relay server sees

The relay routes your messages and understands none of them:

Relay seesRelay cannot see
Encrypted blobsMessage content
A daily-rotating routing tokenA stable identity
TimestampsWhat you changed
Padded message sizeWho you are — or your IP

Messages are padded to standard buckets (256 B, 512 B, 1 KB, 4 KB) so even the shape of your traffic gives nothing away, and requests arrive via an Oblivious HTTP gateway that strips your IP. The relay is told the absolute minimum required to be a courier, and not one byte more.

On your device

Your keys rest in whatever vault your operating system already trusts:

PlatformKey storage
iOSKeychain
AndroidKeyStore
macOSKeychain
WindowsCredential Manager
LinuxSecret Service (where available)

Backups

A backup is encrypted with your password — and your password alone:

  1. Key stretch: Argon2id, deliberately slow and memory-hungry, so brute force is expensive even with a fast machine
  2. Encryption: XChaCha20-Poly1305
  3. Result: without the password, the file is so much noise — to an attacker, and to us

Use a passphrase: four random words you'll remember beat a clever squiggle you won't.

Security properties, in one table

PropertyHow it's achieved
ConfidentialityXChaCha20-Poly1305
IntegrityAEAD authentication tags
AuthenticityEd25519 signatures
Forward secrecyDouble Ratchet, one-time keys
Break-in recoveryDH ratchet, ephemeral keys
Replay preventionPer-message nonces
Traffic-analysis resistanceMessage padding + OHTTP

Don't take our word for it

All of this is open source. Read the implementation, check the claims, report anything that smells wrong: gitlab.com/vauchi. "Verifiable" is a stronger word than "trusted."

What encryption can't do

Honesty matters more than reassurance, so: encryption protects the data, not the laws of physics or human nature. It does not cover —

  • What you choose to reveal: your name and the fields you make visible are visible on purpose
  • Physical access: someone holding your unlocked device is past the cryptography
  • Screenshots: a contact can photograph what you showed them
  • The brief window before secure delete finishes

No tool removes the need for judgement. It just makes good judgement sufficient.

Backup & Recovery

Because your device is your account, losing it would matter — so Vauchi gives you two quite different safety nets, and they're at their best when you set them up before you need them. A backup is the five-minute insurance policy you'll be grateful for on a bad day. Social recovery is the same trick humans have always used when documents fail: people who know you, vouching that you're you.

MethodWhen it's forWhat it needs
Encrypted backupA planned safety netYour backup file + password
Social recoveryEvery device lost, no backupA few trusted contacts to vouch

Encrypted backup

Make one

  1. Go to Settings > Backup
  2. Tap Export Backup
  3. Choose a strong password (it must pass the strength check)
  4. Confirm it
  5. Save the backup file somewhere safe

Important

  • Keep the backup file somewhere you'll find it (a password manager, a printed copy in a drawer).
  • Memorise the password — it genuinely cannot be recovered.
  • Backup file plus password equals your whole account. Guard them the way you'd guard the two halves of a safe combination.

What's inside

A full backup is exactly that — a complete copy of your account:

DataIncluded?
Your identity (the master seed your keys grow from)Yes
Your contactsYes
Your own cardYes
Your labels and groupsYes
Per-conversation forward-secrecy keysNo*

*Those short-lived keys are meant to be disposable — that's what gives you forward secrecy. After you restore, each secure channel simply re-establishes itself the next time you and a contact sync. You get your people back without dragging yesterday's throwaway keys along.

Restore from it

  1. Install Vauchi on the new device
  2. Choose Restore from Backup
  3. Provide your backup file
  4. Enter your password
  5. You're back

Your identity, contacts, card, and labels return intact, and secure channels re-form on the next sync.

How the backup is protected

  • Encryption: XChaCha20-Poly1305
  • Key stretch: Argon2id — deliberately slow and memory-hungry, so guessing the password is expensive even with serious hardware
  • Without the password: the file is meaningless noise

Reach for a passphrase, not a password: a handful of ordinary words strung together is both easier to remember and harder to crack than the clever tangle of symbols you'll have forgotten by Tuesday.

Social recovery

Lost every device and have no backup? This is the net beneath the net. Trusted contacts confirm your identity and migrate your contacts to a fresh one.

How it works

┌──────────────────┐                 ┌───────────┐     ┌───────────┐   ┌───────────┐   ┌───────┐                    
│ You (New Device) │                 │ Contact 1 │     │ Contact 2 │   │ Contact 3 │   │ Relay │                    
└─────────┬────────┘                 └─────┬─────┘     └─────┬─────┘   └─────┬─────┘   └───┬───┘                    
          │                                │                 │               │             │                        
          ├───┐                            │                 │               │             │                        
          │   │ Create recovery claim      │                 │               │             │                        
          ◀───┘                            │                 │               │             │                        
          │                                │                 │               │             │                        
          │  Meet in person, share claim   │                 │               │             │                        
          │────────────────────────────────▶                 │               │             │                        
          │                                │                 │               │             │                        
          │                                ├───┐             │               │             │                        
          │                                │   │ Verify it's really you      │             │                        
          │                                ◀───┘             │               │             │                        
          │                                │                 │               │             │                        
          │        Create voucher          │                 │               │             │                        
          ◀────────────────────────────────│                 │               │             │                        
          │                                │                 │               │             │                        
          │           Meet in person, share claim            │               │             │                        
          │──────────────────────────────────────────────────▶               │             │                        
          │                                │                 │               │             │                        
          │                 Create voucher │                 │               │             │                        
          ◀──────────────────────────────────────────────────│               │             │                        
          │                                │                 │               │             │                        
          │                   Meet in person, share claim    │               │             │                        
          │──────────────────────────────────────────────────────────────────▶             │                        
          │                                │                 │               │             │                        
          │                         Create voucher           │               │             │                        
          ◀──────────────────────────────────────────────────────────────────│             │                        
          │                                │                 │               │             │                        
          │                       Submit recovery proof (vouchers)           │             │                        
          │────────────────────────────────────────────────────────────────────────────────▶                        
          │                                │                 │               │             │                        
          │                                │                 │               │             ├───┐                    
          │                                │                 │               │             │   │ Verify vouchers    
          │                                │                 │               │             ◀───┘                    
          │                                │                 │               │             │                        
┌────────────────────────────────────┐     │                 │               │             │                        
│ Contacts migrated to new identity! │     │                 │               │             │                        
└────────────────────────────────────┘     │                 │               │             │                        
          │                                │                 │               │             │                        
┌─────────┴────────┐                 ┌─────┴─────┐     ┌─────┴─────┐   ┌─────┴─────┐   ┌───┴───┐                    
│ You (New Device) │                 │ Contact 1 │     │ Contact 2 │   │ Contact 3 │   │ Relay │                    
└──────────────────┘                 └───────────┘     └───────────┘   └───────────┘   └───────┘                    

It guards both doors at once: you can't be locked out (your friends can let you in), and an impostor can't walk in (they'd have to fool several of your friends, in person, at once).

Start recovery

  1. Install Vauchi on the new device
  2. Create a new identity
  3. Go to Settings > Recovery
  4. Tap Recover Old Identity
  5. Enter your old public ID
  6. A recovery claim is generated

Collect vouchers

For each one:

  1. Meet the contact in person
  2. Share your recovery claim
  3. They confirm it's really you (they're looking right at you)
  4. They create a voucher
  5. They send it to you

What it takes

  • Vouchers from a small threshold of contacts (a handful, by default)
  • Each must have previously exchanged with your old identity
  • Together they prove your real social network recognises the request

Finish

  1. Import the vouchers
  2. Vauchi submits the recovery proof
  3. The network verifies through mutual connections
  4. Your identity settles onto the new device

Social recovery mints a new cryptographic identity — your old signing keys stay lost, and a few settings (like per-contact visibility) may need re-tuning. You keep your people; you replace the lock.

Vouching for someone else

If a contact asks you to vouch:

  1. Go to Settings > Recovery
  2. Tap Help Someone Recover
  3. Paste their recovery claim
  4. Verify it's really them — call them, or meet in person
  5. Create a voucher
  6. Send it over

Warning

Only vouch when you are certain who you're vouching for. The whole defence against identity theft is that you check. A careless voucher is the one an attacker is counting on.

Good habits

Before you ever need it: make a backup the day you set up; store it somewhere safe; use a memorable passphrase; and keep enough trusted contacts (five or so) that a couple being unreachable won't sink you.

When the day comes: try backup restore first — it's faster and simpler. Fall back to social recovery only if you must, do the vouching in person, and don't rush. Verifying carefully is the point, not the obstacle.

Troubleshooting

Forgot the backup password. It can't be recovered — that's the guarantee working as designed. Fall back to social recovery, or to another linked device if you still have one; failing both, start a new identity and re-exchange.

Can't reach enough contacts. See who's still reachable, give it a little time if some are temporarily away, and treat a new identity as the last resort.

A voucher was rejected. Usually it's for the wrong identity, corrupted, or expired (90 days). Ask the contact to issue a fresh one.

Multi-Device Sync

A single device holding your only identity is a single point of failure — and single points of failure have a way of failing at the worst moment. A second device changes the whole risk calculation: lose your phone and it's an inconvenience, not a catastrophe. One identity, several windows onto it, kept quietly in step.


How it works

Every device you link shares the same identity. A change on one shows up on all of them.

┌─────────────────┐                               
│  Your Identity  │                               
│                 │                               
│                 │                               
│ ┌─────────────┐ │                               
│ │             │ │                               
│ │ Master Seed ├─┼───┬────┐                      
│ │             │ │   │    │                      
│ └──────┬──────┘ │   └────┼──────────────┐       
│        │        │        │              │       
└────────┼────────┘        │              │       
         │                 │              │       
         │                 │              │       
         │                 │              │       
┌────────┼─────────────────┼──────────────┼──────┐
│        │           Devices              │      │
│        │                 │              │      │
│        ▼                 ▼              ▼      │
│ ┌─────────────┐     ┌────────┐     ┌─────────┐ │
│ │             │     │        │     │         │ │
│ │    Phone    │     │ Tablet │     │ Desktop │ │
│ │             │     │        │     │         │ │
│ └─────────────┘     └────▲───┘     └────▲────┘ │
│                          │              │      │
└──────────────────────────┼──────────────┼──────┘
                           │              │       
                           │              │       
                           │              │       
  ┌─────────────┐          │              │       
  │             │          │              │       
  │      R      │◄─────────┴──────────────┘       
  │             │                                 
  └─────────────┘                                 

You'll need: your existing device set up, the new device with Vauchi installed, and both online.

  1. On the existing device, go to Settings > Devices
  2. Tap Link New Device
  3. A QR code appears — valid for 5 minutes
  4. On the new device, install Vauchi
  5. Choose Join Existing Identity
  6. Scan the QR (or paste the data string on desktop/CLI)
  7. Check the confirmation code matches on both screens
  8. Confirm

Both devices now share your identity and sync on their own.

The confirmation code

Both devices show a 6-digit code (e.g. 123-456), derived cryptographically from the shared link data — only the two devices in the link can compute it. Matching codes mean nobody slipped into the middle. It takes a second to check and closes the one gap a network attacker might hope for; check it.

How many devices

  • Maximum: 10 per identity — more than most people own, few enough to keep the circle of trust small
  • Minimum: 1

Need an 11th? Revoke one first.

Platform support

PlatformLink (generate)Join (scan/paste)Manage devices
iOSPlannedPlannedPlanned
AndroidPlannedPlannedPlanned
DesktopYesYes (paste)Yes
TUIYesPlannedYes
CLIYesYesYes

Managing devices

See what's linked. Settings > Devices lists every device — name, platform, and status — with your current one marked.

Revoke a device that's lost, stolen, or retired:

  1. On another linked device, go to Settings > Devices
  2. Find the one to remove
  3. Tap Revoke and confirm

Warning

You can't revoke the device you're holding — use another linked device to cut off a lost one. (Which is itself a quiet argument for having a second device.)

A revoked device loses access instantly, can no longer send or receive updates, and can't be re-linked without starting fresh.

What syncs, and how

Changes sync automatically when you're online, end-to-end encrypted, with the relay none the wiser. Offline changes catch up when you reconnect.

DataSyncs?
Your contact cardYes
Your contactsYes
Visibility settingsYes
App preferencesYes
Device-specific settingsNo

Timing: near-instant when both devices are online; a pull of anything pending whenever you open the app; and a manual Settings > Sync Now when you're impatient.

Moving to a new phone

Link it (recommended). Link the new phone as a device, let it sync, then optionally revoke the old one. This preserves each device's own keys and hands over cleanly.

Or restore from backup. Create an encrypted backup on the old phone; restore it on the new one. Ideal when the old phone is already gone.

Troubleshooting

Sync isn't working. Check connectivity on both devices, make sure the app is open on each, try a manual sync, and confirm the device wasn't revoked.

A device won't appear. Give sync a few minutes, restart the app on both ends, make sure the link code hasn't expired (5 minutes), and generate a fresh one if needed.

Security

  • Each device holds its own keys, derived from your master seed
  • Revoking a device invalidates its keys immediately
  • The relay never sees plaintext
  • Device-to-device traffic is end-to-end encrypted
  • Confirmation codes block man-in-the-middle attempts during linking

How to Exchange Contacts

Step-by-step guide for exchanging contact cards with other Vauchi users.

A handshake is a security check we stopped noticing centuries ago: you confirm someone is real, present, and willing, all in one gesture. A Vauchi exchange is the same idea wearing newer clothes. The simplest version — and the one this guide walks through — is two phones showing each other a QR code while you stand close enough to read them.


Prerequisites

  • Both you and the other person have Vauchi installed
  • For the in-person QR exchange below: both devices have working cameras
  • You don't strictly need both at once — see Other ways to exchange if you're not in the same room

QR Code Exchange

Both people show and scan each other's QR codes. Every exchange uses fresh keys, so the past stays private even if a key later leaks (forward secrecy). The QR itself carries only the person's public key and a one-time token — nothing secret, and it goes stale after five minutes.

Step 1: Open Exchange

  1. Open Vauchi
  2. Tap the Exchange tab at the bottom

Step 2: Show Your QR Code

  1. Tap Show My QR Code
  2. A QR code appears on your screen
  3. Show it to the other person

Tip

Keep your screen brightness up and hold steady for easier scanning.

Step 3: They Scan Your Code

  1. The other person points their camera at your QR code
  2. Their device confirms a successful scan

Step 4: Scan Their Code

  1. Tap Scan QR Code
  2. Point your camera at their QR code
  3. Wait for the scan to complete

Step 5: Confirm

Both devices show "Exchange Successful"

You now have each other's contact cards.

Info

On iOS, "physically together" isn't taken on trust: the two devices play a brief ultrasonic handshake (~18–20 kHz, audible to the dog and no one else) to confirm they're within about three metres. If the tone doesn't carry, it falls back to a manual confirmation tap. Android proximity checking is planned; desktop, CLI, and TUI confirm manually.


Other ways to exchange

QR is the default because it works on every platform and needs nothing but a camera. But proximity has more than one shape, and Vauchi offers a few:

ModeHow it worksBest for
QR (default)Show and scan codesAnywhere, any device
TapHold phones together (NFC)Quick face-to-face swaps
BumpBump or shake the devices (BLE + motion)Crowded rooms, no line of sight
LinkShare a one-off vauchi://exchange?… URLPeople who aren't in the room

The first three all prove you were in the same place at the same time, which is the strongest defence against a stranger quietly inserting themselves into the middle of your exchange. Link mode relaxes that proof in return for reach: you send a single-use URL, and the exchange finishes asynchronously through the relay over the following days. It's the difference between a handshake and a sealed letter — both work, one knows the room was empty.


Troubleshooting

QR Code Won't Scan

  1. Lighting: Make sure the QR code is well-lit
  2. Stability: Hold both devices steady
  3. Distance: Try moving closer or farther
  4. Clean lens: Wipe your camera lens
  5. Refresh: Generate a new QR code (they expire after 5 minutes)

Exchange Keeps Failing

  1. Check internet connectivity on both devices
  2. Ensure the QR code hasn't expired (5-minute limit)
  3. Restart the app on both devices
  4. Try a fresh QR code

After Exchange

Once exchange completes:

  • They appear in your Contacts list
  • You can see their card (fields they've shared)
  • They can see your card (fields you've shared)
  • Future changes sync automatically

Next Steps


Security Notes

  • QR codes expire after 5 minutes (replay protection)
  • Both parties must scan each other's QR codes (mutual verification)
  • Each exchange uses fresh ephemeral keys (forward secrecy)
  • Exchange uses encrypted key agreement
  • The relay is a blind courier: it forwards encrypted blobs, routes them with daily-rotating mailbox tokens rather than your identity, and never learns your IP address (Oblivious HTTP). It carries the envelope; it cannot read the letter.

For more on security, see Encryption.

How to Manage Visibility

Step-by-step guide for controlling what each contact can see.


Overview

A paper business card has exactly one privacy setting: whoever holds it sees all of it. Vauchi splits that decision down to the field. Every field on your card is, for every contact, one of three things: visible to everyone, visible to a chosen few, or hidden from all. Show your work email to colleagues, your personal phone to friends, hide your home address from everyone else — and change your mind later without reprinting anything.

The rule that matters: visibility is per field, per contact. Everything else in this guide is shortcuts on top of that.


Change What One Contact Sees

Step 1: Open Contact

  1. Go to Contacts
  2. Tap on the contact you want to modify

Step 2: Find Visibility Settings

  1. Scroll down to "What They Can See"
  2. You'll see a list of all your fields

Step 3: Toggle Fields

  • Enabled (green): They can see this field
  • Disabled (gray): They cannot see this field

Tap any field to toggle it.

Step 4: Confirm

Your choice is saved at once, but the contact's copy isn't updated until they next sync or open the app. Visibility is a change to the card you publish, not a live feed into their phone — so "immediately" means immediately for you, and "soon" for them.


Show/Hide a Field for Everyone

Step 1: Go to Your Card

  1. Go to Home
  2. Find the field you want to change

Step 2: Open Visibility Menu

  1. Tap the visibility icon (eye) next to the field
  2. A menu appears

Step 3: Choose Option

  • Show to all: Makes the field visible to all contacts
  • Hide from all: Hides the field from all contacts
  • Customize: Opens per-contact toggles

Using Labels

Setting visibility one contact at a time is honest but tedious, like addressing wedding invitations by hand. Labels — Work, Family, Friends — let you decide once for a category. But they aren't a separate permission system bolted on the side: a label is just a faster way to write the same per-contact rules. Under the hood, "show this to Family" expands to "show this to Alice, Bob, and Mum." Move someone out of Family and their access updates with them; the rule was always about the people, never the badge.

Creating a Label

  1. Go to Settings > Labels
  2. Tap Add Label
  3. Enter a name (e.g., "Work", "Family", "Friends")
  4. Tap Create

Assigning Contacts to Labels

  1. Open a contact
  2. Scroll to Labels
  3. Tap to assign/unassign labels

Setting Visibility by Label

  1. Go to Home
  2. Tap the visibility icon next to a field
  3. Tap Customize
  4. Switch to the Labels tab
  5. Toggle labels on/off

Example: Enable "Family" and "Friends", disable "Work" for your personal phone.


Common Configurations

Business Card Mode

Share only professional information:

FieldVisibility
Work EmailAll
Work PhoneAll
Personal EmailNone
Personal PhoneNone
Home AddressNone

Close Friends

Share everything with trusted contacts:

  1. Create a "Close Friends" label
  2. Assign trusted contacts
  3. Show all fields to "Close Friends"
  4. Restrict fields for everyone else

Temporary Sharing

Share a field temporarily:

  1. Show the field to a specific contact
  2. Complete whatever you needed
  3. Hide the field again

Hiding takes effect on their side the next time they sync — quick, but not instant, so don't treat "hide" as a remote wipe of something they may already have copied down.


Checking What Someone Sees

Step 1: Open Contact

  1. Go to Contacts
  2. Tap on the contact

Step 2: Review Visible Fields

Look at "What They Can See":

  • Enabled fields = they see these
  • Disabled fields = they don't see these

Summary View

At the bottom of the contact, you'll see:

"Alice can see 3 of your 7 fields"


Default Visibility

For New Fields

When you add a new field, it's hidden by default. Nobody sees it until you choose to share it.

To share it:

  1. Add the field
  2. Tap the visibility icon
  3. Choose who can see it

For New Contacts

When you exchange with someone new, they see only the fields you've made visible to "everyone".


Troubleshooting

Contact Still Sees Hidden Field

  1. Changes sync when they open the app
  2. Ask them to refresh their contacts
  3. Wait a few minutes for sync

Can't Find Visibility Options

  1. Make sure you're on the contact's detail page
  2. Scroll down — visibility is below their card info
  3. If missing, update the app

Label Changes Not Applying

  1. Make sure contacts are assigned to the label
  2. Check the label visibility settings
  3. Try removing and re-adding the label

Tips

Be Intentional

  • Review visibility when adding new fields
  • Periodically audit what each contact sees
  • Use labels to stay organized

Think in Categories

Group contacts by relationship type:

  • Work: Professional info only
  • Family: Everything
  • Acquaintances: Name and email only

Start Restrictive

It's easier to show more later than to hide after sharing.


Privacy Notes

  • Hidden fields simply vanish from their view — and a hidden field looks exactly like one you never had. The contact can't tell "she's hiding her address" from "she never listed an address." Absence carries no information.
  • No notification fires when you hide a field. The polite cough doesn't happen; the field is just gone next time they look.
  • Contacts only ever see your current card — never its history. There's no changelog of what you showed and then withdrew.
  • They can't see your visibility settings, only their own resulting view.
  • Hiding is per field, per contact — it withdraws access, it doesn't delete the field from your card.

For more on privacy, see Privacy Controls.

How to Recover Your Account

Step-by-step guide for restoring access to your Vauchi identity.

Self-custody is a marvellous thing right up until the afternoon you're locked out. Vauchi holds nothing on your behalf — there's no "forgot password" email, because there's no one on the other end who could honour it. So recovery isn't a single button; it's whichever of these you prepared for in advance. Pick the highest row in the table you can satisfy.


Choose Your Recovery Method

SituationMethodTime Required
Have backup file + passwordBackup Restore5 minutes
Have another linked deviceDevice Link5 minutes
Lost everythingSocial RecoveryHours to days

Backup Restore

If you have your encrypted backup and its password:

Step 1: Start Fresh

  1. Install Vauchi on your new device
  2. On the welcome screen, tap Restore from Backup

Step 2: Enter Backup

  1. Paste your backup code (the long string of characters)
  2. Tap Next

Step 3: Enter Password

  1. Enter your backup password
  2. Tap Restore

Step 4: Wait for Sync

  1. Vauchi restores your identity, contacts, your own card, and your labels straight from the backup
  2. Per-contact forward-secrecy state isn't in the file; it quietly re-establishes itself on the next sync with each contact
  3. Within minutes, you should see your contacts

Success

You're back! Your identity is fully restored.

Info

The backup is sealed with your password and nothing else. Vauchi stretches that password with Argon2id (deliberately slow, to make guessing expensive) and then encrypts everything with XChaCha20-Poly1305. The flip side of that strength: a forgotten password is unrecoverable, by design. No key escrow means no back door — for you or anyone else.


If you have another device still logged in:

Step 1: On Your Working Device

  1. Open Vauchi
  2. Go to Settings > Devices
  3. Tap Link New Device
  4. A QR code appears

Step 2: On Your New Device

  1. Install Vauchi
  2. Tap Join Existing Identity
  3. Scan the QR code

Step 3: Revoke Lost Device (Optional)

If your old device was lost or stolen:

  1. On your working device, go to Settings > Devices
  2. Find the lost device
  3. Tap Revoke

Success

You're back! Your new device is now synced.


Social Recovery

If you've lost all devices and don't have a backup:

Overview

Long before passwords, identity was vouched for by people who knew you — and that older system never forgot a face. Social recovery makes it cryptographic. A handful of contacts who once exchanged with your old identity, in person, confirm that you are still you.

Two things to understand before you start:

  • The number of vouchers needed is a small, configurable threshold — a few by default, not a magic "exactly 3." Treat "round up a handful" as the plan.
  • Social recovery rebuilds your relationships, not your old keys. It mints a new cryptographic identity; the old signing keys stay lost for good. Most things carry over, but some settings — visibility rules in particular — may want re-tuning afterwards.

Step 1: Create New Identity

  1. Install Vauchi on a new device
  2. Create a new identity (fresh start)
  3. This gives you a new device to work from

Step 2: Start Recovery

  1. Go to Settings > Recovery
  2. Tap Recover Old Identity
  3. Enter your old public ID (if you know it)
    • If you don't know it, ask a contact — they can find it in your contact details
  4. A recovery claim is generated

Step 3: Collect Vouchers

For each voucher, you need to meet a contact in person:

  1. Meet in person (physical presence required)
  2. Show them your recovery claim
  3. They open Settings > Recovery > Help Someone Recover
  4. They paste your claim
  5. They verify it's really you
  6. They tap Create Voucher
  7. They share the voucher with you

Repeat until you reach the threshold (a handful by default).

Tip

Ask contacts you've met in person and who will recognize you immediately.

Step 4: Submit Recovery

  1. Go to Settings > Recovery
  2. Import each voucher you received
  3. Once you've reached the threshold, tap Complete Recovery
  4. Vauchi submits your recovery proof

Step 5: Wait for Verification

  1. The relay verifies your vouchers
  2. Other contacts may verify via mutual connections
  3. Once verified, your identity transitions

Step 6: Re-Exchange (If Needed)

Some contacts may need to re-verify you:

  1. They'll see your card update through recovery
  2. Meet them in person to confirm
  3. Your relationship continues

Success

You're back! Your identity has been recovered.


Helping Someone Else Recover

If a contact asks you to vouch for their recovery:

Step 1: Verify Their Identity

Before creating a voucher:

  • Meet in person if possible
  • Confirm they are who they claim to be
  • Be suspicious of unusual requests

Warning

Only vouch if you're certain. A voucher is you putting your name on "yes, this is really them" — false vouching is how identity theft gets in the door.

Step 2: Create Voucher

  1. Go to Settings > Recovery
  2. Tap Help Someone Recover
  3. Paste their recovery claim
  4. Tap Create Voucher

Step 3: Share Voucher

  1. Copy the voucher
  2. Send it to them (AirDrop, messaging, etc.)

Troubleshooting

Backup Restore: "Invalid Password"

  • Check for typos
  • Passwords are case-sensitive
  • Try any variations you might have used

If you truly can't remember the password, there's no recovering the backup — that's the design, not a bug. Fall back to social recovery.

Backup Restore: "Invalid Backup Code"

  • Make sure you copied the entire code
  • Check for extra spaces or line breaks
  • Try copying again from the original source

Social Recovery: "Not Enough Vouchers"

  • You haven't reached the threshold yet (a handful by default)
  • Round up more contacts who exchanged with your old identity
  • Vouchers must come from different contacts

Social Recovery: "Voucher Rejected"

  • The voucher may be for a different identity
  • The voucher may have expired (vouchers go stale after about 90 days)
  • Ask the contact to create a fresh one

Can't Remember Old Public ID

  • Ask any contact who had your old card
  • They can find your ID in your contact details
  • Look through old screenshots or notes

Prevention Tips

A backup you make today is a favour to a future, locked-out version of yourself. The cheapest recovery is the one you never need.

  1. Create a backup as soon as you set up
  2. Store backup securely (password manager, safe)
  3. Link multiple devices (phone + tablet/desktop)
  4. Remember your password (use a passphrase — long beats cryptic)
  5. Stay in touch with a handful of contacts who could vouch for you

Security Notes

  • Social recovery requires in-person verification — presence is the proof
  • Needing several independent vouchers prevents any single person from impersonating you
  • Vouchers expire after about 90 days, so a stolen one doesn't stay useful
  • A backup is yours alone: Argon2id + XChaCha20-Poly1305, no escrow, no back door
  • The relay only ever forwards encrypted blobs, routed by daily-rotating mailbox tokens — it never sees your identity, your IP, or your data

For more on security, see Backup & Recovery Feature.

How to Set Up Multi-Device

Step-by-step guide for using Vauchi on multiple devices.

Adding a device is less like copying a file and more like introducing two people who then keep each other up to date. The introduction has to happen once, in a way no eavesdropper can fake — after that, your phone and your laptop hold the same identity and sync between themselves. This guide covers the introduction, and what to do when a device is lost.


Prerequisites

  • Your existing device with Vauchi set up
  • A new device with Vauchi installed (but not set up)
  • Both devices have internet connectivity

Linking a New Device

On your existing device:

Mobile (iOS/Android):

  1. Open Vauchi
  2. Go to Settings (gear icon)
  3. Tap Devices
  4. Tap Link New Device
  5. A QR code appears (valid for 5 minutes)

Desktop:

  1. Open Vauchi Desktop
  2. Go to Devices (from the sidebar)
  3. Click Link New Device
  4. A QR code and data string appear (valid for 5 minutes)

TUI:

  1. Open Vauchi TUI
  2. Press d to go to Devices
  3. Press l to generate a link
  4. A QR code and data string appear in an overlay

CLI:

vauchi device link

A QR code and data string are displayed in the terminal.

Step 2: Join on New Device

On your new device:

Mobile (iOS/Android):

  1. Open Vauchi
  2. On the welcome screen, tap Join Existing Identity
  3. Point your camera at the QR code from Step 1
  4. Verify the confirmation code matches on both devices
  5. Wait for the linking to complete

Desktop:

  1. Open Vauchi Desktop
  2. On the setup screen, click Join Existing Identity
  3. Paste the data string from the existing device
  4. Verify the confirmation code matches on both devices
  5. Click Confirm to complete linking

CLI:

vauchi device join <data-string>

Then on the existing device, pass the encrypted request data from the new device:

vauchi device complete <request-data>

That six-digit code is doing real work

Both devices show a short confirmation code like 123-456. It isn't a password or a formality — each device computes it independently from the keys they just agreed on, so the numbers can only match if the two devices really negotiated with each other and nobody slipped into the middle. Matching codes are your proof of no man-in-the-middle. If they differ, stop and start over.

Step 3: Confirm

Both devices should show:

  • Your existing device: "Device linked successfully"
  • Your new device: "Welcome back, [Your Name]"

Your new device is now synced with your identity.


Verifying Setup

After linking:

On New Device

  1. Go to Contacts — your contacts should appear
  2. Go to Home — your contact card should appear
  3. Go to Settings > Devices — both devices should be listed

On Existing Device

  1. Go to Settings > Devices
  2. You should see both devices listed
  3. Your new device shows its platform and last sync time

Syncing Data

Sync is request-and-response, not a live broadcast — each device asks the relay for what's waiting and sends what's new. So changes land near-instantly when both devices are online, and catch up the moment a sleeping device wakes. Throughout, the relay only ever handles end-to-end encrypted blobs; it never sees your plaintext.

  • Near-instant: When both devices are online
  • On app open: When you open the app
  • Manual: Pull to refresh or Settings > Sync Now

What Syncs

DataSyncs?
Your contact cardYes
Your contactsYes
Visibility settingsYes
App preferencesYes

Managing Devices

Viewing All Devices

Mobile/Desktop: Go to Settings > Devices to see all linked devices. Your current device is marked.

TUI: Press d to open the Devices screen. Navigate with j/k or arrow keys. Current device is marked [this device].

CLI:

vauchi device list

Revoking a Device

If a device is lost, stolen, or no longer needed:

Mobile/Desktop:

  1. Go to Settings > Devices on another device
  2. Find the device to revoke
  3. Tap Revoke
  4. Confirm by tapping Revoke Device

TUI:

  1. Press d to open Devices
  2. Navigate to the device with j/k
  3. Press r to revoke
  4. Press y to confirm

CLI:

vauchi device revoke <device-id>

Warning

You can't revoke the device in your hand — a chair can't be pulled out from under the person sitting in it. Revoke from another linked device.

Danger

Revocation is immediate and permanent. The revoked device loses all access.


Troubleshooting

QR codes are valid for 5 minutes. If expired:

  1. On your existing device, generate a new link code
  2. Scan or paste the new code quickly

New Device Not Syncing

  1. Check internet on both devices
  2. Wait a few minutes for initial sync
  3. Pull to refresh on the new device
  4. Check Settings > Sync for last sync time

"Too Many Devices" Error

Ten is the ceiling — generous, but a ceiling. An eleventh device has to wait for a seat:

  1. Go to Settings > Devices
  2. Revoke a device you no longer use
  3. Try linking the new device again

Migrating to a New Phone

  1. Keep your old phone accessible
  2. Follow the steps above to link your new phone
  3. Wait for sync to complete
  4. Optionally, revoke your old phone

This is the cleanest migration path.

Option 2: Backup & Restore

If you can't access your old phone:

  1. Restore from an encrypted backup
  2. See How to Recover Your Account

Security Notes

  • Each device has its own derived keys
  • Revoking a device invalidates its keys immediately
  • Up to 10 devices per identity; revoke one to make room for an eleventh
  • The relay only forwards end-to-end encrypted data, routed by daily-rotating mailbox tokens — never your plaintext, identity, or IP
  • Link codes expire after 5 minutes
  • A 6-digit confirmation code, computed independently on both devices, proves you're linking the right two — no man-in-the-middle

For more on security, see Multi-Device Feature.

About Vauchi

Privacy-focused updatable contact cards via in-person exchange.


What We're Building

Vauchi is a living contact card you exchange face to face — a small, deliberate moment, like a handshake. After that, when your details change, everyone who holds your card sees the update automatically. No reminders to send, no "I got a new number" group text.

The relationship is yours and the person's you met. No one else is in the room.

Unlike traditional contact apps:

  • Meet once, stay in touch — exchange in person; it keeps itself current
  • No sign-up, no phone number — your device is your identity
  • Yours alone — end-to-end encrypted, so only you and your contacts can read it
  • Open source — verify every claim yourself

Why We Built It

Your address book quietly rots. You change jobs, move cities, get a new number — and one by one, the people who matter drift out of reach. The usual fix is to let a platform — a social network, a messaging app — be the keeper of who you know. That works, but the price is handing your relationships to a company.

We think the moment you meet someone is worth protecting. So Vauchi keeps your details current for the people you've actually met, and keeps everyone else — including us — out of it.

Learn More

Open Source

Vauchi is open source under GPL-3.0-or-later. You can:

  • Inspect the code
  • Verify security claims
  • Contribute improvements
  • Run your own relay server

GitLab: https://gitlab.com/vauchi GitHub Mirror: https://github.com/vauchi

Contact

  • Issues: GitLab Issues
  • Email: hello@vauchi.app
  • Security: security@vauchi.app

Vauchi Principles

The single source of truth for core principles and philosophy.

All solutions must be validated against these principles before implementation.


Core Value Statement

Vauchi is built on five interlocking commitments:

1. Privacy is a right, not an option

All design starts with: "How would we build this if users were our only concern?"

  • E2E encryption for all communications
  • Oblivious privacy-preserving relay (sees only encrypted blobs)
  • No tracking, analytics, or telemetry
  • User owns and controls their data

2. Trust is earned in person

Human recognition is the security anchor, not passwords or platforms.

  • QR exchange with physical proximity verification for full trust; opt-in remote discovery at reduced trust
  • No accounts or registration (device IS the identity)
  • Social vouching for recovery (people you've actually met)
  • No trust-on-first-use for contact verification — you verify contacts in person. Relay server identity is pinned during contact exchange. No platform-mediated relationships

3. Quality comes from rigorous methodology

Confidence through discipline, not hope.

  • Test-Driven Development (TDD) is mandatory
  • Problem-first workflow with full traceability
  • Threat modeling drives security decisions
  • No hacks, no tech debt, no ignored tests

4. Simplicity serves the user

Vauchi respects your time and attention — it does one thing well and stays out of your way.

  • No engagement tricks, no notifications designed to pull you back
  • Clear, minimal interface — useful without a learning curve
  • Features earn their place by solving real problems, not adding complexity
  • The app is a tool, not a destination

5. Beauty adapts to the user

Simplicity and beauty go hand in hand — and beauty is personal.

  • Design that feels good without demanding attention
  • Theming and customisation let users make it their own
  • Aesthetic choices serve clarity, never compete with it
  • A beautiful tool is one that fits the person using it

Principle Categories

Privacy Principles

PrincipleStatement
User OwnershipData stored locally, encrypted, user-controlled
Oblivious RelayRelay sees only encrypted blobs, not content
No HarvestingNo analytics, telemetry, or tracking
No SharingData never shared with third parties
Selective VisibilityPer-field, per-contact visibility control

Security Principles

PrincipleStatement
Proximity Full TrustQR + BLE/ultrasonic; remote restricted
Audited Crypto OnlyRustCrypto audited crates; no custom crypto
Forward SecrecyDouble Ratchet; past messages safe if keys leak
Memory SafetyRust enforced; no unsafe in crypto paths
Defense in DepthMultiple layers: encryption, signing, verification

Technical Principles

PrincipleStatement
TDD MandatoryTidy, Red, Green, Refactor. Test first. No exceptions
90%+ CoverageFor vauchi-core; real crypto in tests (no mocking)
Rust CoreMemory safety, no GC, cross-platform compilation
Clean Depsvauchi-core standalone; downstream uses git deps
Gherkin Tracefeatures/*.feature files drive test writing

UX Principles

PrincipleStatement
Complexity HiddenUsers see "scan QR, contact added"
In-Person TrustHuman recognition is the security anchor
Local-FirstData on device; queues offline, syncs on connect
Portable IdentityNo lock-in; restore from backup, switch devices
Cross-PlatformSame experience on iOS, Android, desktop

Process Principles

PrincipleStatement
Problem-FirstEvery task starts as a problem
Artifacts AccumulateInvestigations and retrospectives attached
No Wasted RejectionsArchive rejected solutions with reasoning
Small Atomic CommitsAfter each green, after each refactor
Retrospective RequiredLearn from every completed problem

Using These Principles

For Solution Validation

When evaluating a proposed solution, check:

  1. Does it align with Core Principles? (Privacy, Trust, Quality, Simplicity, Beauty)
  2. Does it fit the Culture? (Process Principles)
  3. Is it compatible with Current Implementation? (Technical Principles)
  4. Does it support existing Features? (UX Principles)

If a solution conflicts with any principle, it must be rejected with documented reasoning.

For Decision Making

When facing a design decision:

  1. Start with the user's perspective
  2. Assume adversarial conditions (what could go wrong?)
  3. Choose the option that best upholds all five core values
  4. Document the decision and rationale

For New Contributors

Read these principles before contributing. They are non-negotiable. If you disagree with a principle, open a problem record to discuss changing it—don't ignore it.


Amending Principles

Principles can be amended, but only through the Problem Workflow:

  1. Create a problem record explaining why the principle should change
  2. Investigate impact across codebase and documentation
  3. Validate the change against remaining principles
  4. Implement with full retrospective

Principles are not immutable, but changes must be deliberate and documented.

Security

How Vauchi protects your data.


Security Model

Vauchi is designed with the assumption that everything outside your device is hostile:

  • Relay server: Assumed compromised
  • Network: Assumed monitored
  • Other devices: Verified through in-person exchange

Despite these assumptions, your data stays private because of end-to-end encryption.

How We Protect You

End-to-End Encryption

All communication is encrypted so only you and your contacts can read it:

DataEncryption
Contact cardsXChaCha20-Poly1305
MessagesXChaCha20-Poly1305 with Double Ratchet
BackupsXChaCha20-Poly1305 with Argon2id KDF
Local storageXChaCha20-Poly1305

The relay server only sees encrypted blobs. It cannot:

  • Read your contacts
  • See your card fields
  • Decrypt any messages
  • Link your identity to your data

In-Person Verification

Contact exchange requires physical presence:

  • QR codes contain cryptographic identity
  • Proximity verification via ultrasonic audio
  • No trust-on-first-use for contact verification — you verify contacts in person. Relay server identity is pinned during contact exchange.

This prevents spam, impersonation, and man-in-the-middle attacks.

Modern Cryptography

Vauchi uses battle-tested cryptographic libraries:

PurposeAlgorithmLibrary
SigningEd25519ed25519-dalek
Key exchangeX25519x25519-dalek
Symmetric encryptionXChaCha20-Poly1305chacha20poly1305
Password KDFArgon2idargon2
Key derivationHKDF-SHA256hkdf

All libraries are:

  • Written in Rust (memory-safe)
  • Well-known, widely used in production

Forward Secrecy

Each message uses a unique key derived via Double Ratchet:

  • Keys are used once then deleted
  • Even if one key is compromised, other messages stay safe
  • Past messages can't be decrypted with current keys

Threat Model

ThreatMitigation
Server compromiseE2E encryption; server can't read data
Network surveillanceTLS 1.3 + OHTTP (IP privacy) + E2E; three layers
Man-in-the-middleIn-person verification of identity
Spam/harvestingProximity required; no remote adding
Device theftOS-level key storage, optional biometrics
Lost deviceSocial recovery + encrypted backups
Traffic analysisPadding to standardized bucket sizes
Replay attacksOne-time tokens, per-message nonces

Metadata Visibility

The relay operator can observe communication patterns: which identities communicate, when messages are sent and received, and message frequency. The relay cannot read message content. Delivery jitter reduces timing correlation between senders and recipients. Running your own relay server eliminates third-party metadata exposure.

Best Practices

For Users

  1. Create a backup — Protect against device loss
  2. Use a strong backup password — A passphrase (4+ words) is recommended. Store it somewhere safe, separate from your devices
  3. Verify important contacts — Compare fingerprints in person
  4. Revoke lost devices immediately — Prevent unauthorized access
  5. Keep your device secure — Enable lock screen, update OS
  6. Only link devices you physically control — Each linked device has full access to your identity

For Privacy

  1. Review visibility settings — Control what each contact sees
  2. Limit field sharing — Only share what's needed
  3. Remove old contacts — They keep seeing updates otherwise

For Recovery

Set up social recovery to protect against total device loss:

  1. Choose diverse guardians — Spread across different social circles (e.g., one family member, one friend, one colleague)
  2. Don't rely on one group — If all guardians are family, a single household event could make recovery impossible
  3. Set threshold to at least 3 — Higher thresholds are more secure
  4. Update guardians when relationships change — Remove guardians you've lost touch with and add new ones
  5. Review periodically — Check your guardian list once a year

For Backups

  1. Use a strong passphrase — At least 4 random words or equivalent strength
  2. Store backups securely — On a USB drive, external storage, or a secure location separate from your devices
  3. Don't store on cloud services — Backup files are encrypted, but keeping them local is more private
  4. Create fresh backups — After adding new contacts or linking devices

Security Reporting

Found a security issue? Please report it responsibly:

Email: security@vauchi.app

We will:

  • Acknowledge within 48 hours
  • Investigate and fix verified issues
  • Credit reporters (unless they prefer anonymity)
  • Not pursue legal action against good-faith researchers

Open Source

All code is open source and available for inspection:

We welcome security reviews and contributions.

Technical Details

For cryptographic implementation details, see:

Community

Join the Vauchi community.


Get Involved

Report Issues

Found a bug or have a feature request?

Contribute Code

We welcome contributions! See our Contributing Guide for:

  • Development setup
  • Code guidelines
  • Merge request process

Translations

Help translate Vauchi to your language:

Discussions

Have questions or ideas?


Code of Conduct

Our Commitment

Vauchi is built on trust earned in person. We extend that same spirit to our community: treat others as you would someone you've just met face-to-face.

Expected Behavior

  • Be respectful and considerate
  • Give and accept constructive feedback graciously
  • Focus on what's best for the project and community
  • Assume good intent; ask for clarification before assuming malice

Unacceptable Behavior

  • Harassment, insults, or personal attacks
  • Trolling or inflammatory comments
  • Publishing others' private information
  • Conduct that would be inappropriate in a professional setting

Scope

This applies to all project spaces: issues, merge requests, discussions, and any public space where you represent Vauchi.

Enforcement

Instances of unacceptable behavior may be reported to: conduct@vauchi.app

Maintainers will review and respond to all complaints. Responses may include:

  • Warning
  • Temporary ban
  • Permanent ban

Attribution

Adapted from the Contributor Covenant, version 2.1.


Contact

  • General: hello@vauchi.app
  • Security: security@vauchi.app
  • Conduct: conduct@vauchi.app

Supporters

Thank you to everyone who supports Vauchi's mission to build privacy-respecting software.


Platinum Sponsors

Become our first Platinum sponsor — GitHub Sponsors

Gold Sponsors

Become our first Gold sponsor — GitHub Sponsors

Silver Sponsors

Become our first Silver sponsor — GitHub Sponsors

Bronze Sponsors

Become our first Bronze sponsor — GitHub Sponsors

Backers

Your name could be here — GitHub Sponsors

Supporters

Your name could be here — GitHub Sponsors


How to Support

Every contribution helps us build privacy-respecting software without compromising on principles.

Where Funds Go

CategoryPurpose
HardwareDevelopment machines, mobile test devices
InfrastructureRelay server hosting, domain costs
SecurityExternal security audits
DevelopmentFull-time development toward v1.0

Thank you for believing in privacy-first software.

Privacy Policy

Last Updated: May 2026

Overview

Vauchi is a privacy-focused contact card exchange application. This privacy policy explains how we handle your data. The short version: your data stays on your devices, encrypted, and under your control.

Data Collection

What We Collect

On Your Device (Local Storage):

  • Your identity (cryptographic keypair, display name)
  • Your contact card (fields you choose to add: email, phone, etc.)
  • Contacts you've exchanged with (their public cards)
  • Visibility rules (which contacts can see which fields)
  • Device registry (for multi-device sync)

On Our Relay Server:

  • Temporary encrypted envelopes containing contact card updates (deleted after delivery or 120 days)
  • Connection metadata (cryptographic identity hash, connection timestamps) for rate limiting — IP addresses are NOT stored or logged
  • No envelope content is ever readable by the server

What We Don't Collect

  • We do not collect analytics or telemetry
  • We do not track your location
  • We do not access your device contacts, photos, or other apps
  • We do not use advertising identifiers
  • We do not sell or share your data with third parties

Data Storage

Local-First Architecture

All your personal data is stored locally on your device:

  • Encryption at Rest: Your data is encrypted using XChaCha20-Poly1305. The key is derived from a master key (the SMK) held in your device's platform secure storage (iOS Keychain / Android KeyStore / OS credential manager), never written to disk in the clear.
  • No Cloud Backup by Default: Your data is not automatically backed up to any cloud service. Nothing leaves your device unless you ask it to.
  • You Control Exports: You can create encrypted backups manually, protected by a password you choose (see Backups below).

Backups

A backup is a single encrypted file that you create and you keep — we never receive it, store it, or see inside it.

  • What's in it: your identity (the master seed that all your keys derive from), your contacts, your own card, and your labels/groups.
  • How it's protected: the file is encrypted with XChaCha20-Poly1305 under a key stretched from your password with Argon2id (memory-hard: 64 MB, 3 passes, 4 lanes). A weak password is the only weak link — the cryptography assumes your password is the secret, so choose a strong one.
  • What is deliberately not in it: the per-conversation forward-secrecy keys (the Double Ratchet state) for each contact. These are intentionally ephemeral. After you restore onto a new device, secure channels re-establish themselves the next time you and a contact sync — you don't lose contacts, only the short-lived keys that protect old messages, which is exactly the property forward secrecy is meant to give you.
  • Where it lives: wherever you put it. The file is just bytes; its safety is your password plus wherever you store it.

Relay Server

The relay is a blind dead-drop. Think of it as a left-luggage locker that the staff can never open and whose tickets change every day. It moves encrypted contact-card updates between your devices and your contacts and knows as little as the design allows:

  • It cannot read anything. Updates are end-to-end encrypted before they leave your device. The relay only ever holds opaque ciphertext blobs — it has none of your keys.
  • It cannot build a social graph. Messages are addressed to daily-rotating mailbox tokens derived from a secret shared only between you and each contact. The token for "you → Alice" is a different random-looking value tomorrow, so the relay cannot link who talks to whom across days, or tie a mailbox to a person.
  • Retention: an undelivered blob is deleted after delivery, or after 120 days, whichever comes first.
  • Logs: connection metadata only (a cryptographic identity hash and timestamps), never card content. IP addresses are not stored or logged. Rate-limiting state (keyed on the identity hash, not IP) is discarded after 30 minutes of inactivity.

Oblivious HTTP (Hiding Your IP)

There is a subtle gap in "we don't log IP addresses": a server that receives a connection still sees the source IP, log or no log. We close that gap structurally rather than asking you to trust a promise.

Client requests are wrapped in Oblivious HTTP (OHTTP, RFC 9458) and routed through an independent OHTTP gateway operated by a different party than the relay:

  • The gateway sees your IP address but only an encrypted request it cannot read.
  • The relay sees the request but only the gateway's IP — never yours.

No single party holds both your identity and your network location. The relay can't log your IP because, by construction, it never receives it. Transport throughout is HTTPS (TLS 1.3) with SPKI certificate pinning to block man-in-the-middle attacks.

Data Sharing

With Your Contacts

When you exchange contact cards with someone:

  • You explicitly choose which fields they can see
  • You can change visibility settings at any time
  • Changes sync automatically to their device

With Third Parties

We do not share your data with any third parties. Period.

With Law Enforcement

If required by law, we can only provide:

  • Connection metadata (timestamps only — IP addresses are not stored or logged)
  • Encrypted envelopes (which we cannot decrypt)

We cannot provide your contact card content, contact list, IP addresses, or any decrypted data because we do not have access to it.

Data Security

Cryptographic Protections

  • Identity Keys: Ed25519 signing keys, derived from a 256-bit master seed, never leave your device
  • Encryption: X25519 key agreement + XChaCha20-Poly1305 for all contact card updates
  • Key Derivation: HKDF-SHA256 with domain separation for all internal keys; Argon2id for password-based encryption (backups)
  • Forward Secrecy: Double Ratchet protocol ensures each contact card update uses a unique, single-use encryption key that is deleted after use
  • Network Privacy: Oblivious HTTP (RFC 9458) separates your IP address from your requests; the relay never sees your network location

Platform Security

  • iOS: Keys stored in Keychain
  • Android: Keys stored in KeyStore
  • Desktop: Keys encrypted with OS-level secure storage

Certificate Pinning

All client apps use SPKI certificate pinning to prevent man-in-the-middle attacks against relay server connections.

Your Rights

Access Your Data

All your data is stored locally on your device. You can view it directly in the app.

Export Your Data

You can export an encrypted backup of all your data at any time from Settings > Backup.

Delete Your Data

  • Account Deletion: Use Settings > Delete Account to initiate deletion. A 7-day grace period allows you to cancel. After 7 days, the app sends a revocation signal to all your contacts (authenticated with your cryptographic identity), requests the relay server to purge all stored data for your account, and permanently deletes all local data (database, keys, and secure storage entries). Your contacts' apps will automatically delete your card upon receiving the revocation.
  • Single Contact Removal: You can remove any contact, which deletes their data from your device
  • Multi-Device: Account deletion is synchronized across all your linked devices. Initiating deletion on one device starts the grace period on all devices; cancellation from any device cancels on all devices.

Data Portability

Encrypted backups can be imported on any device where you install Vauchi.

Account Recovery

Vauchi has no central account or "forgot password" mechanism. Recovery depends on your situation:

  • Linked devices (primary method): If you have multiple linked devices and at least one remains accessible, your identity and all data are already synchronized. No recovery process is needed.
  • Social recovery (all devices lost): If all your devices are lost, trusted contacts you previously designated can vouch for your identity, allowing you to migrate your contacts to a new cryptographic identity on a new device. Note: social recovery creates a new identity — your old signing keys cannot be recovered. Trusted contact designations and per-contact visibility labels may need to be reconfigured.

Children's Privacy

Vauchi does not require registration and does not verify the age of its users. Parents and guardians should be aware that Vauchi allows users to share contact information with people they meet in person.

Changes to This Policy

We may update this privacy policy from time to time. We will notify you of significant changes through the app or our website. Continued use of Vauchi after changes constitutes acceptance of the updated policy.

Open Source

Vauchi is open source software. You can inspect exactly how your data is handled by reviewing our source code at: gitlab.com/vauchi

Contact Us

For privacy-related questions or concerns:


Summary

QuestionAnswer
Store my contacts on servers?No, only on your device
Can you read my card updates?No, end-to-end encrypted
Can you see who I talk to?No, daily-rotating mailbox tokens
Do you log my IP address?No — Oblivious HTTP hides it from the relay
Do you sell my data?No, never
Do you use tracking/analytics?No
Can I delete my data?Yes, Settings > Delete Account (7-day grace)
What's in a backup?Your identity, contacts, card, labels — encrypted with your password
Data backed up automatically?No, you control backups
What if I lose my device?Linked device or social recovery

Terms of Service

Last Updated: May 2026

1. Acceptance of Terms

By downloading, installing, or using Vauchi ("the App"), you agree to these Terms of Service ("Terms"). If you do not agree, do not use the App.

2. Description of Service

Vauchi is a privacy-focused contact card exchange application. It allows you to:

  • Create and manage a personal contact card
  • Exchange contact cards with others via QR code scanning in physical proximity
  • Control which fields each contact can see
  • Sync contact data across your own devices
  • Back up and restore your data

3. Your Data and Privacy

Vauchi is designed around local-first, user-controlled data. Please review our Privacy Policy for full details.

Key points:

  • Your data stays on your devices. We do not store your contact cards, identity, or personal information on our servers.
  • End-to-end encryption. Contact card updates sent through our relay are encrypted on your device before transmission. We cannot read them.
  • No accounts. Your device holds your cryptographic identity. There is no username, password, or central account.

4. Relay Service

Vauchi operates a relay server that delivers encrypted contact card updates between devices. The relay:

  • Stores encrypted envelopes temporarily (deleted after delivery or 120 days)
  • Cannot decrypt any content
  • Logs connection metadata (cryptographic identity hash, connection timestamps) for rate limiting — IP addresses are not stored or logged, and connection metadata is retained for 24 hours
  • May be rate-limited to prevent abuse

We provide the relay service on a best-effort basis and may suspend or discontinue it at any time with reasonable notice.

5. User Responsibilities

You agree to:

  • Use the App only for lawful purposes
  • Not attempt to circumvent security measures or reverse-engineer the encryption
  • Not use the App to harass, stalk, or harm others
  • Not transmit malicious content through contact card fields
  • Not abuse the relay service (automated mass requests, denial-of-service attempts)

6. Account Deletion

You can delete your account and all associated data at any time from Settings > Delete Account. A 7-day grace period allows cancellation. After deletion:

  • A revocation signal is sent to all your contacts
  • The relay purges all stored data for your account
  • All local data is permanently destroyed
  • This action is irreversible after the grace period

7. Age Requirement

Vauchi is not intended for use by children under the age of 13. By using the App, you confirm that you are at least 13 years old, or that you have obtained parental or guardian consent.

8. Intellectual Property

Vauchi is free software, licensed under the GNU General Public License v3.0 or later (GPL-3.0-or-later). You may use, modify, and distribute the source code under the terms of this license. The source code is available at gitlab.com/vauchi.

The Vauchi name and logo are trademarks of the Vauchi project. Use of these trademarks is subject to our trademark guidelines.

9. Disclaimer of Warranties

THE APP IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.

We do not warrant that:

  • The App will be uninterrupted or error-free
  • The relay service will be available at all times
  • The encryption will protect against all possible threats

10. Limitation of Liability

TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE VAUCHI PROJECT AND ITS CONTRIBUTORS SHALL NOT BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES, INCLUDING BUT NOT LIMITED TO LOSS OF DATA, LOSS OF CONTACTS, OR INABILITY TO ACCESS YOUR INFORMATION.

11. Self-Hosting

You may self-host the Vauchi relay server under the terms of the GPL-3.0-or-later license. Self-hosted instances are your responsibility. We provide no warranty or support for self-hosted deployments beyond published documentation.

12. Third-Party Services

Vauchi does not integrate with third-party services. The App does not use analytics, advertising networks, social media SDKs, or cloud storage providers.

13. Export Compliance

Vauchi uses strong encryption. By using the App, you acknowledge that export and use of encryption software may be subject to regulations in your jurisdiction. You are responsible for complying with applicable export control laws.

14. Modifications to Terms

We may update these Terms from time to time. We will notify you of significant changes through the App or our website. Continued use after changes constitutes acceptance of the updated Terms.

15. Governing Law

These Terms are governed by the laws of Switzerland. Any disputes shall be subject to the exclusive jurisdiction of the courts of Switzerland.

16. Severability

If any provision of these Terms is found to be unenforceable, the remaining provisions continue in full force and effect.

17. Contact

For questions about these Terms:

For Developers

Welcome to Vauchi development! This section contains everything you need to contribute.


Getting Started

New to the project? Start here:

  1. Contributing Guide — Set up your environment and learn the workflow
  2. Architecture Overview — Understand how the system works
  3. GUI Guidelines & UX Guidelines — Design rules for all platforms
  4. Cryptography Reference — Deep dive into encryption

Documentation

DocumentDescription
ContributingDev workflow, code guidelines, PR process
GUI GuidelinesComponent design — toasts, editing
UX GuidelinesPhysical-first, local-first, flow design
ArchitectureSystem overview, components, data flow
CryptographyEncryption algorithms, key management, protocols
Tech StackLanguages, frameworks, libraries
DiagramsSequence diagrams for core flows

Repository Structure

Vauchi is a multi-repo project under the vauchi GitLab group:

RepositoryPurpose
vauchi/Orchestrator repo (this documentation)
core/Rust workspace: vauchi-core + UniFFI bindings
relay/HTTP v2 relay server
linux-gtk/GTK4 Linux desktop app
linux-qt/Qt6 (Widgets) Linux desktop app
macos/macOS native app (SwiftUI)
windows/Windows native app (WinUI3)
ios/SwiftUI app
android/Kotlin/Compose app
features/Gherkin specs
locales/i18n JSON files

Quick Commands

# Clone and setup workspace
git clone git@gitlab.com:vauchi/vauchi.git
cd vauchi
just setup

# Build everything
just build

# Run all checks
just check

# Run tests
just test

# Show all commands
just help

Key Principles

All development follows our core principles:

  • TDD mandatory — Red → Green → Refactor
  • 90%+ coverage — For vauchi-core
  • Real crypto in tests — No mocking
  • Problem-first — Every task starts as a problem record

Getting Help

Architecture Overview

Vauchi is a privacy-focused contact card system. Users exchange contact cards in person via QR code (with NFC and Bluetooth as additional transport options). After exchange, cards update automatically — when you change your phone number, everyone who has your card sees the change.

System Architecture

┌─────────────────────────────────────────────────────────────────────────────────────────┐ ┌──────────────────────────────────────────────────────────────┐
│                                         CLIENTS                                         │ │                        RELAY SERVER                          │
│                                                                                         │ │           • Store-and-forward encrypted messages             │
│                                                                                         │ │            • No access to plaintext (oblivious)              │
│ ┌───────────────────────────┐     ┌─────────┐     ┌─────────┐     ┌──────┐     ┌──────┐ │ │ ┌───────────•─Rate limiting,─quotas,┐GDPR purge────────────┐ │
│ │                           │     │         │     │         │     │      │     │      │ │ │ │              │     │              │     │                │ │
│ │                           │     │         │     │         │     │      │     │      │ │ │ │              │     │              │     │                │ │
│ │            iOS            │     │ Android │     │ Desktop │     │ CLI  │     │ TUI  │ │ │ │ Blob Storage │     │ Device Sync  │     │ Recovery Store │ │
│ │          SwiftUI          │     │ Compose │     │  Native │     │ Rust │     │ Rust │ │ │ │ (encrypted)  │     │ (per-device) │     │  (90-day TTL)  │ │
│ │                           │     │         │     │         │     │      │     │      │ │ │ │              │     │              │     │                │ │
│ └─────────────┬─────────────┘     └────┬────┘     └────┬────┘     └───┬──┘     └───┬──┘ │ │ └──────────────┘     └──────────────┘     └────────────────┘ │
│               │                        │               │              │            │    │ │                                                              │
│               │                        │               │              │            │    │ └──────────────────────────────────────────────────────────────┘
│               ├────────────────────────┴───────────────┴──────────────┴────────────┘    │                                                                 
│               │                                                                         │                                                                 
│               ▼                                                                         │                                                                 
│ ┌───────────────────────────┐                                                           │                                                                 
│ │                           │                                                           │                                                                 
│ │        vauchi-core        │                                                           │                                                                 
│ │          (UniFFI)         │                                                           │                                                                 
│ │ Crypto, storage, protocol │                                                           │                                                                 
│ │                           │                                                           │                                                                 
│ └─────────────┬─────────────┘                                                           │                                                                 
│               │                                                                         │                                                                 
└────────OHTTP─encrypted──────────────────────────────────────────────────────────────────┘                                                                 
                │                                                                                                                                           
                │                                                                                                                                           
                ▼                                                                                                                                           
  ┌───────────────────────────┐                                                                                                                             
  │                           │                                                                                                                             
  │                           │                                                                                                                             
  │       OHTTP Gateway       │                                                                                                                             
  │     (strips client IP)    │                                                                                                                             
  │                           │                                                                                                                             
  └─────────────┬─────────────┘                                                                                                                             
                │                                                                                                                                           
        HTTP v2 (TLS 1.3)                                                                                                                                   
                │                                                                                                                                           
                │                                                                                                                                           
                ▼                                                                                                                                           
  ┌───────────────────────────┐                                                                                                                             
  │                           │                                                                                                                             
  │           Relay           │                                                                                                                             
  │                           │                                                                                                                             
  └───────────────────────────┘                                                                                                                             

Note: All remote client↔relay traffic flows through an OHTTP gateway per ADR-037 — the relay never sees client IP addresses, and the gateway never sees request content. Sequence diagrams below omit the gateway hop for protocol clarity.

Core Components

vauchi-core

The Rust core library provides all cryptographic and protocol functionality:

ModulePurposeKey Files
crypto/Encryption, signing, KDFencryption.rs, signing.rs
exchange/Contact exchange protocolsession.rs, qr.rs, x3dh.rs
sync/Update propagationdevice_sync.rs, delta.rs
recovery/Social recoverymod.rs
storage/Local encrypted databasecontacts.rs, identity.rs
network/Relay communicationconnection.rs, protocol.rs
ui/Core-driven UI (vauchi-app)screen.rs, component.rs
i18nInternationalization (vauchi-app)i18n.rs

vauchi-protocol

Shared protocol message types used by both vauchi-core and the relay:

  • Serde-only crate (no crypto, no I/O)
  • Defines MessageEnvelope, MessagePayload, and all variant structs
  • Provides framing helpers (encode_message/decode_message)
  • Ensures wire format consistency between clients and relay

Relay Server

Rust server for message routing (depends on vauchi-protocol for shared types):

  • HTTP v2 store-and-forward (synchronous /v2/ request/response API)
  • TLS required in production
  • No user accounts — just encrypted blobs
  • Background cleanup tasks (hourly)

Client Applications

PlatformStackBinding
iOSSwiftUIvauchi-platform-swift (SPM)
AndroidKotlin/ComposeMaven AAR from core CI
Linux (GTK)GTK4 (gtk4-rs)Direct Rust linkage
Linux (Qt)Qt6 (Widgets)cbindgen C FFI
macOSSwiftUIUniFFI (shared with iOS)
WindowsWinUI3 (C# .NET 8)C ABI (vauchi-cabi)
CLIRustDirect library use
TUIRust (ratatui)Direct library use

Core-Driven UI

Core defines what to show; frontends only decide how to render natively. New workflows are pure Rust — zero frontend code unless a new component type is needed.

┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐           
│                                                                                                                                                  Core (Rust)                                                                                                                                                   │ │                                       Frontend (per platform)                                       │           
│                                                                                                                                                                                                                                                                                                                │ │                                                                                                     │           
│                                                                                                                                                                                                                                                                                                                │ │                                                                                                     │           
│ ┌────────────────────────────────────────────┐     ┌───────────────────────────────────────────┐     ┌───────────────────────────────────────────────┐     ┌────────────────────────────────────────┐                                                          ┌─────────────────────────────────────────────┐ │ │ ┌───────────────────────────────────────────────────────────┐     ┌───────────────────────────────┐ │   ┌──────┐
│ │                                            │     │                                           │     │                                               │     │                                        │                                                          │                                             │ │ │ │                                                           │     │                               │ │   │      │
│ │            WorkflowEngine trait            │     │                                           │     │                                               │     │                                        │                                                          │                                             │ │ │ │    "Component Library (one native widget per Component)   │     │         ScreenRenderer        │ │   │      │
│ │      • current_screen() → ScreenModel      │     │ ScreenModel { screen_id, title, subtitle, │     │ Component { TextInput, ToggleList, FieldList, │     │ UserAction { TextChanged, ItemToggled, │                                                          │   ActionResult { UpdateScreen, NavigateTo,  │ │ │ │ TextInput → TextField / OutlinedTextField / &lt;input&gt; │     │  Maps ScreenModel → native UI │ │   │ Core │
│ │ • handle_action(UserAction) → ActionResult │     │      components, actions, progress }      │     │  CardPreview, InfoPanel, Text, Divider, ... } │     │          ActionPressed, ... }          │                                                          │ ValidationError, Complete, ShowToast, ... } │ │ │ │         ToggleList → Toggle list / Checkboxes / [x        │     │ Sends UserAction back to core │ │   │      │
│ │                                            │     │                                           │     │                                               │     │                                        │                                                          │                                             │ │ │ │                                                           │     │                               │ │   │      │
│ └────────────────────────────────────────────┘     └───────────────────────────────────────────┘     └───────────────────────────────────────────────┘     └────────────────────────────────────────┘                                                          └─────────────────────────────────────────────┘ │ │ └───────────────────────────────────────────────────────────┘     └───────────────────────────────┘ │   └───┬──┘
│                                                                                                                                                                                                                                                                                                                │ │                                                                                                     │       │   
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────────────────────────┘       │   
                                                                                                                                                                                                                                                                                                                                                                                                                                 │   
                                                                                                                                                                                                                                                                                                                                                                                                                                 │   
                                                                                                                                                                                                                                                                                                                                                                                                                                 │   
  ┌────────────────────────────────────────────┐                                                                                                                                                                                                                                                                                                                                                                                 │   
  │                                            │                                                                                                                                                                                                                                                                                                                                                                                 │   
  │                  Frontend                  │◄─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────ScreenModel─(JSON─or─direct)───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘   
  │                                            │                                                                                                                                                                           UserAction (JSON or direct)                                                                                                                                                                               
  └────────────────────────────────────────────┘                                                                                                                                                                                                                                                                                                                                                                                     

Each frontend implements a component library (one native component per Component variant) and a ScreenRenderer that maps ScreenModel to native UI. The component library is built once and reused across all workflows.

ComponentLinux GTK4Linux Qt (Widgets)macOS/iOS (SwiftUI)Android (Compose)Windows (WinUI3)TUI (Ratatui)CLI
TextInputgtk::EntryTextFieldTextFieldOutlinedTextFieldTextBoxInput widgetstdin prompt
ToggleListgtk::CheckButtonCheckBoxList + ToggleLazyColumn + CheckboxToggleSwitch[x]/[ ] listnumbered choice
FieldListgtk::ListBoxListViewList + chipsLazyColumn + chipsListViewTable rowsformatted output
CardPreviewgtk::FrameFrameCard viewCard composableBorderBox rendertext output
InfoPanelgtk::BoxColumnLayoutVStackColumnStackPanelBlockprintln sections

Transport: Rust clients (CLI, TUI, Desktop) call WorkflowEngine directly. Mobile clients (iOS, Android) use JSON over UniFFI.

Adding workflows: Implement a new WorkflowEngine in core. All frontends render it automatically via the existing component library — no frontend changes needed.

Adding component types: Define a new Component variant in core, then implement the corresponding native widget in each frontend's component library. This is rare — the vocabulary stabilizes quickly.

Data Flow

1. Contact Exchange (In-Person)

 ┌───────┐                         ┌─────┐   
 │ Alice │                         │ Bob │   
 └───┬───┘                         └──┬──┘   
     │                                │      
     │  Display QR (identity + key)   │      
     │────────────────────────────────▶      
     │                                │      
     │   Scan QR, verify proximity    │      
     ◀────────────────────────────────│      
     │                                │      
     │      X3DH key agreement        │      
     │────────────────────────────────▶      
     │                                │      
     │   Exchange encrypted cards     │      
     ◀────────────────────────────────│      
     │                                │      
   ┌──────────────────────────────────┐      
   │ Both now have each other's cards │      
   └──────────────────────────────────┘      
     │                                │      
 ┌───┴───┐                         ┌──┴──┐   
 │ Alice │                         │ Bob │   
 └───────┘                         └─────┘   

2. Card Updates (Remote via Relay)

┌──────────────────────────────────────────┐
│                                          │
│        Alice updates phone number        │
│                                          │
└─────────────────────┬────────────────────┘
                      │                     
                      │                     
                      │                     
                      │                     
                      ▼                     
┌──────────────────────────────────────────┐
│                                          │
│                                          │
│          Encrypt delta with CEK          │
│ (per-contact shared key, Double Ratchet) │
│                                          │
└─────────────────────┬────────────────────┘
                      │                     
                      │                     
                      │                     
                      │                     
                      ▼                     
┌──────────────────────────────────────────┐
│                                          │
│                                          │
│              Send to relay               │
│                (HTTP v2)                 │
│                                          │
└─────────────────────┬────────────────────┘
                      │                     
                      │                     
                      │                     
                      │                     
                      ▼                     
┌──────────────────────────────────────────┐
│                                          │
│                                          │
│       Relay stores encrypted blob        │
│        (indexed by recipient_id)         │
│                                          │
└─────────────────────┬────────────────────┘
                      │                     
                      │                     
                      │                     
                      │                     
                      ▼                     
┌──────────────────────────────────────────┐
│                                          │
│                                          │
│               Bob connects               │
│       (receives pending messages)        │
│                                          │
└─────────────────────┬────────────────────┘
                      │                     
                      │                     
                      │                     
                      │                     
                      ▼                     
┌──────────────────────────────────────────┐
│                                          │
│                                          │
│              Decrypt delta               │
│      (update Alice's card locally)       │
│                                          │
└──────────────────────────────────────────┘

3. Multi-Device Sync

All devices under one identity share the same master seed. Device-specific keys are derived via HKDF:

┌─────────────┐     ┌───────────────────────┐
│             │     │                       │
│             │     │                       │
│ Master Seed ├────►│     Device 1 keys     │
│             │     │ (HKDF + device_index) │
│             │     │                       │
└──────┬──────┘     └───────────────────────┘
       │                                     
       │                                     
       │                                     
       │                                     
       │                                     
       │            ┌───────────────────────┐
       │            │                       │
       │            │                       │
       ├───────────►│     Device 2 keys     │
       │            │ (HKDF + device_index) │
       │            │                       │
       │            └───────────────────────┘
       │                                     
       │                                     
       │                                     
       │                                     
       │                                     
       │            ┌───────────────────────┐
       │            │                       │
       │            │                       │
       └───────────►│     Device 3 keys     │
                    │ (HKDF + device_index) │
                    │                       │
                    └───────────────────────┘

Device linking uses QR code scan with time-limited token.

4. Recovery (Social Vouching)

When all devices are lost:

  1. Create new identity
  2. Generate recovery claim (old_pk → new_pk)
  3. Meet contacts in person, collect signed vouchers
  4. When threshold (3) met, upload proof to relay
  5. Other contacts discover proof, verify via mutual contacts
  6. Accept/reject identity transition

Security Model

End-to-End Encryption

  • All card data encrypted with XChaCha20-Poly1305
  • Per-contact keys derived via X3DH + Double Ratchet
  • Forward secrecy: each message uses unique key
  • Relay sees only encrypted blobs

Key Hierarchy

┌───────────────────────────────────────────┐                                                                     
│                                           │                                                                     
│                                           │                                                                     
│                Master Seed                │                                                                     
│ (256-bit, generated at identity creation) │                                                                     
│                                           │                                                                     
└───────────────────────────────────────────┘                                                                     
                      │                                                                                           
                      │                                                                                           
                      ├─────────────────────────────────────────┬──────────────────────────────────┐              
                      │                                         │                                  │              
                      ▼                                         ▼                                  ▼              
┌───────────────────────────────────────────┐     ┌───────────────────────────┐     ┌────────────────────────────┐
│                                           │     │                           │     │                            │
│                                           │     │                           │     │                            │
│            Identity Signing Key           │     │        Exchange Key       │     │ SMK (Shredding Master Key) │
│            (Ed25519, raw seed)            │     │   (X25519, HKDF derived)  │     │       (HKDF derived)       │
│                                           │     │                           │     │                            │
└───────────────────────────────────────────┘     └───────────────────────────┘     └────────────────────────────┘
                                                                                                   │              
                                                                                                   │              
                      ┌─────────────────────────────────────────┬──────────────────────────────────┤              
                      │                                         │                                  │              
                      ▼                                         ▼                                  ▼              
┌───────────────────────────────────────────┐     ┌───────────────────────────┐     ┌────────────────────────────┐
│                                           │     │                           │     │                            │
│                                           │     │                           │     │                            │
│                    SEK                    │     │            FKEK           │     │      Per-Contact CEK       │
│          (Storage Encryption Key)         │     │ (File Key Encryption Key) │     │      (random 256-bit)      │
│                                           │     │                           │     │                            │
└───────────────────────────────────────────┘     └───────────────────────────┘     └────────────────────────────┘

Physical Verification

Contact exchange requires in-person presence:

  • QR + ultrasonic audio verification (18-20 kHz) — implemented on iOS, planned for Android
  • NFC Active tap (planned — centimeters range)
  • BLE with RSSI proximity check (planned — GATT transport)

Repository Structure

vauchi/                    ← Orchestrator repo
├── core/                  ← vauchi-core + vauchi-platform + vauchi-protocol
├── relay/                 ← HTTP v2 relay server (uses vauchi-protocol)
├── linux-gtk/             ← GTK4 Linux desktop app
├── linux-qt/              ← Qt6 (Widgets) Linux desktop app
├── macos/                 ← macOS native app (SwiftUI)
├── windows/               ← Windows native app (WinUI3)
├── ios/                   ← SwiftUI app
├── android/               ← Kotlin/Compose app
├── cli/                   ← Command-line interface
├── tui/                   ← Terminal UI
├── features/              ← Gherkin specs
├── locales/               ← i18n JSON files
├── ohttp-relay/           ← OHTTP relay proxy
├── themes/                ← Design tokens
├── e2e/                   ← End-to-end tests
└── docs/                  ← Documentation

Cryptography Reference

Concise reference for all cryptographic operations in Vauchi.

Algorithms

PurposeAlgorithmLibraryNotes
SigningEd25519ed25519-dalekIdentity, registry
Key ExchangeX25519x25519-dalekX3DH + identity binding
Sym. EncryptXChaCha20-Poly1305chacha20poly1305192-bit nonce
Forward SecrecyDouble Ratchethkdf + hmacChain limit 2000
Key DerivationHKDF-SHA256hkdfRFC 5869
Password KDFArgon2idargon2m=64MB, t=3, p=4
CSPRNGOsRngrandOS entropy
TLSTLS 1.2/1.3rustls (aws-lc-rs)Relay transport + SPKI pinning
IP PrivacyOHTTP (RFC 9458)ohttp (rust-hpke)Unlinks client IP from request

Post-quantum (planned, ADR-060 / ADR-062). Key agreement is classical-only today. A hybrid X25519 + ML-KEM-768 upgrade is planned to close the harvest-now-decrypt-later gap on relay-carried ciphertext; signatures (Ed25519) migrate later since they are not harvest-now-forge-later.

Key Types

Identity Keys

KeyTypeSizePurpose
Master SeedSymmetric256-bitRoot of all keys
Signing KeyEd2551932+64 bytesIdentity, signatures
Exchange KeyX2551932 bytesKey agreement

Storage Keys (Shredding Hierarchy)

┌──────────────────────────────────────────────┐                                                                                                            
│                                              │                                                                                                            
│            Master Seed (256-bit)             │                                                                                                            
│                                              │                                                                                                            
└──────────────────────────────────────────────┘                                                                                                            
                        │                                                                                                                                   
                        │                                                                                                                                   
                        ├─────────────────────────────────────────────────────┬─────────────────────────────────────────────────────┐                       
                        │                                                     │                                                     │                       
                        ▼                                                     ▼                                                     ▼                       
┌──────────────────────────────────────────────┐     ┌─────────────────────────────────────────────────┐     ┌─────────────────────────────────────────────┐
│                                              │     │                                                 │     │                                             │
│                                              │     │                                                 │     │                                             │
│             Identity Signing Key             │     │                   Exchange Key                  │     │          SMK (Shredding Master Key)         │
│        raw seed (Ed25519 requirement)        │     │ HKDF(seed, &quot;Vauchi_Exchange_Seed_v2&quot;) │     │ HKDF(seed, &quot;Vauchi_Shred_Key_v2&quot;) │
│                                              │     │                                                 │     │                                             │
└──────────────────────────────────────────────┘     └─────────────────────────────────────────────────┘     └─────────────────────────────────────────────┘
                                                                                                                                    │                       
                                                                                                                                    │                       
                        ┌─────────────────────────────────────────────────────┬─────────────────────────────────────────────────────┤                       
                        │                                                     │                                                     │                       
                        ▼                                                     ▼                                                     ▼                       
┌──────────────────────────────────────────────┐     ┌─────────────────────────────────────────────────┐     ┌─────────────────────────────────────────────┐
│                                              │     │                                                 │     │                                             │
│         SEK (Storage Encryption Key)         │     │          FKEK (File Key Encryption Key)         │     │               Per-Contact CEK               │
│ HKDF(SMK, &quot;Vauchi_Storage_Key_v2&quot;) │     │   HKDF(SMK, &quot;Vauchi_FileKey_Key_v2&quot;)  │     │          random 256-bit per contact         │
│        encrypts all local SQLite data        │     │            encrypts file key storage            │     │   encrypts individual contact's card data   │
│                                              │     │                                                 │     │                                             │
└──────────────────────────────────────────────┘     └─────────────────────────────────────────────────┘     └─────────────────────────────────────────────┘

HKDF Convention: Master seed as IKM, no salt, domain string as info. All derivations use HKDF::derive_key(None, &seed, info).

HKDF Context Strings:

ContextUsage
Vauchi_Exchange_Seed_v2Exchange key derivation from master seed
Vauchi_Shred_Key_v2SMK derivation from master seed
Vauchi_Storage_Key_v2SEK derivation from SMK
Vauchi_FileKey_Key_v2FKEK derivation from SMK
vauchi-x3dh-symmetric-v2X3DH transcript binding (4-key HKDF info)
vauchi-x3dh-key-v2X3DH key agreement derivation
Vauchi_Root_RatchetDH ratchet root key step
Vauchi_Message_KeySymmetric ratchet message key
Vauchi_Chain_KeySymmetric ratchet chain key advance
Vauchi_AnonymousSender_v2Anonymous sender ID derivation
Vauchi_Mailbox_v1Contact mailbox token (daily rotation)
Vauchi_DeviceSyncDevice-to-device encryption key derivation
Vauchi_DeviceSync_v1Device sync self-token (daily rotation)

Ratchet Keys

KeyTypeLifecycle
Root Key32 bytesUpdated on DH ratchet
Chain Key32 bytesAdvances with each message
Message Key32 bytesSingle-use, deleted after

Ciphertext Format

algorithm_tag (1 byte) || nonce || ciphertext || tag
TagAlgorithmNonceNotes
0x01AES-256-GCM12 bytesRemoved — no longer supported
0x02XChaCha20-Poly130524 bytesDefault since v0.1.2
0x03XChaCha20-Poly1305 + AD24 bytesDouble Ratchet (header-bound)

Tag 0x03 binds message header as AEAD associated data to prevent relay manipulation.

Message Padding

All messages padded to fixed buckets before encryption:

BucketSizeTypical Content
Small256 BACK, presence, revocation
Medium-Small512 BShort card deltas, single-field updates
Medium1 KBCard deltas, small updates
Large4 KBMedia references, large payloads

Messages > 4 KB: rounded to next 256-byte boundary.

Format: [4-byte BE length prefix] [plaintext] [random padding]

X3DH Key Agreement

Full X3DH with identity binding (no signed pre-keys):

QR / Mutual Exchange (Symmetric)

Both sides:
  ephemeral ← generate X25519 keypair
  shared_bytes ← DH(our_ephemeral_secret, their_ephemeral_public)

  // Transcript binding: all four public keys sorted lexicographically
  // and appended to info, preventing identity misbinding attacks
  info ← "vauchi-x3dh-symmetric-v2" || sort(id_lo, id_hi) || sort(eph_lo, eph_hi)
  shared ← HKDF(ikm=shared_bytes, salt=None, info=info)

NFC/BLE Exchange

Same as Mutual QR — fresh ephemeral keys on both sides, HKDF-derived shared secret.

Double Ratchet

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                                                         DOUBLE RATCHET                                                                          │
│                                                                                                                                                                 │
│                                                                                                                                                                 │
│ ┌──────────────────────────────────────┐ ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────┐          │
│ │             DH RATCHET               │ │                                             SYMMETRIC RATCHET                                             │          │
│ │                                      │ │                                                                                                           │          │
│ │                                      │ │                                                                                                           │          │
│ │ ┌──────────────────────────────────┐ │ │ ┌─────────────────────────────────────────────────┐     ┌───────────────────────────────────────────────┐ │          │
│ │ │                                  │ │ │ │                                                 │     │                                               │ │          │
│ │ │ our_dh_secret × their_dh_public  │ │ │ │                    chain_key                    │     │                       DH                      ├─┼──────┐   │
│ │ │                                  │ │ │ │                                                 │     │                                               │ │      │   │
│ │ └─────────────────┬────────────────┘ │ │ └─────────────────────────────────────────────────┘     └───────────────────────────────────────────────┘ │      │   │
│ │                   │                  │ │                          │                                                                                │      │   │
│ │                   │                  │ │                          │                                                                                │      │   │
│ │                   │                  │ │                          ├──────────────────────────────────────────────────────┐                         │      │   │
│ │                   │                  │ │                          │                                                      │                         │      │   │
│ │                   ▼                  │ │                          ▼                                                      ▼                         │      ▼   │
│ │ ┌──────────────────────────────────┐ │ │ ┌─────────────────────────────────────────────────┐     ┌───────────────────────────────────────────────┐ │   ┌────┐ │
│ │ │                                  │ │ │ │                                                 │     │                                               │ │   │    │ │
│ │ │                                  │ │ │ │                                                 │     │                                               │ │   │    │ │
│ │ │  HKDF(root_key, shared_secret,   │ │ │ │ HKDF(chain_key, &quot;Vauchi_Message_Key&quot;) │     │ HKDF(chain_key, &quot;Vauchi_Chain_Key&quot;) │ │   │ SR │ │
│ │ │ &quot;Vauchi_Root_Ratchet&quot;) │ │ │ │            → message_key (single use)           │     │                → next_chain_key               │ │   │    │ │
│ │ │                                  │ │ │ │                                                 │     │                                               │ │   │    │ │
│ │ └─────────────────┬────────────────┘ │ │ └─────────────────────────────────────────────────┘     └───────────────────────────────────────────────┘ │   └────┘ │
│ │                   │                  │ │                                                                                                           │          │
│ │                   │                  │ └───────────────────────────────────────────────────────────────────────────────────────────────────────────┘          │
│ │                   │                  │                                                                                                                        │
│ │                   │                  │                                                                                                                        │
│ │                   ▼                  │                                                                                                                        │
│ │ ┌──────────────────────────────────┐ │                                                                                                                        │
│ │ │                                  │ │                                                                                                                        │
│ │ │  "[new_root_key, new_chain_key   │ │                                                                                                                        │
│ │ │                                  │ │                                                                                                                        │
│ │ └──────────────────────────────────┘ │                                                                                                                        │
│ │                                      │                                                                                                                        │
│ └──────────────────────────────────────┘                                                                                                                        │
│                                                                                                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Limits:

  • Max chain generations: 2000
  • Max skipped keys stored: 1000
  • Message key deleted immediately after use

Ratchet Message (Authenticated, Not Encrypted Header)

#![allow(unused)]
fn main() {
RatchetMessage {
    dh_public: [u8; 32],      // Current DH public key
    dh_generation: u32,       // DH ratchet step counter
    message_index: u32,       // Message index in current chain
    previous_chain_length: u32, // Messages sent in previous chain
    ciphertext: Vec<u8>,      // Encrypted payload
}
}

Header (44 bytes) bound as AEAD associated data (tag 0x03).

Backup Format

v3 — Full Backup (Current)

[0x03] || salt(16) || ciphertext
  • Key derivation: Argon2id (m=64MB, t=3, p=4), followed by HKDF-SHA256 with domain separation b"vauchi-backup-v3"
  • Cipher: XChaCha20-Poly1305
  • Plaintext: JSON FullBackupEnvelope
FullBackupEnvelope {
  version, created_at,
  sections: {
    identity:  { display_name, master_seed_b64, device_index, device_name },
    contacts:  [ ... ],
    own_card:  ContactCard?,     // optional
    labels:    [ LabelSection ]  // optional
  }
}

The v3 envelope carries the full account: identity master seed, contacts, your own card, and labels. Per-contact Double Ratchet state is not included — it is ephemeral by design and re-established on the next sync after restore. Source: core/vauchi-core/src/backup/full_backup.rs.

v2 — Identity-Only (Legacy)

[0x02] || salt(16) || ciphertext
  • Key derivation: Argon2id (m=64MB, t=3, p=4)
  • Cipher: XChaCha20-Poly1305
  • Plaintext: display_name_len(4) || display_name || master_seed(32) || device_index(4) || device_name_len(4) || device_name
  • Status: Superseded by v3. Read path retained for migration; new backups are written as v3.

v1 (Removed)

salt(16) || nonce(12) || ciphertext || tag(16)
  • Key derivation: PBKDF2-HMAC-SHA256
  • Cipher: AES-256-GCM
  • Status: Removed from codebase. Documented for format reference only.

Transport Encryption

The shipping client (vauchi-core) talks to the relay over the HTTP v2 protocol: a synchronous request/response API (suited to contact-card sync, not real-time chat), carried over TLS 1.3 with SPKI certificate pinning. The WebSocket + Noise modules were removed from the client in favour of HTTP v2 (core/vauchi-core/src/network/mod.rs: "websocket and noise modules removed — relay uses HTTP v2 transport").

Oblivious HTTP (OHTTP, RFC 9458)

To unlink the client's IP address from its requests, HTTP v2 requests are encapsulated with OHTTP and relayed through an independent OHTTP gateway:

  • Gateway key config is fetched from GET /v2/ohttp-key (application/ohttp-keys).
  • Each request is encapsulated single-use (OhttpClient::encapsulate), HPKE-sealed to the gateway.
  • ADR-037 requires the gateway operator and the relay operator to be distinct entities — the gateway sees the client IP but not the (sealed) request; the relay sees the request but only the gateway's IP. Neither sees both.

Source: core/vauchi-core/src/network/http_transport.rs, core/vauchi-core/src/network/ohttp_client.rs, relay/src/ohttp_gateway.rs.

Mailbox Routing (Daily-Rotating Tokens)

The relay routes without identities. Messages are addressed to daily-rotating mailbox tokens, HKDF(shared_key, day_epoch, "Vauchi_Mailbox_v1"), which both parties derive independently for a given UTC day (day_epoch = unix_time / 86400). Clients register today's and yesterday's tokens to absorb clock skew. Source: core/vauchi-core/src/network/mailbox_token.rs.

Noise NK (Removed — Historical)

The original relay transport was a Noise NK inner layer (Noise_NK_25519_ChaChaPoly_BLAKE2s) over WebSocket, as defense-in-depth inside TLS. It is fully retired: the shipping client migrated to HTTP v2 + OHTTP (ADR-004, superseded), and the relay's Noise NK implementation — the snow dependency and the Noise transport — was deleted. The relay keeps only its X25519 identity keypair, from which its Ed25519 federation signing key is derived; no client or relay performs a Noise handshake. (A residual relay identity public key field still rides in the discovery payload, unused by clients.)

Security Properties

PropertyMechanism
ConfidentialityXChaCha20-Poly1305 encryption
IntegrityAEAD authentication tag
AuthenticityEd25519 signatures
Forward SecrecyDouble Ratchet, message keys deleted
Break-in RecoveryDH ratchet with ephemeral keys
No Nonce ReuseRandom 24-byte nonces
Memory Safetyzeroize on drop for all keys
Traffic Analysis PreventionStandardized bucket-size message padding
Replay PreventionDouble Ratchet counters
Transport EncryptionTLS 1.3 + SPKI certificate pinning (HTTP v2)
Network-Location PrivacyOblivious HTTP (RFC 9458), independent gateway
Unlinkable RoutingDaily-rotating mailbox tokens

Source Files

Paths are in the core repository.

Threat Model

Formal threat analysis of the Vauchi system using the STRIDE framework.


System Context

Vauchi is a contact card exchange system, not a messaging app:

  • Traffic pattern: Generated when users physically meet (exchange) or update contact info (small delta to all contacts)
  • Data type: Contact cards (name, phone, email, address, social handles)
  • Exchange model: In-person only via QR code, NFC, or BLE
  • Update frequency: Infrequent (users rarely change phone numbers or emails)
  • Update size: Small (delta of changed fields, typically < 1 KB)

This context shapes the threat model: low traffic volume and infrequent updates reduce the value of traffic analysis compared to messaging apps.

Trust Boundaries

┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                                   CLIENT DEVICE                                                    │
│                                                                                                                    │
│                                                                                                                    │
│ ┌───────────────────────────────────────────────────────┐     ┌────────────────────────┐     ┌───────────────────┐ │
│ │                                                       │     │                        │     │                   │ │
│ │                                                       │     │                        │     │                   │ │
│ │                      vauchi-core                      ├┄┄┐  │        Local DB        │     │ Platform Keychain │ │
│ │                   (crypto, protocol)                  │  ┆  │  (encrypted at rest)   │     │                   │ │
│ │                                                       │  ┆  │                        │     │                   │ │
│ └───────────────────────────┬───────────────────────────┘  ┆  └────────────────────────┘     └───────────────────┘ │
│                             │                              ┆                                                       │
└────────────────────TLS─+─E2E┼encrypted─────────────────────┆───────────────────────────────────────────────────────┘
                              │                              └┄┄┄┄┄┄┄┄┄┄┄optional                                     
                              │                                              ┆                                        
                              ▼                                              ▼                                        
  ┌───────────────────────────────────────────────────────┐     ┌────────────────────────┐                            
  │                                                       │     │                        │                            
  │        SELF-HOSTED REVERSE PROXY (nginx/caddy)        │     │                        │                            
  │        • Strips all client-identifying headers        │◄via┄┤ Optional: SOCKS5 proxy │                            
  │         • Relay never sees client IP addresses        │     │    (Tor, VPN, etc.)    │                            
  │                                                       │     │                        │                            
  └───────────────────────────┬───────────────────────────┘     └────────────────────────┘                            
                              │                                                                                       
                      Internal│network                                                                                
                              │                                                                                       
                              │                                                                                       
                              ▼                                                                                       
  ┌───────────────────────────────────────────────────────┐                                                           
  │                                                       │                                                           
  │                                                       │                                                           
  │         OHTTP LAYER (RFC 9458) — optional path        │                                                           
  │   • OHTTP relay: sees client IP, cannot read content  │                                                           
  │ • Gateway: decrypts content, sees only OHTTP relay IP │                                                           
  │ • No single hop sees both client identity and request │                                                           
  │                                                       │                                                           
  └───────────────────────────┬───────────────────────────┘                                                           
                              │                                                                                       
                              │                                                                                       
                              │                                                                                       
                              │                                                                                       
                              ▼                                                                                       
  ┌───────────────────────────────────────────────────────┐                                                           
  │                                                       │                                                           
  │                                                       │                                                           
  │                      RELAY SERVER                     │                                                           
  │        • Assumed compromised (oblivious design)       │                                                           
  │     • Sees only encrypted blobs, never client IPs     │                                                           
  │         • No user accounts, no decryption keys        │                                                           
  │              • Store-and-forward with TTL             │                                                           
  │   • Timing obfuscation: sync jitter, payload padding  │                                                           
  │                                                       │                                                           
  └───────────────────────────────────────────────────────┘                                                           
BoundaryProtectionTrust Level
Client ↔ Rev. ProxyTLSTrusted (self-hosted)
Rev. Proxy ↔ RelayInternal netUntrusted (no client IPs)
Client ↔ OHTTP ↔ GWOHTTP (9458)No hop sees both ID + content
Client ↔ ClientE2E (X3DH + DR)Verified in person
Device ↔ DeviceHKDF device keysTrusted (same seed)
Contact ↔ ContactPer-contact CEKVerified at exchange

Assets

AssetSensitivityDescription
Contact card dataHighPersonal info (phone, email, address)
Identity keysCriticalEd25519 signing + X25519 exchange keys
Master seedCritical256-bit root secret for all key derivation
Social graphHighWho knows whom (contact relationships)
Update metadataMediumWhen someone changed their info

Adversary Model

AdversaryCapabilityMotivation
Passive observerEncrypted traffic onlySurveillance
Malicious relayBlobs + timing, no IPsData harvesting
Harvest-now adversaryRecords ciphertext nowDecrypt at Q-Day
OHTTP relayClient IP, no contentCorrelation
Compromised deviceFull device accessTargeted attack
Physical attackerSteals/seizes deviceLaw enforcement
Malicious contactLegitimate contactStalking

STRIDE Analysis

Spoofing

Threat: Attacker impersonates a contact or creates a fake identity.

Mitigations:

  • Ed25519 identity keys bound to each user
  • Full-trust contact exchange requires physical presence (QR, NFC, or BLE with proximity check)
  • Opt-in remote discovery establishes reduced-trust contacts (Tier 0/1) with restricted visibility
  • No trust-on-first-use for contact verification: you verify who you connect with in person
  • Device registry is cryptographically signed; unauthorized devices cannot be added

Residual risk: Social engineering (convincing someone to scan a QR code under false pretenses).

Tampering

Threat: Relay or network attacker modifies messages in transit.

Mitigations:

  • AEAD encryption (XChaCha20-Poly1305) detects any modification via authentication tag
  • Ed25519 signatures verify sender authenticity
  • Double Ratchet counters and per-message nonces detect replay and reordering
  • Device registry version numbers prevent rollback

Residual risk: None. Tampering is cryptographically detected and rejected.

Repudiation

Threat: User denies having sent an update.

Design decision: Repudiation is a privacy feature, not a bug. Vauchi intentionally does not provide non-repudiation for contact card updates. Users should be able to update or remove their information without permanent proof of past states.

Information Disclosure

Threat: Unauthorized access to contact card data.

Mitigations:

  • All data E2E encrypted with XChaCha20-Poly1305 before leaving the device
  • Per-contact encryption keys (CEK) derived via X3DH + Double Ratchet
  • Forward secrecy: each message uses a unique key, deleted after use
  • Relay stores only encrypted blobs (oblivious privacy-preserving design)
  • Local storage encrypted with device-derived keys (SEK from HKDF key hierarchy)
  • Sensitive key material zeroized on drop (zeroize crate)
  • Message padding to fixed buckets (256 B, 512 B, 1 KB, 4 KB) prevents size-based inference

Residual risk: Metadata (connection timing, recipient pseudonyms) visible to relay. Mitigated by the four-layer privacy architecture: reverse proxy (strips client IPs), OHTTP (cryptographic content protection), timing obfuscation (sync jitter + padding), and optional SOCKS5 proxy support.

Denial of Service

Threat: Attacker disrupts relay availability.

Mitigations:

  • Token-bucket rate limiting (60 msgs/min per client, configurable)
  • Stricter recovery rate limit (10 queries/min, anti-enumeration)
  • Connection limit (max 1000 concurrent, RAII guard)
  • Multi-layer timeout protection: handshake timeout, idle timeout (5 min)
  • Message size limit (1 MB)
  • Automatic message expiration (120-day TTL; recovery store: 90-day TTL)
  • Federation support for relay redundancy

Residual risk: Sustained DDoS from many source IPs can overwhelm a single relay.

Elevation of Privilege

Threat: Attacker gains unauthorized capabilities.

Mitigations:

  • No user accounts on relay: no admin interface, no privilege levels
  • Client capabilities limited to own identity (can only decrypt own messages)
  • Master seed required for all identity operations
  • Device linking requires physical QR scan + identity key signature verification

Residual risk: Compromised device with master seed has full identity control. Mitigated by device revocation broadcast.

Key Security Properties

PropertyMechanism
ConfidentialityXChaCha20-Poly1305 E2E encryption
IntegrityAEAD authentication tag + Ed25519 signatures
Forward secrecyDouble Ratchet with ephemeral DH keys
Break-in recoveryDH ratchet step generates new key material
Oblivious relayEncrypted blobs only, no keys
Physical verificationQR + audio / NFC / BLE (full); SAS (video)
Traffic analysis resist.Bucket padding + routing tokens + jitter
IP privacyReverse proxy + OHTTP (RFC 9458)
Memory safetyRust (no unsafe in crypto paths) + zeroize on drop
Replay preventionDouble Ratchet counters + version numbers

Attack Scenarios

Relay Compromise

If an attacker gains full control of a relay:

  • Cannot read contact card data (E2E encrypted)
  • Cannot forge messages (AEAD + signatures)
  • Can see pseudonymous recipient IDs and message timing
  • Can drop or delay messages (detected by delivery acknowledgments)
  • Cannot see client IP addresses (reverse proxy strips headers; OHTTP encrypts requests to gateway)
  • Can observe connection timing patterns (mitigated by timing obfuscation: 30s-5min post-exchange jitter, ±15% sync interval jitter)

Device Theft

If an attacker steals a device:

  • Master seed encrypted with user password via Argon2id (m=64 MB, t=3, p=4)
  • Platform-native key storage (macOS Keychain, iOS Keychain, Android KeyStore)
  • All key material zeroized on drop
  • Weak passwords can be brute-forced (Argon2id raises cost significantly)
  • Recommendation: Use a strong backup password, enable OS-level device encryption

Recovery Impersonation

If an attacker tries to abuse social recovery:

  • Must meet K contacts in person (default threshold: 3)
  • Each voucher signs with their identity key
  • Vouchers validated against victim's known contact list
  • Conflicting claims detected and flagged by relay
  • Voucher timestamps prevent replay (48-hour window)

Federation Security

When relays federate (forward blobs to peer relays for redundancy), additional trust boundaries apply.

What a federated relay CAN see:

  • Recipient routing IDs (pseudonymous public key hashes)
  • Blob sizes (padded to fixed buckets: 256, 512, 1024, 4096 bytes)
  • Message creation timestamps and hop count
  • Network topology via gossip protocol

What a federated relay CANNOT see:

  • Message content (E2E encrypted)
  • Sender identity (not included in federation protocol)
  • Real identity behind routing IDs
  • Client IP addresses (federation is relay-to-relay; clients are behind reverse proxy + OHTTP)

Guarantees:

  • mTLS authentication prevents unauthorized peers
  • Hop count limit prevents amplification attacks
  • SHA-256 integrity verification on all federated blobs
  • DNS rebinding protection: explicit resolution + SSRF validation before connecting to peer relays

Device Linking (STRIDE)

CategoryThreatMitigation
SpoofingScan link QR5-min expiry, proximity
TamperingModified QRSigned by identity key
Info DisclosureSeed interceptedEphemeral key encryption
DoSExcess linkingMax 10 devices
Elev. of PrivilegeUnauth deviceSigned registry + version

Known limitation: Currently, the full master seed is shared during device linking (not per-device subkeys). A compromised linked device gains full identity control. Per-device subkey isolation is planned for 1.0 release.

Remote Discovery (Opt-In)

When remote discovery is enabled, users can generate discovery tokens that allow contacts to be established without physical proximity. This introduces a new attack surface that is mitigated by graduated trust tiers.

Trust Tiers

TierNameEstablished ViaCapabilities
0PendingToken-based contact requestView-only, expires after 30 days
1AcceptedRecipient accepts requestEveryone-visibility fields only
2VideoVerifiedSAS + liveness (video)Label-based visibility
3InPersonPhysical QR/NFC/BLEFull access, recovery

Recovery and facilitated introductions remain gated to Tier 3 (InPerson) only.

Attack Surface

ThreatMitigation
Token spamExpiry, one-time use, rate limit
Phishing tokenTier 0 only, no sensitive fields
Social eng. to T1Everyone fields only
Video MITMSAS 6-digit code mismatch
Deepfake/replayFinger-count liveness challenge
Sender leakSealed sender, ephemeral IDs
Recipient leakDaily-rotating mailbox tokens
Token replayEd25519 sig + expiry + one-time
Mailbox correlationDaily rotation per-contact pair

Sealed Sender Properties

Sealed sender is always enabled — it improves metadata protection for all users, not just remote contacts. With sealed sender delivery, the relay sees:

  • Session ID: Ephemeral, per-connection (no identity key)
  • Mailbox token: Opaque, daily rotation, derived from shared key
  • Encrypted blob: AEAD ciphertext, padded to fixed buckets

The relay cannot determine: who sent a message, who the real recipient is (beyond the opaque token), or whether two sessions belong to the same user across reconnections.

Residual Risks

RiskSev.Notes
Weak remote trustLowTier gating limits to Everyone
Untrusted token channelsMedUser responsibility
Compromised video platformLowSAS binding is independent
Mailbox token correlationLowBrief overlap, mitigated by OHTTP

Known Limitations

LimitationImpactMitigation
Relay sees metadataGraph inferenceSealed sender, 4-layer arch
Device compromiseFull seed exposedSubkeys (planned), passwords
Linked device = full seedFull identitySubkeys (planned)
Single relay SPOFNo sync if downFederation (planned)
Blocked contacts keep dataCan't unsendBy design
Recovery leaks voucher PKsPartial graphAccepted tradeoff
No guardian diversitySame circleUX warnings (planned)
No key transparencyNot auditableSigned key log (future)
Remote trust weakerNo in-personTier gating; upgrade path
Push leaks metadataDelivery timingEmpty push + fetch
Quantum HNDLCiphertext readable at Q-DayHybrid ML-KEM (ADR-060)

Core-UI Trust Boundary

The vauchi-core library is consumed by multiple UI layers (macOS/SwiftUI, Linux-GTK, Linux-Qt, CLI, TUI, mobile Swift/Kotlin via UniFFI). The trust relationship between core and its callers:

What core trusts from UI:

  • UI provides correct file paths for storage
  • UI invokes lifecycle methods in the correct order (init before use)
  • UI does not hold references to sensitive data after core has zeroized them

What core does NOT trust from UI:

  • Input lengths: Core enforces maximum lengths at its API boundary (display name: 100 chars, field value: 1000 chars, field label: 64 chars, card size: 64 KB, avatar: 256 KB)
  • Field values: Core validates phone/email format, URL safety, and rejects malformed data
  • Contact IDs: Core verifies contact existence before processing updates
  • Ratchet messages: Core validates signatures, AEAD tags, and replay nonces before accepting peer data
  • File content: Core verifies checksums on all content fetched from remote servers

A compromised or buggy UI layer cannot cause vauchi-core to:

  • Decrypt data for an unauthorized recipient
  • Produce unsigned or weakly signed messages
  • Skip replay detection or signature verification
  • Bypass visibility label enforcement

UniFFI surface note: The UniFFI binding layer does not add rate limiting at the API boundary. Rate limiting for relay operations is enforced server-side. UI layers should implement their own rate limiting for user-facing operations to prevent accidental rapid-fire calls.

Relay Metadata Exposure Analysis

While the relay sees only encrypted blobs, it observes metadata that can reveal the social graph:

What the relay sees:

DatumVisibilityExample
Sender (session ID)Per-connectionEphemeral, no identity key
Recipient (mailbox)Daily rotationHKDF-derived, opaque
Message timingObfuscatedJittered 30s-5min / ±15%
Message sizeApproximatePadded to buckets
Connection freq.ApproximateJittered intervals

What the relay CANNOT see:

  • Client IP addresses (stripped by self-hosted reverse proxy; OHTTP provides additional cryptographic separation)
  • Message content (E2E encrypted, AEAD)
  • Contact card fields (encrypted under per-contact CEK)
  • Sender identity beyond pseudonymous routing ID
  • Which specific contact fields changed

Risk assessment for Vauchi:

The social graph inference risk is lower than for messaging apps because:

  1. Updates are infrequent (users rarely change contact info)
  2. Updates are small and padded to fixed buckets (256 B, 512 B, 1 KB, 4 KB)
  3. All locale files are downloaded in bulk to prevent language inference
  4. Routing IDs are pseudonymous and session-scoped
  5. Relay never sees client IPs (reverse proxy strips headers; OHTTP provides cryptographic separation)
  6. Timing obfuscation (sync jitter, post-exchange delay) prevents correlation of connection patterns

Current mitigations (four-layer privacy architecture):

  1. Self-hosted reverse proxy (nginx/caddy) — strips all client-identifying headers before forwarding to relay. The relay provably never touches client IP addresses.
  2. OHTTP (RFC 9458) — cryptographic content protection. The OHTTP relay sees the client IP but cannot read request content (encrypted to gateway). The gateway decrypts but only sees the OHTTP relay's internal IP. No single hop sees both client identity and request content.
  3. Timing obfuscation — post-exchange sync jitter (30s-5min random delay), sync interval jitter (+-15%), payload padding to bucket sizes. Applied to all users by default.
  4. Generic SOCKS5 proxy support — optional, user-configured. Route through Tor, VPN, or any SOCKS5 proxy for ISP-level hiding.

Additional mitigations: routing token rotation, suppress_presence flag, standardized bucket-size message padding.

Why OHTTP over Tor: Tor's 90-120s mobile bootstrap latency is fatal for adoption. Tor traffic is banned or flagged in countries that need privacy most (China, Iran, Russia). OHTTP traffic is indistinguishable from normal HTTPS — no special protocol signatures. The four-layer architecture provides equivalent metadata protection for Vauchi's threat model without the usability and censorship-resistance penalties.

Future considerations: Private Information Retrieval (PIR) could eliminate recipient pseudonym visibility entirely. Cover traffic patterns could further reduce timing correlation. These are not currently prioritized given Vauchi's low-frequency traffic pattern, but remain on the architectural roadmap for high-threat deployments.

Key Transparency

Current Model

Vauchi uses a trust-on-exchange model: during the initial in-person exchange (QR/NFC/BLE), both parties verify each other's Ed25519 identity keys directly. This provides strong initial authentication.

After the exchange, contacts receive updates via the Double Ratchet:

  • If a user adds a new device, it derives keys from the same master seed
  • Contacts receive a DeviceRegistry update signed by the existing identity key
  • The ratchet provides forward secrecy and break-in recovery

Gap

There is no append-only, auditable log of a user's key history. A sophisticated attacker who compromises both a relay and a user's device could theoretically:

  1. Generate a new identity key for the victim
  2. Distribute it to the victim's contacts via the compromised relay
  3. Contacts would have no way to verify this is consistent with the victim's key history

Accepted Tradeoff

This attack requires simultaneous compromise of both the relay and the target device, which is beyond Vauchi's primary threat model (relay-only compromise). The in-person exchange provides a strong root of trust that subsequent key changes cannot easily override without raising suspicion (contacts would see unexpected re-exchange requests).

Future Direction

A lightweight key transparency mechanism could provide additional assurance:

  • Signed key history: Each user maintains a signed append-only log of their key changes. Contacts can audit this log to detect unauthorized key modifications.
  • Cross-contact verification: Contacts can compare their view of a user's key history with other contacts to detect divergence (split-world attack detection).

This is not currently implemented but is documented as a future enhancement for high-assurance deployments. A full CONIKS-style transparency log is not necessary given Vauchi's decentralized architecture and low update frequency.

Push Notification Constraints

Push notifications (APNs for iOS, FCM for Android) are not currently implemented. If they are added in the future, the following constraint is mandatory:

Threat: Naive push notifications would expose message receipt timing to Apple/Google. Even though the relay sees only encrypted blobs, a push notification would link a specific device token (tied to a real Apple/Google account) to the exact moment a Vauchi message arrives. This undermines the oblivious privacy-preserving relay design.

Required pattern: Empty push + app-side fetch.

  1. The relay sends a push notification with no payload and no sender information — only a "wake up" signal
  2. The app receives the push, connects to the relay independently over TLS
  3. The app fetches pending messages using its normal encrypted channel
  4. Apple/Google learn only that the app was woken up, not who sent a message or what it contains

This pattern is used by Signal and other privacy-focused applications. Any implementation of push notifications in Vauchi must follow this pattern. Direct payload delivery via push is explicitly prohibited.

Cryptographic Primitives

PurposeAlgorithmLibrary
SigningEd25519ed25519-dalek
Key exchangeX25519x25519-dalek
Symmetric encryptionXChaCha20-Poly1305chacha20poly1305
Password KDFArgon2idargon2
Key derivationHKDF-SHA256hkdf
CSPRNGOsRngrand
TLSTLS 1.2/1.3rustls (aws-lc-rs backend)

Post-quantum: key agreement is classical-only today. A hybrid X25519 + ML-KEM-768 upgrade is planned (ADR-060) to close the harvest-now-decrypt-later gap on relay-carried ciphertext. Ed25519 signatures are not subject to harvest-now-forge-later, so they migrate later, not now.

For the full cryptographic specification, see the Cryptography Reference.

Comparison with Messaging Apps

AspectVauchiMessaging Apps
Traffic volumeVery lowHigh (continuous)
Timing analysisLow (jittered)High
Social graphLow (no IPs)High
MetadataRecipient ID onlySender + recip + IPs
IP privacyProxy + OHTTPOften server sees IPs
Forward secrecyYes (DR)Varies
Relay knowledgeEncrypted blobsOften plaintext
RecoverySocial vouchingPhone/cloud backup

Vauchi's infrequent, small updates significantly reduce the value of traffic analysis compared to messaging apps.

Security Reporting

Found a vulnerability? Please report it responsibly:

Email: security@vauchi.app

We acknowledge reports within 48 hours and do not pursue legal action against good-faith researchers.

Contributing to Vauchi

Quick Start

# Clone and setup workspace
git clone git@gitlab.com:vauchi/vauchi.git
cd vauchi
just setup

# Build and test
just build
just check

Development Workflow

All repos have protected main branches — no direct pushes, merge via MR only, force push disabled.

Step 1: Create a branch

git checkout -b feature/my-feature

Branch naming: {type}/{short-description}

TypeUse Case
feature/New functionality
bugfix/Bug fixes
refactor/Code restructuring
tidy/Structural-only cleanup (Tidy First)
investigation/Research/exploration

For multi-repo features, use the same branch name in every affected repo:

just git branch feature/remote-content-updates features core docs

Step 2: Do the work — commit often

  • If changing code: strict TDD. Tidy -> Red -> Green -> Refactor. No exceptions.
    1. Tidy first — small structural improvement if the code is hard to change (own tidy: commit)
    2. Write failing test first (Red)
    3. Write minimal code to pass (Green)
    4. Refactor
    5. Tests must trace to features/*.feature scenarios
  • Commit atomically — each commit should be a single logical change.
  • Run just check before pushing (formats, lints, tests).
  • For tighter loops, run only what you changed: just test-diff runs each touched repo's tests (Rust, web, Android) scoped to your branch's diff, and just mutants-diff runs mutation testing on just the changed Rust lines. Both are on-demand — they are not push gates.
  • See Principles for core values.

Commit message format:

{type}: {Short description}

{Longer description if needed}

Types: feat, fix, refactor, tidy, docs, test, chore

Use imperative mood: "Add feature" not "Added feature". Keep first line under 72 characters.

Use just commit for interactive commits across all repos with changes.

Step 3: Create a Merge Request

# Push all repos with changes (runs pre-push checks)
just git push-all

# Create MR for a specific repo
just gitlab mr-create core

CI must pass (security scans + tests) before merge.

If the work spans multiple repos, each MR must list the related MRs it depends on:

## Summary
- Bullet points of changes

## Related MRs
- vauchi/features!42 - Gherkin specs
- vauchi/core!78 - Implementation

## Test Plan
- [ ] Tests pass
- [ ] Manual testing done

## Checklist
- [ ] Follows TDD
- [ ] Documentation updated
- [ ] Feature file updated (if applicable)
- [ ] Follows [GUI Guidelines](gui-guidelines.md)
  and [UX Guidelines](ux-guidelines.md)
  (if UI changes)

Use GitLab MR reference format: {group}/{project}!{mr_number}

Code Guidelines

Rust

  • Use RustCrypto crates: audited (ed25519-dalek, x25519-dalek — Trail of Bits), IETF-standardized (chacha20poly1305, argon2), well-established (sha2, hmac, hkdf); aws-lc-rs for TLS only (via rustls)
  • Never mock crypto in tests
  • 90%+ test coverage for vauchi-core
  • Use Result/Option, fail fast

Structure

crate/
├── src/      # Production code only
└── tests/    # Integration tests only

Mobile Bindings Workflow

UniFFI generates Swift/Kotlin bindings from Rust. When modifying vauchi-platform or vauchi-core:

When to Regenerate Bindings

Regenerate bindings when you:

  • Add/remove/rename exported types or functions
  • Change function signatures
  • Add new #[uniffi::export] or #[derive(uniffi::*)] annotations

How to Regenerate

cd core

# IMPORTANT: Build without symbol stripping to preserve metadata
RUSTFLAGS="-Cstrip=none" cargo build -p vauchi-platform --release

# Regenerate for both platforms (macOS required for iOS)
./scripts/build-bindings.sh

# Or regenerate Android only (works on Linux)
./scripts/build-bindings.sh --android

# Validate bindings have all expected types
./scripts/validate-bindings.sh

Common Issues

Empty or incomplete bindings:

  • Cause: Library built with symbol stripping (default in release)
  • Fix: Use RUSTFLAGS="-Cstrip=none" when building

Missing types in generated code:

  • Run validate-bindings.sh to check expected types
  • Regenerate with the steps above

Repository Organisation

This is a multi-repo project under the vauchi GitLab group. The root repo (vauchi/vauchi) is the workspace orchestrator — just setup clones all sub-repos as sibling directories. Each subdirectory is its own Git repo at gitlab.com/vauchi/<name>.

vauchi/                          ← root repo (justfile, CI config)
│
├── core/                        ← Rust workspace: vauchi-core + vauchi-platform (UniFFI)
├── relay/                       ← HTTP v2 relay server (standalone Rust)
├── cli/                         ← Command-line interface
├── tui/                         ← Terminal user interface
│
├── android/                     ← Kotlin/Compose native app
├── ios/                         ← SwiftUI native app
├── macos/                       ← SwiftUI macOS app
├── linux-gtk/                   ← GTK4 + libadwaita Linux app
├── linux-qt/                    ← Qt6 Linux app
├── windows/                     ← WinUI 3 Windows app
├── web-demo/                    ← SolidJS + WASM demo app
├── vauchi-platform-swift/       ← Generated Swift bindings + XCFramework (SPM distribution)
│
├── e2e/                         ← End-to-end tests
├── features/                    ← Gherkin specs (shared across all platforms)
├── locales/                     ← i18n locale files
├── themes/                      ← Design tokens
├── ohttp-relay/                 ← OHTTP relay proxy
│
├── docs/                        ← Public documentation (this site)
├── scripts/                     ← Dev tools, hooks, utilities
├── website/                     ← Landing page source
└── assets/                      ← Brand assets, logos

Platform bindings (vauchi-platform-swift/) are not manually editedcore/ CI generates UniFFI bindings and pushes artifacts to this repo when merging to main. Android bindings are distributed as a Maven AAR published by core CI.

Release Workflow

Vauchi uses a 3-tier versioning system. Each tier triggers a different CI pipeline scope:

TierTag FormatWhat RunsPublishes?
Devv0.2.3-dev.NLint + test onlyNo
RCv0.2.3-rc.NLint + test + coverage + mutation + securityNo
PRODv0.2.3Full release: build, package, publish, deployYes

Creating releases

just release-dev [repo]      # Fast feedback — default: core. E.g., just release-dev cli
just release-rc [repo]       # Full quality gate. E.g., just release-rc relay
just release-prod [repo]     # Full release. E.g., just release-prod core
just release-history [repo]  # Show promotion chain. E.g., just release-history tui

Typical flow

  1. Merge feature MRs to main
  2. just release-dev — verify basic CI passes (~2 min)
  3. just release-rc — full quality gate with coverage + mutation (~15 min)
  4. just release-prod — publish to package registry, trigger mobile binding distribution

Dev and RC tags never publish artifacts, trigger mobile repos, or create GitLab releases. Only PROD tags do.

Useful Commands

just help              # Show all commands
just check-annotations # Check test coverage vs features
just test-diff         # Test only the repos you changed (scoped to branch diff)
just mutants-diff      # Mutation-test only the Rust lines you changed
just relay             # Start local relay for testing
just run cli           # Run CLI
just git sync          # Fetch all + pull where on main

Getting Help

License

By contributing, you agree that your contributions will be licensed under GPL-3.0-or-later.

Technology Stack

Core Library (Shared)

ComponentTechnologyNotes
LanguageRustMemory safety, cross-platform
Cryptoed25519-dalek, x25519-dalek, chacha20poly1305, argon2Audited (ed25519/x25519: Trail of Bits) + IETF-standardized (chacha20/argon2)
StorageSQLiteEncrypted with XChaCha20-Poly1305
Serializationserde + JSONProtocol messages
Networkingureq (HTTP v2) + ohttp (OHTTP, RFC 9458)Client talks to the relay over the synchronous /v2/ HTTP API, wrapped in Oblivious HTTP through an independent gateway to hide the client IP
TLSrustls (aws-lc-rs)TLS 1.3 with SPKI certificate pinning
FFIUniFFISwift/Kotlin bindings

Mobile Apps

iOS

ComponentTechnology
UI FrameworkSwiftUI
LanguageSwift
BindingsUniFFI (SPM package)
Min iOS15.0

Android

ComponentTechnology
UI FrameworkJetpack Compose
LanguageKotlin
BindingsUniFFI (Gradle dependency)
Min SDK26 (Android 8.0)

Desktop Apps (Native)

PlatformFrameworkLanguageBindings
macOSSwiftUISwiftUniFFI (SPM)
Linux (GTK)GTK4 + libadwaitaRustDirect (same process)
Linux (Qt)Qt 6 (Widgets)C++C ABI (vauchi-cabi)
WindowsWinUI 3C# (.NET 8)C ABI (vauchi-cabi)

Web Demo

ComponentTechnologyNotes
FrameworkSolidJSTypeScript, WASM bridge
Corevauchi-core (WASM)wasm32-unknown-unknown target
CryptoPure RustCrypto (WASM)No WebCrypto bridge needed (SP-30)

CLI & TUI

ComponentTechnology
CLIRust (clap)
TUIRust (ratatui)

Relay Server

ComponentTechnologyNotes
LanguageRustStandalone binary
Client APIHTTP v2 (/v2/)Synchronous request/response; client WebSocket is rejected (HTTP 426)
Federationtokio-tungsteniteWebSocket used only for relay-to-relay federation, not the client path
OHTTP gatewayohttp (RFC 9458)Decrypts client requests; operated by a separate party from the relay
TLSrustls (aws-lc-rs)TLS 1.3, certificate handling
StorageIn-memory + diskEncrypted blobs only

Development Tools

ToolPurpose
JustTask runner
CargoRust package manager
npm/pnpmJavaScript dependencies
DockerContainerization
GitLab CIContinuous integration

Performance Targets

OperationTarget
Contact exchange< 3 seconds
Update propagation< 30 seconds (when online)
Local operations< 100ms
App startup< 2 seconds

Data Limits

LimitValue
Max contact card size64KB (encrypted)
Max contacts per user10,000
Max fields per card200
Max linked devices10

Repository Dependencies

vauchi-core (standalone, no workspace deps)
    ↑ (git dependency)
cli/, tui/, e2e/, macos/, windows/, linux-gtk/, linux-qt/, web-demo/

vauchi-platform (UniFFI bindings, in core/ workspace)
    ↑ (via generated binding repos)
android/ ← Maven AAR from core CI
ios/     ← vauchi-platform-swift (SPM)

vauchi-cabi (C ABI exports, in core/ workspace)
    ↑ (cbindgen)
linux-qt/, windows/

relay/ (standalone, uses vauchi-protocol for shared types only)

Downstream repos use git dependencies with branch-based pinning (branch = "main"). Local development uses .cargo/config.toml path overrides.

Glossary

Terms used across the developer documentation. Each entry links to the page (or source file) where the concept is specified.

Keys and Derivation

Master Seed — The 256-bit root secret created at identity setup. Every other key derives from it (directly or via HKDF); it is what a backup protects and what recovery restores. See Key Types.

Identity Signing Key — Ed25519 signing key. Uses the raw master seed directly (an Ed25519 requirement), not an HKDF derivation.

SMK (Shredding Master Key) — Root of the crypto-shredding hierarchy, derived once from the master seed (Vauchi_Shred_Key_v2) and held in platform secure storage. Destroying the SMK renders all locally encrypted data unreadable — that destruction is crypto shredding.

SEK (Storage Encryption Key) — Derived from the SMK (Vauchi_Storage_Key_v2); encrypts all local SQLite data at rest.

FKEK (File Key Encryption Key) — Derived from the SMK (Vauchi_FileKey_Key_v2); encrypts file key storage.

CEK (Content Encryption Key) — Random 256-bit key, one per contact, wrapping that contact's card data so a single contact's content can be shredded independently. See vauchi-core/src/crypto/cek.rs.

HKDF domain separation — Every derivation path uses a unique info string (e.g. Vauchi_Mailbox_v1); the full table is in the Cryptography Reference.

Protocol

X3DH — Extended Triple Diffie-Hellman key agreement used at contact exchange to establish the initial shared secret. See Contact Exchange.

Double Ratchet — Signal-style ratchet giving forward secrecy and break-in recovery for card updates between two contacts. Limits: 2000 chain generations, 1000 stored skipped keys. See Cryptography Reference.

Mailbox token — Anonymous, daily-rotating routing identifier derived via HKDF from the per-pair shared key (or the master seed for a device's own sync token). The relay routes by token and never sees a stable identity; public keys are never routing identifiers.

OHTTP (Oblivious HTTP) — RFC 9458 relaying that hides client IP addresses from the relay; the OHTTP gateway and the relay must be run by distinct operators. See Transport Encryption.

SPKI pinning — TLS certificate pinning against the SHA-256 hash of the server's SubjectPublicKeyInfo (RFC 7469 style), so a pin survives certificate renewal under the same key.

WBEX — The 4-byte magic prefix identifying a Vauchi exchange payload in QR codes (currently format v3). A historical protocol identifier with no current expansion. See Contact Exchange.

Data Protection

Crypto shredding — Deleting data by destroying the key that encrypts it (SMK or a per-contact CEK) instead of overwriting the ciphertext.

Duress PIN — A secondary unlock credential that shows a decoy contact list and silently alerts trusted contacts, for users unlocking under coercion. See Security.

Decoy contacts — The plausible fake contact list presented in duress mode, stored separately from real contacts.

TDD Rules

Three Laws

  1. No production code without a failing test
  2. Write only enough test to fail
  3. Write only enough code to pass

Tidy-Red-Green-Refactor-Commit

TIDY     → Small structural improvement  → COMMIT (no behavior change)
RED      → Write failing test
GREEN    → Minimal code to pass          → COMMIT (tests green)
REFACTOR → Improve design                → COMMIT (tests still green)

Inspired by Kent Beck's Tidy First?: make the change easy, then make the easy change.

TIDY is an optional pre-step before starting a Red-Green cycle. A tidying is a small, structural-only change that makes the upcoming work easier — guard clauses, extract helper, rename for clarity, reorder for readability, delete dead code. Tidyings never change behavior and always get their own commit (tidy: type).

When to tidy:

TimingGuidance
Tidy FirstDefault. The code you're about to change is hard to read or extend
Tidy AfterYou understand the shape of the change better after implementing
Tidy LaterBatch structural cleanup into a separate branch/MR
NeverCode works, won't change again, and is readable enough

Commit early, commit often:

  • Commit tidyings separately before the RED step (tidy: commit)
  • Commit immediately after GREEN (tests pass for the first time)
  • Commit after each REFACTOR cycle (if tests still pass)
  • Small, atomic commits make rollback and review easier
  • Never commit with failing tests

Tidying Catalog

Small, safe, structural-only changes (from Tidy First?):

TidyingWhat it does
Guard ClausesReplace nested if/match with early returns
Dead CodeDelete unreachable or unused code
Normalize SymmetriesMake similar code use consistent patterns
New Interface, Old ImplementationWrap before replacing internals
Reading OrderReorder declarations top-down
Cohesion OrderGroup related items together
Move Declaration and Initialization TogetherClose the gap between let and first use
Explaining VariablesName intermediate results
Explaining ConstantsReplace magic numbers with named constants
Explicit ParametersPass values instead of relying on ambient state
Chunk StatementsAdd blank lines between logical blocks
Extract HelperPull reusable logic into a function
One PileInline before re-extracting with better structure
Explaining CommentsAdd "why" comments where intent isn't obvious
Delete Redundant CommentsRemove comments that repeat the code

Test Types

TypeScopeSpeedCoverage
UnitSingle function< 100ms90% min
IntegrationMultiple components< 5sCritical paths
E2EFull system< 60sAll Gherkin scenarios

Test Behavior, Not Implementation

Test what a module promises through its public API — not how it keeps that promise, and not its internal content. A good test pins a contract the module offers its callers: it survives any refactor that preserves that contract and fails only when the contract is broken.

Test through the module's API — the surface a real caller uses:

  • Exercise the module via its exported functions, types, and interfaces.
  • Assert on inputs and outputs, returned errors, and state observable through that surface.
  • Assert the outcome of an action, not the steps taken to reach it.

Do not couple tests to implementation or internal content:

  • Private functions, fields, or internal data structures — reach them only through the public API that exercises them.
  • The specific sequence or count of internal calls — unless the call is the contract (e.g. "sends exactly one network request").
  • The concrete algorithm or data layout chosen inside the module.
  • Log lines, intermediate variables, or rendering internals.

Litmus: could this test fail while the module still honors its API contract? If yes, it is testing implementation — it will break on healthy refactors and erode trust in the suite. Rewrite it to assert behavior through the API, or delete it.

Put a behavior test where the behavior lives. If a test asserts an outcome owned by another module — business logic reached through a UI, a value persisted by a core service — it belongs with that module's API, not the caller's. A behavior test stranded in a consumer breaks whenever the boundary shifts, even though nothing it cares about changed.

The unit of isolation is the test, not the function, class, or module. Isolate tests from each other — independent, order-independent, no shared mutable state — not each function or class from its collaborators. A single test may drive several functions, classes, or modules together through one public API; that is normal, not a smell. Don't add a mock just because a collaborator exists: mock only true boundaries (network, clock, external services — see Mocking Strategy below) and use real collaborators everywhere else. (Kent Beck's classical TDD: "unit" names the test's isolation, not the code under test.)

Naming

test_<function>_<scenario>_<expected>

Examples:
- test_encrypt_valid_key_returns_ciphertext
- test_decrypt_wrong_key_fails

Critical Rules

Crypto - Never mock. Test with real crypto:

  • Roundtrip (encrypt/decrypt, sign/verify)
  • Wrong key rejection
  • Tampered data rejection

Gherkin - Every scenario in features/ must have a test.

Coverage - 90% minimum for vauchi-core.

Forbidden

  • Writing code before tests
  • Mocking crypto operations
  • #[ignore] without tracking issue
  • Flaky/non-deterministic tests
  • Hardcoded test secrets

Mocking Strategy

ComponentApproach
CryptoReal (never mock)
NetworkMock transport
StorageIn-memory DB
TimeMockable clock

PR Checklist

  • Structural tidyings in separate tidy: commits (if any)
  • Tests written before code
  • All Gherkin scenarios covered
  • No ignored tests
  • Coverage ≥ 90%
  • Crypto has security tests

GUI Design Guidelines

Cross-platform design rules for all vauchi client interfaces — smartphones, dumb-phones, smartwatches, tablets, laptops, desktops (Windows, macOS, Linux, Android, iOS).

These rules enforce Principle 4: Simplicity serves the user — vauchi stays out of your way. All GUI contributions must follow these guidelines. For interaction-level guidelines (flows, physical device usage, navigation philosophy), see the sibling document UX Interaction Guidelines.


Problem Statement

Modal dialogs and confirmation popups interrupt user flow. Every "Are you sure?" dialog forces the user to stop, read, and click — even for routine, reversible actions like deleting a contact or hiding a field. Users develop dialog fatigue: they stop reading and click through reflexively, which defeats the safety purpose entirely.

The fix is not fewer safety nets — it's better ones. Modern interfaces (Gmail, Slack, iOS Mail) prove that act-then-undo is both safer and faster than ask-then-act. Users stay in flow, and recovery from mistakes is immediate.


The Rules

UI-01: No Dialogs for Reversible Actions

If an action can be undone, do it immediately. Show a non-blocking toast/snackbar with an Undo button (5-second window). No confirmation dialog.

Examples:

ActionWrongRight
Delete contact"Are you sure?" modalToast: "Deleted. Undo"
Hide fieldConfirmation dialogToast: "Hidden. Undo"
Unlink device"Confirm unlink?" popupToast: "Unlinked. Undo"

Why: Users perform reversible actions frequently. Interrupting each one trains them to click "OK" without reading — making real confirmations invisible.


UI-02: Confirm Only Irrevocable Actions

Reserve confirmation for actions that cannot be undone: permanent identity wipe, recovery phrase reset, key deletion. These are rare by design.

When confirmation is needed, use inline confirmation — expand the action area in place with a clear warning and explicit confirm/cancel buttons. Do not launch a modal overlay.

[ Delete Identity ]
     ↓ click
┌─────────────────────────────────────────┐
│ This permanently deletes your identity  │
│ and all contacts. This cannot be undone.│
│                                         │
│         [ Cancel ]  [ Delete Forever ]  │
└─────────────────────────────────────────┘

Why: Inline confirmation keeps context visible. Modal dialogs obscure what the user was looking at, forcing them to hold the context in memory.


UI-03: Inline Over Overlay

Editing, status messages, errors, and form validation appear inline — in the context where the action happened. Never launch a modal to show a single text field, a status message, or a validation error.

Use caseWrongRight
Edit contact nameModal with text inputName becomes editable in place
Validation errorAlert popupRed border + inline message
Success message"Saved!" modalBrief inline indicator or toast
Error messageError dialogInline error banner at the relevant location

Why: Overlays break spatial context. Users lose their place and must re-orient after dismissing the dialog.


UI-04: Progressive Disclosure

Show only what the user needs now. Advanced options, secondary actions, and details expand in-place on demand.

  • Default views show primary content only
  • "Show more", accordions, and expandable sections reveal detail
  • Settings pages use sections, not nested dialogs
  • Help text appears on hover/focus, not in separate windows

Why: Front-loading complexity overwhelms users and obscures the primary action. Let them drill in when they choose to.


UI-05: Follow Platform Conventions

Each platform adapts these rules using native idioms. Users expect their platform's patterns — don't invent new ones.

ConceptLinux GTK4Linux Qt (Widgets)Windows (WinUI3)macOS (SwiftUI)Android (Compose)iOS (SwiftUI)watchOS / Wear OSKaiOS (Web)
Toast/Undoadw::ToastCustom QWidget overlayInfoBarCustom overlaySnackbarHostCustom overlayHaptic + brief textSoft-key toast
Inline confirmIn-place gtk::BoxInline QHBoxLayoutInline StackPanelInline VStackInline RowSwipe + confirmCrown press-holdConfirm soft-key
Inline editgtk::Entry swapQLineEdit swapTextBox swapTextField swapTextField swapTextField swapVoice or companionD-pad select
Navigationadw::NavigationViewQStackedWidgetNavigationViewNavigationStackNavHost / M3NavigationStackVertical page listSoft-key tabs
Loadinggtk::SpinnerQProgressBarProgressRingProgressViewCircularProgressProgressViewDots animationInline text

When platform convention conflicts with these rules: Platform convention wins for interaction patterns (gestures, navigation, system dialogs). These rules win for information architecture (what triggers a dialog vs. inline action).


UI-06: One Primary Action per Screen

Each screen has one primary action, visually dominant. Secondary actions are visually subordinate (smaller, muted, or behind a menu).

  • Primary action: filled/accent button, prominent placement
  • Secondary actions: outlined or text buttons, grouped away from primary
  • Destructive actions: never the primary visual element — require deliberate reach (end of list, behind a menu, or expandable section)

Why: When everything is prominent, nothing is. Users hesitate when faced with multiple equally weighted choices.


UI-07: Immediate Feedback

Every user action gets visible feedback within 100ms.

  • Tap/click: visual state change (pressed state, color shift)
  • Submit: loading indicator or optimistic UI update
  • Error: inline message at the point of failure
  • Success: brief visual confirmation (checkmark, state change) — not a dialog

If an operation takes longer than 300ms, show a loading state. If longer than 2 seconds, show a progress indicator with context ("Syncing contacts...").

Why: Delayed feedback makes users tap again, double-submit, or assume the app is broken.


UI-08: Escape Hatches Are Visible

Every state must have a clear, visible way back.

  • Back buttons are always present in navigation stacks
  • Cancel is always available during multi-step flows
  • Undo appears immediately after destructive actions (see UI-01)
  • No state requires a gesture (swipe, long-press) as the only way out — always provide a visible alternative

Why: Hidden escape hatches create anxiety. Users avoid taking actions when they're unsure they can get back.


Applying the Rules

For New Screens

Before implementing a new screen, answer:

  1. What is the one primary action on this screen? (UI-06)
  2. Are any actions truly irrevocable? List them. Everything else gets undo. (UI-01, UI-02)
  3. Can all editing happen inline? (UI-03)
  4. What is the minimum the user needs to see on first load? (UI-04)

For Existing Screens

When modifying an existing screen, check whether it violates any rule. If it does, fix the violation in a separate commit — don't mix guideline fixes with feature work.

For Code Review

Reviewers should check:

  • No new modal dialogs for reversible actions
  • Confirmation only for irrevocable actions, done inline
  • Error and status messages appear inline
  • Primary action is visually clear
  • Every action has visible feedback
  • Undo is available for destructive but reversible actions

Decision Record

These guidelines were adopted on 2026-02-24 based on:

UX Interaction Guidelines

Cross-platform interaction rules for all vauchi clients — smartphones, dumb-phones, smartwatches, tablets, laptops, desktops (Windows, macOS, Linux, Android, iOS).

These rules enforce Principle 4: Simplicity serves the user and Principle 2: Trust is earned in person. For component-level behavior (toasts, inline editing, confirmations), see the sibling document GUI Design Guidelines.


Philosophy

Four commitments shape every interaction in vauchi:

  1. Don't make me think — Every screen is self-explanatory. If a user pauses to figure out what to do, the design has failed.
  2. Keep the user informed — Always show what's happening, what just happened, and what comes next.
  3. Success paths are straight lines — Primary flows have no forks, no optional detours, no "you can also..." on the main screen.
  4. Simple views, few transitions — Each view does one thing well. Don't bounce users between screens when content can update in place.

These are not aspirations — they are constraints. Designs that violate them must be reworked.


The Rules

UX-01: Physical Device First

Prefer real-world device interaction over typing, pasting, or link sharing. Vauchi's trust model is built on physical proximity — the UX must reflect that.

ScenarioWrongRight
Contact exchange (phone ↔ phone)Copy a code, paste in appHold phones together, scan QR
Contact exchange (phone ↔ laptop)Type a code on laptopPoint phone camera at laptop screen QR
NFC-capable devicesShare a linkTap devices together
BLE proximityManual pairing flowAutomatic discovery, confirm on both devices

On devices without cameras or NFC (some desktops, dumb-phones without camera, CLI): fall back to the simplest available method (display QR for the other device to scan, or paste a one-time code). The device with more hardware capabilities drives the interaction.

On smartwatches: the watch displays a QR code; the other person's phone scans it. Watches don't drive complex flows — they companion with the paired phone for setup and editing.

Why: Physical actions are faster, harder to phish, and align with Principle 2 (trust is earned in person). Copy-paste is error-prone and trains users to move secrets through clipboards.


UX-02: Zero-Instruction Screens

If a screen needs written instructions to be understood, redesign it. Labels, icons, layout, and context must be self-evident.

  • Button labels state the action: "Add Contact", not "Submit"
  • Icons have text labels on first encounter (icon-only after familiarity)
  • Empty states explain what goes here and how to start: "No contacts yet. Scan a QR code to add one."
  • Error states say what went wrong and what to do (see UX-07)

Exceptions: Security-critical screens (identity wipe, recovery) may include a short warning. This is a safety message, not an instruction.

Why: Users scan, they don't read. Instructions are ignored or cause hesitation. Self-evident design eliminates both problems.


UX-03: Show State, Not Chrome

Screen real estate belongs to the user's data and current status. Decorative elements, branding, and navigation chrome are subordinate.

  • Contact list shows contacts, not app branding
  • Exchange screen shows the camera viewfinder or QR code — not a toolbar and a sidebar
  • Status indicators (syncing, connected, offline) are compact and contextual
  • On small screens (phones, watches), chrome compresses or hides entirely during focused tasks

Why: Users open vauchi to do something with their contacts, not to admire the app. Every pixel of chrome competes with the content they came for.


UX-04: One Happy Path

Primary flows (onboarding, exchange, editing a contact) have exactly one path forward. Alternatives, advanced options, and edge cases are hidden until needed.

  • Onboarding: one screen at a time, one action per screen, linear progression
  • Exchange: scan → confirm → done. No "choose your method" screen unless the device supports multiple methods
  • Settings: grouped by topic, expanded on demand (progressive disclosure per UI-04)

When multiple hardware methods exist (QR + NFC + BLE on a phone): auto-select the best available method. Show alternatives only on failure or explicit user request — not as a decision tree at the start.

Why: Every fork in the path is a decision point. Decisions cost attention. One clear path is faster and less stressful than three options with no clear winner.


UX-05: Progress Is Always Visible

Multi-step flows show where the user is, where they've been, and how many steps remain.

  • Step indicators: "Step 2 of 4" or a progress bar — not just the current screen
  • Completed steps show a checkmark or similar confirmation
  • The final step clearly signals completion ("Your identity is ready", not just returning to a blank home screen)
  • Long operations (sync, key generation) show a progress indicator with context: "Generating keys..." not just a spinner

On desktop and laptop: larger screens can show a step sidebar or breadcrumb trail. On phones, a compact progress bar or step counter at the top suffices.

Why: Users abandon flows when they can't tell how much is left. Visible progress reduces anxiety and drop-off.


UX-06: Transitions Are Earned

Don't navigate to a new screen when content can update in place. Screen changes are only for genuinely new contexts.

SituationWrongRight
Edit a contact fieldNavigate to edit screenField becomes editable in place (UI-03)
Show exchange resultNew "success" screenContact appears in list, toast confirms (UI-01)
Toggle a settingNavigate to sub-pageToggle updates in place
View contact detailsNew screenExpand contact card in list (phone), or side panel (desktop/laptop)

When a transition is appropriate: navigating to a genuinely different context (contact list → exchange camera), entering a multi-step flow (settings → identity wipe confirmation), or switching between top-level sections.

Desktop/laptop consideration: larger screens should use split views, side panels, and in-place expansion more aggressively. A phone might navigate to a contact detail screen; a laptop should show it alongside the list.

Why: Every screen transition resets the user's spatial context. Frequent transitions create disorientation and make the app feel heavier than it is.


UX-07: Errors Name the Fix

Every error message tells the user what happened and what they can do about it. Generic errors are forbidden.

WrongRight
"Something went wrong""Camera permission denied. Open Settings → Privacy → Camera to allow vauchi."
"Network error""Can't reach the relay. Your changes are saved and will sync when you're back online."
"Invalid QR""This QR code isn't a vauchi contact card. Ask the other person to open vauchi and show their code."
"Exchange failed""Couldn't complete the exchange. Move closer and try again."

Structure: [What happened]. [What to do about it]. — two sentences, no jargon.

Offline context: When offline, never show errors for things that will work later. Instead, show status: "Saved locally. Will sync when connected."

Why: An error without a fix is a dead end. Users can't solve "something went wrong" — they can solve "move closer and try again."


UX-08: Offline and In-Person First

QR exchange, contact viewing, and card editing work without network connectivity. The app never blocks on a network call for local operations.

  • Exchange: QR generation and scanning are fully local. BLE/NFC are local. No server round-trip needed.
  • Contact list and card details: always available from local storage
  • Editing own card: immediate, local. Sync happens when connectivity returns.
  • Sync status: shown as a subtle indicator, never as a blocking state

What requires connectivity: relay sync (pushing updates to contacts), recovery voucher upload, relay registration. These are background operations — never in the critical path of a user action.

On laptops and desktops: the same rules apply. A desktop user editing their card on a train without WiFi should have the same experience as someone on a connected phone.

Why: Vauchi's trust model is in-person. Two people standing next to each other should never see "Connecting..." when exchanging contacts. Local-first is both a UX and a security principle.


UX-09: Hardware Guides the Flow

When the device's hardware is active (camera, NFC, BLE), the hardware's output IS the primary UI. Don't cover it with chrome.

  • Camera (QR scan): viewfinder fills the available space. A subtle frame or overlay guides alignment — nothing more.
  • NFC: the system's NFC animation (iOS tap indicator, Android NFC dialog) is the feedback. The app shows a brief "Hold near the other device" prompt, then gets out of the way.
  • BLE discovery: show a compact list of nearby devices as they appear. No full-screen takeover.

On devices without hardware (desktop without camera, CLI): clearly communicate the fallback. "Display this QR code on your screen — the other person scans it with their phone."

Desktop with camera: the same camera-fills-the-space rule applies. A laptop scanning a phone's QR should show the webcam feed prominently, not buried in a small widget.

Why: Hardware feedback is immediate and real. Overlaying it with app UI creates competition for attention. Let the camera be the camera.


UX-10: Reachability Drives Layout

Primary actions sit where the user can reach them without effort. On touch devices, this is the thumb zone. On desktop and laptop, this is the main content area and keyboard shortcuts.

Smartphones and tablets:

  • Primary actions: bottom of screen, center or dominant-hand side
  • Navigation: bottom tab bar or swipe gestures
  • Destructive/rare actions: top of screen or behind a menu — require deliberate reach
  • Minimum tap targets: 44×44pt (iOS) / 48×48dp (Android)

Smartwatches:

  • One primary action per screen — crown or single tap to confirm
  • Scrollable vertical list for navigation — no side menus or tabs
  • Minimal text — icons and short labels only
  • Destructive actions require crown press-and-hold or companion app

Dumb-phones (KaiOS):

  • D-pad navigation — primary action on center/select key
  • Soft keys at bottom for contextual actions (left = back, right = options)
  • No gestures — every action reachable via key presses

Desktop and laptop (Windows, macOS, Linux):

  • Primary actions: prominent buttons in the main content area, keyboard shortcuts for frequent actions
  • Navigation: sidebar or top bar — persistent, not hidden behind a hamburger menu
  • Destructive/rare actions: behind a menu or at the end of a settings list
  • Keyboard accessibility: every action reachable without a mouse

Why: Frequent actions that are hard to reach feel heavy. Destructive actions that are easy to reach cause accidents. Layout should match action frequency and risk.


Applying the Rules

For New Flows

Before designing a new user flow, answer:

  1. Can this be done with a physical device action instead of typing? (UX-01)
  2. Does every screen explain itself without instructions? (UX-02)
  3. Is there one clear path forward? (UX-04)
  4. Does the user always know where they are in the flow? (UX-05)
  5. Can any screen transition be replaced with an in-place update? (UX-06)
  6. Does it work offline? If not, why not? (UX-08)

For Existing Flows

When modifying an existing flow, check whether it violates any rule. Fix violations in a separate commit — don't mix UX fixes with feature work (same as GUI guidelines).

For Code Review

Reviewers should check:

  • Physical interaction preferred over manual input where hardware allows
  • No screens require reading instructions to understand
  • Primary flow has one path, no unnecessary forks
  • Multi-step flows show progress
  • Screen transitions only for genuinely new contexts
  • Errors include what happened and what to do
  • Core operations work offline
  • Hardware UI (camera, NFC) not obscured by app chrome
  • Primary actions in easy-reach zones per platform

Platform Adaptation

These rules apply to all platforms, but implementation adapts:

ConceptSmartphoneDumb-phone (KaiOS)SmartwatchTabletLaptop/DesktopCLI/TUI
QR exchangeCamera viewfinderCamera viewfinderDisplay QR (no camera)Camera viewfinderWebcam or display QR for phone to scanDisplay QR in terminal (ASCII/sixel)
NFC/BLENative hardwareNFC if availableNFC tapNative hardwareUSB NFC reader (if available)Not applicable — QR fallback
ProgressTop progress barStep counterMinimal step dotsTop progress barStep sidebar or breadcrumbStep counter: [2/4]
ReachabilityThumb zone (bottom)D-pad center/selectCrown/single buttonThumb zone (bottom)Main content area + shortcutsCommand-line arguments
Inline editingTap to editSelect to editNot applicable — voice or companion appTap to editClick to edit, Enter to saveNot applicable — command-based
Split viewsFull-screen navigationFull-screen navigationSingle view onlySide panel + listSide panel + listNot applicable

Relationship to Other Documents

  • GUI Design Guidelines: Component-level behavior — how toasts, inline confirmations, and inline editing work. UX guidelines say when to use them; GUI guidelines say how they behave.
  • Principles: Philosophical foundation. UX guidelines are the practical application of Principles 2 (trust in person) and 4 (simplicity serves the user).
  • ADR-022 (Core-driven UI): UX guidelines inform what WorkflowEngine implementations should produce; ADR-022 defines the mechanism. See the internal Architecture Decision Records for details.

Decision Record

These guidelines were adopted on 2026-03-10 based on:

Diagrams

Sequence diagrams for core Vauchi flows.


Available Diagrams

DiagramDescription
Contact ExchangeIn-person QR code exchange
Device LinkingMulti-device setup
Sync UpdatesHow card updates propagate
Contact RecoverySocial recovery flow
Message DeliveryEnd-to-end message delivery flow
Crypto HierarchyKey derivation and storage hierarchy

Reading These Diagrams

All diagrams use Mermaid sequence diagram notation:

  • Solid arrows (->>) = Synchronous request
  • Dashed arrows (-->>) = Asynchronous/response
  • Notes = Context or explanation
  • Participants = Entities involved in the flow

Interaction Types

Each diagram indicates the interaction type:

IconTypeMeaning
🤝IN-PERSONPhysical proximity required
☁️REMOTEVia relay server
🔒ENCRYPTEDEnd-to-end encrypted

Contact Exchange Sequence

Interaction Type: 🤝 IN-PERSON (Proximity Required)

Two users exchange contact cards by scanning QR codes while physically present together. Proximity is verified via ultrasonic audio handshake to prevent remote/screenshot attacks.

Participants

  • Alice - User initiating exchange (displays QR)
  • Alice's Device - Mobile/Desktop running Vauchi
  • Bob - User completing exchange (scans QR)
  • Bob's Device - Mobile/Desktop running Vauchi
  • Relay - HTTP v2 relay server (fallback only)

Sequence Diagram

 ┌───────┐           ┌────────────────┐                                ┌──────────────┐               ┌─────┐   ┌───────┐     
 │ Alice │           │ Alice's Device │                                │ Bob's Device │               │ Bob │   │ Relay │     
 └───┬───┘           └────────┬───────┘                                └───────┬──────┘               └──┬──┘   └───┬───┘     
     │                        │                                                │                         │          │         
     │  Tap "Share Contact"   │                                                │                         │          │         
     │────────────────────────▶                                                │                         │          │         
     │                        │                                                │                         │          │         
     │                        ├───┐                                            │                         │          │         
     │                        │   │ Generate ephemeral X25519 keypair          │                         │          │         
     │                        ◀───┘                                            │                         │          │         
     │                        │                                                │                         │          │         
     │                        ├───┐                                            │                         │          │         
     │                        │   │ Create exchange token (expires 5 min)      │                         │          │         
     │                        ◀───┘                                            │                         │          │         
     │                        │                                                │                         │          │         
     │                        ├───┐                                            │                         │          │         
     │                        │   │ Generate audio challenge seed              │                         │          │         
     │                        ◀───┘                                            │                         │          │         
     │                        │                                                │                         │          │         
     │                        ├───┐                                            │                         │          │         
     │                        │   │ Encode QR: [public_key, token, audio_challenge]                      │          │         
     │                        ◀───┘                                            │                         │          │         
     │                        │                                                │                         │          │         
     │    Display QR code     │                                                │                         │          │         
     ◀────────────────────────│                                                │                         │          │         
     │                        │                                                │                         │          │         
     │                        │                                                │  Open camera, scan QR   │          │         
     │                        │                                                ◀─────────────────────────│          │         
     │                        │                                                │                         │          │         
     │                        │                                                ├───┐                     │          │         
     │                        │                                                │   │ Decode QR data      │          │         
     │                        │                                                ◀───┘                     │          │         
     │                        │                                                │                         │          │         
     │                        │                                                ├───┐                     │          │         
     │                        │                                                │   │ Validate token not expired     │         
     │                        │                                                ◀───┘                     │          │         
     │                        │                                                │                         │          │         
     │                        │                                                ├───┐                     │          │         
     │                        │                                                │   │ Extract Alice's public key     │         
     │                        │                                                ◀───┘                     │          │         
     │                        │                                                │                         │          │         
     │                        │ ┌───────────────────────────────────────────┐  │                         │          │         
     │                        │ │ PROXIMITY VERIFICATION (Ultrasonic Audio) │  │                         │          │         
     │                        │ └───────────────────────────────────────────┘  │                         │          │         
     │                        │                                                │                         │          │         
     │                        ├───┐                                            │                         │          │         
     │                        │   │ Emit ultrasonic challenge (18-20 kHz)      │                         │          │         
     │                        ◀───┘                                            │                         │          │         
     │                        │                                                │                         │          │         
     │                        │                                                ├───┐                     │          │         
     │                        │                                                │   │ Detect ultrasonic challenge    │         
     │                        │                                                ◀───┘                     │          │         
     │                        │                                                │                         │          │         
     │                        │                                                ├───┐                     │          │         
     │                        │                                                │   │ Sign challenge with Bob's key  │         
     │                        │                                                ◀───┘                     │          │         
     │                        │                                                │                         │          │         
     │                        │                                                ├───┐                     │          │         
     │                        │                                                │   │ Emit ultrasonic response       │         
     │                        │                                                ◀───┘                     │          │         
     │                        │                                                │                         │          │         
     │                        ├───┐                                            │                         │          │         
     │                        │   │ Detect and verify response                 │                         │          │         
     │                        ◀───┘                                            │                         │          │         
     │                        │                                                │                         │          │         
     │                        ├───┐                                            │                         │          │         
     │                        │   │ Confirm proximity                          │                         │          │         
     │                        ◀───┘                                            │                         │          │         
     │                        │                                                │                         │          │         
     │                        │                                                ├───┐                     │          │         
     │                        │                                                │   │ Confirm proximity   │          │         
     │                        │                                                ◀───┘                     │          │         
     │                        │                                                │                         │          │         
     │                        │            ┌────────────────────┐              │                         │          │         
     │                        │            │ X3DH KEY AGREEMENT │              │                         │          │         
     │                        │            └────────────────────┘              │                         │          │         
     │                        │                                                │                         │          │         
     │                        │                                                ├───┐                     │          │         
     │                        │                                                │   │ Generate ephemeral X25519 keypair        
     │                        │                                                ◀───┘                     │          │         
     │                        │                                                │                         │          │         
     │                        │   Send: [Bob's identity key, ephemeral key]    │                         │          │         
     │                        ◀────────────────────────────────────────────────│                         │          │         
     │                        │                                                │                         │          │         
     │                        ├───┐                                            │                         │          │         
     │                        │   │ X3DH: Derive shared secret                 │                         │          │         
     │                        ◀───┘                                            │                         │          │         
     │                        │                                                │                         │          │         
     │                        │  Send: [Alice's identity key, ephemeral key]   │                         │          │         
     │                        │────────────────────────────────────────────────▶                         │          │         
     │                        │                                                │                         │          │         
     │                        │                                                ├───┐                     │          │         
     │                        │                                                │   │ X3DH: Derive shared secret     │         
     │                        │                                                ◀───┘                     │          │         
     │                        │                                                │                         │          │         
     │                        │     ┌───────────────────────────────────┐      │                         │          │         
     │                        │     │ Both have identical shared secret │      │                         │          │         
     │                        │     └───────────────────────────────────┘      │                         │          │         
     │                        │                                                │                         │          │         
     │                        │           ┌───────────────────────┐            │                         │          │         
     │                        │           │ CONTACT CARD EXCHANGE │            │                         │          │         
     │                        │           └───────────────────────┘            │                         │          │         
     │                        │                                                │                         │          │         
     │                        ├───┐                                            │                         │          │         
     │                        │   │ Encrypt Alice's card with shared secret    │                         │          │         
     │                        ◀───┘                                            │                         │          │         
     │                        │                                                │                         │          │         
     │                        │          Send encrypted contact card           │                         │          │         
     │                        │────────────────────────────────────────────────▶                         │          │         
     │                        │                                                │                         │          │         
     │                        │                                                ├───┐                     │          │         
     │                        │                                                │   │ Decrypt Alice's card│          │         
     │                        │                                                ◀───┘                     │          │         
     │                        │                                                │                         │          │         
     │                        │                                                ├───┐                     │          │         
     │                        │                                                │   │ Store Alice as contact         │         
     │                        │                                                ◀───┘                     │          │         
     │                        │                                                │                         │          │         
     │                        │                                                ├───┐                     │          │         
     │                        │                                                │   │ Encrypt Bob's card with shared secret    
     │                        │                                                ◀───┘                     │          │         
     │                        │                                                │                         │          │         
     │                        │          Send encrypted contact card           │                         │          │         
     │                        ◀────────────────────────────────────────────────│                         │          │         
     │                        │                                                │                         │          │         
     │                        ├───┐                                            │                         │          │         
     │                        │   │ Decrypt Bob's card                         │                         │          │         
     │                        ◀───┘                                            │                         │          │         
     │                        │                                                │                         │          │         
     │                        ├───┐                                            │                         │          │         
     │                        │   │ Store Bob as contact                       │                         │          │         
     │                        ◀───┘                                            │                         │          │         
     │                        │                                                │                         │          │         
     │  Exchange Successful   │                                                │                         │          │         
     ◀────────────────────────│                                                │                         │          │         
     │                        │                                                │                         │          │         
     │                        │                                                │   Exchange Successful   │          │         
     │                        │                                                │─────────────────────────▶          │         
     │                        │                                                │                         │          │         
     │                       ┌───────────────────────────────────────────────────┐                       │          │         
     │                       ││Alice and Bob now have each other's contact cards │                       │          │         
     │                       └───────────────────────────────────────────────────┘                       │          │         
     │                        │                                                │                         │          │         
 ┌───┴───┐           ┌────────┴───────┐                                ┌───────┴──────┐               ┌──┴──┐   ┌───┴───┐     
 │ Alice │           │ Alice's Device │                                │ Bob's Device │               │ Bob │   │ Relay │     
 └───────┘           └────────────────┘                                └──────────────┘               └─────┘   └───────┘     

Data Exchanged

QR Code Contents

Binary format (v3) with WBEX magic bytes:

WBEX (4 bytes magic)
version (1 byte) = 0x03
Ed25519 public key (32 bytes)
X25519 exchange key (32 bytes)
exchange token (32 bytes)
audio_challenge seed (16 bytes)
creation timestamp (8 bytes)
name_len (2 bytes, big-endian)
name (N bytes, UTF-8)
flags (1 byte, bitfield)
  [if bit 0] relay_url_len (2 bytes) + relay_url (M bytes)
  [bits 1-7 reserved]
signature (64 bytes, Ed25519 over all preceding fields)

Minimum size (empty name, no relay fields): 192 bytes.

Contact Card (Encrypted)

{
  "display_name": "Alice Smith",
  "fields": [
    {"type": "phone", "label": "Mobile", "value": "+1-555-1234"},
    {"type": "email", "label": "Personal", "value": "alice@example.com"}
  ],
  "signature": "Ed25519 signature of card"
}

Security Properties

PropertyMechanism
ProximityUltrasonic audio (18-20 kHz)
No MITMX3DH with identity keys
Forward SecrecyEphemeral keys discarded
Replay PreventionOne-time token, 5-min expiry
Card AuthenticityEd25519 signature

Failure Scenarios

Proximity Verification Fails

┌────────────────┐  ┌──────────────┐                                  
│ Alice's Device │  │ Bob's Device │                                  
└────────┬───────┘  └───────┬──────┘                                  
         │                  │                                         
         ├───┐              │                                         
         │   │ Emit ultrasonic challenge                              
         ◀───┘              │                                         
         │                  │                                         
         │                  ├╌╌╌┐                                     
         │                  │   │ No ultrasonic detected (too far)    
         │                  ◀╌╌╌┘                                     
         │                  │                                         
         │                  ├───┐                                     
         │                  │   │ Proximity verification FAILED       
         │                  ◀───┘                                     
         │                  │                                         
         │                  ├───┐                                     
         │                  │   │ Proximity verification failed       
         │                  ◀───┘                                     
         │                  │                                         
┌───────────────────────────────────────┐                             
│ Exchange blocked - no cards exchanged │                             
└───────────────────────────────────────┘                             
         │                  │                                         
┌────────┴───────┐  ┌───────┴──────┐                                  
│ Alice's Device │  │ Bob's Device │                                  
└────────────────┘  └──────────────┘                                  

QR Code Expired

┌────────────────┐  ┌──────────────┐                         
│ Alice's Device │  │ Bob's Device │                         
└────────┬───────┘  └───────┬──────┘                         
         │                  │                                
         │                  ├───┐                            
         │                  │   │ Decode QR, check expiry    
         │                  ◀───┘                            
         │                  │                                
         │                  ├───┐                            
         │                  │   │ Token expired              
         │                  ◀───┘                            
         │                  │                                
         │                  ├───┐                            
         │                  │   │ QR code expired            
         │                  ◀───┘                            
         │                  │                                
   ┌────────────────────────────┐                            
   │ Alice must generate new QR │                            
   └────────────────────────────┘                            
         │                  │                                
┌────────┴───────┐  ┌───────┴──────┐                         
│ Alice's Device │  │ Bob's Device │                         
└────────────────┘  └──────────────┘                         

Platform Variations

PlatformProximity MethodFallback
iOS ↔ iOSUltrasonicManual confirm
Android ↔ AndroidUltrasonicManual confirm
iOS ↔ AndroidUltrasonicManual confirm
Desktop ↔ MobileN/A (no mic)Manual confirm
Desktop ↔ DesktopN/AManual confirm

Sync Updates Sequence

Interaction Type: ☁️ REMOTE (Via Relay)

Contact card changes propagate automatically to contacts via the relay network. All data is end-to-end encrypted - relays only see encrypted blobs.

Participants

  • Alice - User updating their contact card
  • Alice's Device - Device where change is made
  • Alice's Other Device - Another linked device
  • Relay - HTTP v2 relay server
  • Bob's Device - Contact receiving the update
  • Bob - Contact who will see the update

Sequence Diagram

 ┌───────┐                             ┌────────────────┐  ┌────────────────┐                 ┌───────┐                ┌──────────────┐                                     ┌─────┐   
 │ Alice │                             │ Alice Device 1 │  │ Alice Device 2 │                 │ Relay │                │ Bob's Device │                                     │ Bob │   
 └───┬───┘                             └────────┬───────┘  └────────┬───────┘                 └───┬───┘                └───────┬──────┘                                     └──┬──┘   
     │                                          │                   │                             │                            │                                               │      
     │  Update phone: "555-1111" → "555-2222"   │                   │                             │                            │                                               │      
     │──────────────────────────────────────────▶                   │                             │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          ├───┐               │                             │                            │                                               │      
     │                                          │   │ Update local contact card                   │                            │                                               │      
     │                                          ◀───┘               │                             │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          ├───┐               │                             │                            │                                               │      
     │                                          │   │ Increment card version                      │                            │                                               │      
     │                                          ◀───┘               │                             │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          ├───┐               │                             │                            │                                               │      
     │                                          │   │ Sign card with identity key                 │                            │                                               │      
     │                                          ◀───┘               │                             │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          │             ┌─────────────────────┐             │                            │                                               │      
     │                                          │             │ PUSH TO OWN DEVICES │             │                            │                                               │      
     │                                          │             └─────────────────────┘             │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          ├───┐               │                             │                            │                                               │      
     │                                          │   │ Encrypt update for Device 2                 │                            │                                               │      
     │                                          ◀───┘               │                             │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          │         Push encrypted update (for AD2)         │                            │                                               │      
     │                                          │─────────────────────────────────────────────────▶                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          │                   │  Forward encrypted update   │                            │                                               │      
     │                                          │                   ◀╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌│                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          │                   ├───┐                         │                            │                                               │      
     │                                          │                   │   │ Decrypt update          │                            │                                               │      
     │                                          │                   ◀───┘                         │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          │                   ├───┐                         │                            │                                               │      
     │                                          │                   │   │ Apply change locally    │                            │                                               │      
     │                                          │                   ◀───┘                         │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          │                   ├───┐                         │                            │                                               │      
     │                                          │                   │   │ Verify signature        │                            │                                               │      
     │                                          │                   ◀───┘                         │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          │              ┌──────────────────┐               │                            │                                               │      
     │                                          │              │ PUSH TO CONTACTS │               │                            │                                               │      
     │                                          │              └──────────────────┘               │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          ├───┐               │                             │                            │                                               │      
     │                                          │   │ Check visibility rules                      │                            │                                               │      
     │                                          ◀───┘               │                             │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          │ ┌─────────────────────┐                         │                            │                                               │      
     │                                          │ │ Phone visible to:   │                         │                            │                                               │      
     │                                          │ │ - Bob           │   │                         │                            │                                               │      
     │                                          │ │ - Carol         │   │                         │                            │                                               │      
     │                                          │ │ - Dave (restricted) │                         │                            │                                               │      
     │                                          │ └─────────────────────┘                         │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          ├───┐               │                             │                            │                                               │      
     │                                          │   │ Encrypt delta for Bob (shared key)          │                            │                                               │      
     │                                          ◀───┘               │                             │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          │ ┌────────────────────────────────────────────┐  │                            │                                               │      
     │                                          │ │ Delta: {field: "phone", value: "555-2222"} │  │                            │                                               │      
     │                                          │ └────────────────────────────────────────────┘  │                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          │         Push encrypted delta (for Bob)          │                            │                                               │      
     │                                          │─────────────────────────────────────────────────▶                            │                                               │      
     │                                          │                   │                             │                            │                                               │      
     │                                          │                   │                         ┌alt [Bob is Online]─────────────────────────────────────────────────────────────────┐  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         │   │  Forward encrypted delta   │                                               │   │  
     │                                          │                   │                         │   │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                                               │   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         │   │                            ├───┐                                           │   │  
     │                                          │                   │                         │   │                            │   │ Decrypt with Alice-Bob shared key         │   │  
     │                                          │                   │                         │   │                            ◀───┘                                           │   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         │   │                            ├───┐                                           │   │  
     │                                          │                   │                         │   │                            │   │ Verify signature                          │   │  
     │                                          │                   │                         │   │                            ◀───┘                                           │   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         │   │                            ├───┐                                           │   │  
     │                                          │                   │                         │   │                            │   │ Update Alice's contact card locally       │   │  
     │                                          │                   │                         │   │                            ◀───┘                                           │   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         │   │                            │  Notification: "Alice updated contact info"   │   │  
     │                                          │                   │                         │   │                            │───────────────────────────────────────────────▶   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         ├[Bob is Offline]╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         │   ├───┐                        │                                               │   │  
     │                                          │                   │                         │   │   │ Queue message for Bob  │                                               │   │  
     │                                          │                   │                         │   ◀───┘                        │                                               │   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         │   │ ┌───────────────────────────┐                                              │   │  
     │                                          │                   │                         │   │ │ Queued until Bob connects││                                              │   │  
     │                                          │                   │                         │   │ └───────────────────────────┘                                              │   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         └────────────────────────────────────────────────────────────────────────────────────┘  
     │                                          │                   │                             │                            │                                               │      
     │                                          │                   │                         ┌opt [Bob was Offline]───────────────────────────────────────────────────────────────┐  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         │   │     Connect to relay       │                                               │   │  
     │                                          │                   │                         │   ◀────────────────────────────│                                               │   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         │   │  Deliver queued messages   │                                               │   │  
     │                                          │                   │                         │   │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                                               │   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         │   │                            ├───┐                                           │   │  
     │                                          │                   │                         │   │                            │   │ Process Alice's update                    │   │  
     │                                          │                   │                         │   │                            ◀───┘                                           │   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         │   │                            ├───┐                                           │   │  
     │                                          │                   │                         │   │                            │   │ Update local contact card                 │   │  
     │                                          │                   │                         │   │                            ◀───┘                                           │   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         │   │                            │          Alice updated contact info           │   │  
     │                                          │                   │                         │   │                            │───────────────────────────────────────────────▶   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │ ┌───────────────────────────────────────┐                │                                               │   │  
     │                                          │                   │ │ Bob now sees Alice's new phone number │                │                                               │   │  
     │                                          │                   │ └───────────────────────────────────────┘                │                                               │   │  
     │                                          │                   │                         │   │                            │                                               │   │  
     │                                          │                   │                         └────────────────────────────────────────────────────────────────────────────────────┘  
     │                                          │                   │                             │                            │                                               │      
 ┌───┴───┐                             ┌────────┴───────┐  ┌────────┴───────┐                 ┌───┴───┐                ┌───────┴──────┐                                     ┌──┴──┐   
 │ Alice │                             │ Alice Device 1 │  │ Alice Device 2 │                 │ Relay │                │ Bob's Device │                                     │ Bob │   
 └───────┘                             └────────────────┘  └────────────────┘                 └───────┘                └──────────────┘                                     └─────┘   

Visibility-Aware Sync

┌────────────────┐                    ┌───────┐                    ┌──────────────┐  ┌────────────────┐   ┌───────────────┐   
│ Alice's Device │                    │ Relay │                    │ Bob's Device │  │ Carol's Device │   │ Dave's Device │   
└────────┬───────┘                    └───┬───┘                    └───────┬──────┘  └────────┬───────┘   └───────┬───────┘   
         │                                │                                │                  │                   │           
         ├───┐                            │                                │                  │                   │           
         │   │ Check visibility rules     │                                │                  │                   │           
         ◀───┘                            │                                │                  │                   │           
         │                                │                                │                  │                   │           
         │ ┌───────────────────────────┐  │                                │                  │                   │           
         │ │ Visibility:               │  │                                │                  │                   │           
         │ │ Bob: [name, phone, email] │  │                                │                  │                   │           
         │ │ Carol: [name, email]      │  │                                │                  │                   │           
         │ │ Dave: [name only]         │  │                                │                  │                   │           
         │ └───────────────────────────┘  │                                │                  │                   │           
         │                                │                                │                  │                   │           
     ┌par [Send to contacts based on visibility]──────────────────────────────────────────────────────────────────────┐       
     │   │                                │                                │                  │                   │   │       
     │   │  To Bob: {phone: "555-2222"}   │                                │                  │                   │   │       
     │   │────────────────────────────────▶                                │                  │                   │   │       
     │   │                                │                                │                  │                   │   │       
     │   │                                │  Forward (Bob can see phone)   │                  │                   │   │       
     │   │                                │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                  │                   │   │       
     │   │                                │                                │                  │                   │   │       
     │   │                         ┌──────────────────────────────┐        │                  │                   │   │       
     │   │                         │ Carol cannot see phone field │        │                  │                   │   │       
     │   │                         └──────────────────────────────┘        │                  │                   │   │       
     │   │                                │                                │                  │                   │   │       
     ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤       
     │   │                                │                                │                  │                   │   │       
     │   │                        No update sent (field not visible)       │                  │                   │   │       
     │   │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                   │   │       
     │   │                                │                                │                  │                   │   │       
     │   │                                │   ┌─────────────────────────────┐                 │                   │   │       
     │   │                                │   │ Dave cannot see phone field││                 │                   │   │       
     │   │                                │   └─────────────────────────────┘                 │                   │   │       
     │   │                                │                                │                  │                   │   │       
     ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤       
     │   │                                │                                │                  │                   │   │       
     │   │                                │ No update sent (field not visible)                │                   │   │       
     │   │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶   │       
     │   │                                │                                │                  │                   │   │       
     └────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘       
         │                                │                                │                  │                   │           
┌────────┴───────┐                    ┌───┴───┐                    ┌───────┴──────┐  ┌────────┴───────┐   ┌───────┴───────┐   
│ Alice's Device │                    │ Relay │                    │ Bob's Device │  │ Carol's Device │   │ Dave's Device │   
└────────────────┘                    └───────┘                    └──────────────┘  └────────────────┘   └───────────────┘   

Offline Queue Handling

┌────────────────┐      ┌───────┐              ┌──────────────┐                            
│ Alice's Device │      │ Relay │              │ Bob's Device │                            
└────────┬───────┘      └───┬───┘              └───────┬──────┘                            
         │                  │                          │                                   
         ├───┐              │                          │                                   
         │   │ Update 1: Change phone                  │                                   
         ◀───┘              │                          │                                   
         │                  │                          │                                   
         │  Queue for Bob   │                          │                                   
         │──────────────────▶                          │                                   
         │                  │                          │                                   
         │                  ├───┐                      │                                   
         │                  │   │ Store encrypted message                                  
         │                  ◀───┘                      │                                   
         │                  │                          │                                   
         ├───┐              │                          │                                   
         │   │ Update 2: Change email                  │                                   
         ◀───┘              │                          │                                   
         │                  │                          │                                   
         │  Queue for Bob   │                          │                                   
         │──────────────────▶                          │                                   
         │                  │                          │                                   
         │                  ├───┐                      │                                   
         │                  │   │ Store encrypted message                                  
         │                  ◀───┘                      │                                   
         │                  │                          │                                   
         ├───┐              │                          │                                   
         │   │ Update 3: Change phone again            │                                   
         ◀───┘              │                          │                                   
         │                  │                          │                                   
         │  Queue for Bob   │                          │                                   
         │──────────────────▶                          │                                   
         │                  │                          │                                   
         │                  ├───┐                      │                                   
         │                  │   │ Store encrypted message                                  
         │                  ◀───┘                      │                                   
         │                  │                          │                                   
         │                  │  Connect (back online)   │                                   
         │                  ◀──────────────────────────│                                   
         │                  │                          │                                   
         │                  │    Deliver update 1      │                                   
         │                  │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                                   
         │                  │                          │                                   
         │                  │    Deliver update 2      │                                   
         │                  │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                                   
         │                  │                          │                                   
         │                  │    Deliver update 3      │                                   
         │                  │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                                   
         │                  │                          │                                   
         │                  │                          ├───┐                               
         │                  │                          │   │ Apply all updates in order    
         │                  │                          ◀───┘                               
         │                  │                          │                                   
         │                  │┌───────────────────────────────────────────────────┐         
         │                  ││ Bob sees latest values after applying all updates │         
         │                  │└───────────────────────────────────────────────────┘         
         │                  │                          │                                   
┌────────┴───────┐      ┌───┴───┐              ┌───────┴──────┐                            
│ Alice's Device │      │ Relay │              │ Bob's Device │                            
└────────────────┘      └───────┘              └──────────────┘                            

Data Exchanged

Update Delta (Encrypted)

{
  "type": "card_update",
  "from": "Alice's public key",
  "version": 6,
  "timestamp": "2026-01-21T12:00:00Z",
  "changes": [
    {
      "op": "update",
      "path": "/fields/phone",
      "value": "555-2222"
    }
  ],
  "signature": "Ed25519 signature"
}

Security Properties

PropertyMechanism
End-to-End EncryptionUpdates encrypted with per-contact shared keys
Relay BlindnessRelay sees only encrypted blobs; sees routing tokens and timing but not content or sender identity
Update AuthenticityEd25519 signature on all updates
Replay PreventionMonotonic version numbers + timestamps
Visibility EnforcementOnly visible fields sent to each contact

Message Delivery Flow

Interaction Type: 🌐 REMOTE (Via Relay)

End-to-end message delivery from card update to acknowledgment.

Participants

  • Alice - User sending card update
  • Alice's Device - Source device
  • Relay - HTTP v2 relay server
  • Bob's Device - Recipient device
  • Bob - Contact receiving update

Message Sizes & Frequency

Message TypePayloadPadded SizeFrequency
Card delta50-200 B256 B1-5/month
Full card500 B-2 KB1-4 KBInitial only
Ack32-64 B256 BPer message
Device sync100-500 B256 B-1 KBReal-time

Complete Delivery Flow

 ┌───────┐                          ┌───────────────────┐                          ┌───────────┐                                ┌─────────────────┐                   ┌─────┐       
 │ Alice │                          │ Alice's Device 📱 │                          │ Relay 🖥️ │                                │ Bob's Device 📱 │                   │ Bob │       
 └───┬───┘                          └─────────┬─────────┘                          └─────┬─────┘                                └────────┬────────┘                   └──┬──┘       
     │                                        │                                          │                                               │                               │          
     │  Edit phone: "555-1111" → "555-2222"   │                                          │                                               │                               │          
     │────────────────────────────────────────▶                                          │                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        ├───┐                                      │                                               │                               │          
     │                                        │   │ Update local card                    │                                               │                               │          
     │                                        ◀───┘                                      │                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        ├───┐                                      │                                               │                               │          
     │                                        │   │ Create CardDelta                     │                                               │                               │          
     │                                        ◀───┘                                      │                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        │ ┌──────────────────────────────────────┐ │                                               │                               │          
     │                                        │ │ Delta: ~100 bytes                    │ │                                               │                               │          
     │                                        │ │ {"field":"phone","value":"555-2222"} │ │                                               │                               │          
     │                                        │ └──────────────────────────────────────┘ │                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        ├───┐                                      │                                               │                               │          
     │                                        │   │ Check visibility: Bob can see phone? ✓                                               │                               │          
     │                                        ◀───┘                                      │                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        ├───┐                                      │                                               │                               │          
     │                                        │   │ Ratchet send chain forward           │                                               │                               │          
     │                                        ◀───┘                                      │                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        │ ┌─────────────────────┐                  │                                               │                               │          
     │                                        │ │ Chain gen 42 → 43   │                  │                                               │                               │          
     │                                        │ │ Message key derived │                  │                                               │                               │          
     │                                        │ └─────────────────────┘                  │                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        ├───┐                                      │                                               │                               │          
     │                                        │   │ Encrypt delta (XChaCha20-Poly1305)   │                                               │                               │          
     │                                        ◀───┘                                      │                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        ├───┐                                      │                                               │                               │          
     │                                        │   │ Pad to 256 bytes (bucket)            │                                               │                               │          
     │                                        ◀───┘                                      │                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        ├───┐                                      │                                               │                               │          
     │                                        │   │ Generate CEK, sign payload           │                                               │                               │          
     │                                        ◀───┘                                      │                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        │ ┌───────────────────┐                    │                                               │                               │          
     │                                        │ │ Final: ~256 bytes │                    │                                               │                               │          
     │                                        │ │ (v0x02 format)    │                    │                                               │                               │          
     │                                        │ └───────────────────┘                    │                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        │  POST /v2/send (recipient_token, blob)   │                                               │                               │          
     │                                        │──────────────────────────────────────────▶                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        │   ┌────────────────────────────────┐     │                                               │                               │          
     │                                        │   │ HTTP v2:                       │     │                                               │                               │          
     │                                        │   │ JSON body, base64-encoded blob │     │                                               │                               │          
     │                                        │   └────────────────────────────────┘     │                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        │                                          ├───┐                                           │                               │          
     │                                        │                                          │   │ Validate recipient_id format              │                               │          
     │                                        │                                          ◀───┘                                           │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        │                                          ├───┐                                           │                               │          
     │                                        │                                          │   │ Check quota (blobs < 1000, storage < 50MB)│                               │          
     │                                        │                                          ◀───┘                                           │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        │                                          ├───┐                                           │                               │          
     │                                        │                                          │   │ Store blob indexed by recipient_id        │                               │          
     │                                        │                                          ◀───┘                                           │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        │      Acknowledgment(status=Stored)       │                                               │                               │          
     │                                        ◀╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌│                                               │                               │          
     │                                        │                                          │                                               │                               │          
     │                                        │                                          │ ┌──────────────────────────────┐              │                               │          
     │                                        │                                          │ │ Blob stored with 120-day TTL │              │                               │          
     │                                        │                                          │ └──────────────────────────────┘              │                               │          
     │                                        │                                          │                                               │                               │          
     │                                    ┌alt [Bob is Online (Connected to Relay)]──────────────────────────────────────────────────────────────────────────────────────────┐      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │            Forward EncryptedUpdate            │                               │   │      
     │                                    │   │                                          │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                               │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │                                               ├───┐                           │   │      
     │                                    │   │                                          │                                               │   │ Receive encrypted blob    │   │      
     │                                    │   │                                          │                                               ◀───┘                           │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │                                               ├───┐                           │   │      
     │                                    │   │                                          │                                               │   │ Resolve anonymous sender ID   │      
     │                                    │   │                                          │                                               ◀───┘                           │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │                                               │ ┌────────────────────┐        │   │      
     │                                    │   │                                          │                                               │ │ Try each contact's │        │   │      
     │                                    │   │                                          │                                               │ │ shared key against │        │   │      
     │                                    │   │                                          │                                               │ │ anonymous_id       │        │   │      
     │                                    │   │                                          │                                               │ └────────────────────┘        │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │                                               ├───┐                           │   │      
     │                                    │   │                                          │                                               │   │ Found: Alice              │   │      
     │                                    │   │                                          │                                               ◀───┘                           │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │                                               ├───┐                           │   │      
     │                                    │   │                                          │                                               │   │ Derive message key (chain gen │3)    
     │                                    │   │                                          │                                               ◀───┘                           │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │                                               ├───┐                           │   │      
     │                                    │   │                                          │                                               │   │ Decrypt payload           │   │      
     │                                    │   │                                          │                                               ◀───┘                           │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │                                               ├───┐                           │   │      
     │                                    │   │                                          │                                               │   │ Remove padding            │   │      
     │                                    │   │                                          │                                               ◀───┘                           │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │                                               ├───┐                           │   │      
     │                                    │   │                                          │                                               │   │ Verify Ed25519 signature  │   │      
     │                                    │   │                                          │                                               ◀───┘                           │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │                                               ├───┐                           │   │      
     │                                    │   │                                          │                                               │   │ Update Alice's card locally   │      
     │                                    │   │                                          │                                               ◀───┘                           │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │                                               │  Alice updated contact info   │   │      
     │                                    │   │                                          │                                               │───────────────────────────────▶   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │  Acknowledgment(status=ReceivedByRecipient)   │                               │   │      
     │                                    │   │                                          ◀───────────────────────────────────────────────│                               │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │         Forward Acknowledgment           │                                               │                               │   │      
     │                                    │   ◀╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌│                                               │                               │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │       ┌─────────────────────────┐        │                                               │                               │   │      
     │                                    │   │       │ Sender notified if      │        │                                               │                               │   │      
     │                                    │   │       │ suppress_presence=false │        │                                               │                               │   │      
     │                                    │   │       └─────────────────────────┘        │                                               │                               │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   ├───┐                                      │                                               │                               │   │      
     │                                    │   │   │ Mark update as delivered             │                                               │                               │   │      
     │                                    │   ◀───┘                                      │                                               │                               │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                          │           ┌───────────────────────┐           │                               │   │      
     │                                    │   │                                          │           │ Blob queued for later │           │                               │   │      
     │                                    │   │                                          │           └───────────────────────┘           │                               │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    │   │                                      ┌opt [Bob comes online later]───────────────────────────┐                           │   │      
     │                                    │   │                                      │   │                                               │   │                           │   │      
     │                                    ├[Bob is Offline]╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤      
     │                                    │   │                                      │   │                                               │   │                           │   │      
     │                                    │   │                                      │   │              Connect (Handshake)              │   │                           │   │      
     │                                    │   │                                      │   ◀───────────────────────────────────────────────│   │                           │   │      
     │                                    │   │                                      │   │                                               │   │                           │   │      
     │                                    │   │                                      │   │             Deliver queued blobs              │   │                           │   │      
     │                                    │   │                                      │   │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶   │                           │   │      
     │                                    │   │                                      │   │                                               │   │                           │   │      
     │                                    │   │                                      │   │                                               ├───│                           │   │      
     │                                    │   │                                      │   │                                               │   │ Process all pending updates   │      
     │                                    │   │                                      │   │                                               ◀───│                           │   │      
     │                                    │   │                                      │   │                                               │   │                           │   │      
     │                                    │   │                                      │   │                                               ├───│                           │   │      
     │                                    │   │                                      │   │                                               │   │ Updates applied in order  │   │      
     │                                    │   │                                      │   │                                               ◀───│                           │   │      
     │                                    │   │                                      │   │                                               │   │                           │   │      
     │                                    │   │                       ┌────────────────────────────────┐                                 │   │                           │   │      
     │                                    │   │                       │ Bob now sees Alice's new phone │                                 │   │                           │   │      
     │                                    │   │                       └────────────────────────────────┘                                 │   │                           │   │      
     │                                    │   │                                      │   │                                               │   │                           │   │      
     │                                    │   │                                      └───────────────────────────────────────────────────────┘                           │   │      
     │                                    │   │                                          │                                               │                               │   │      
     │                                    └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘      
     │                                        │                                          │                                               │                               │          
 ┌───┴───┐                          ┌─────────┴─────────┐                          ┌─────┴─────┐                                ┌────────┴────────┐                   ┌──┴──┐       
 │ Alice │                          │ Alice's Device 📱 │                          │ Relay 🖥️ │                                │ Bob's Device 📱 │                   │ Bob │       
 └───────┘                          └───────────────────┘                          └───────────┘                                └─────────────────┘                   └─────┘       

Double Ratchet Message Flow

 ┌─────────────────┐                           ┌───────────────┐                                  
 │ Alice's Ratchet │                           │ Bob's Ratchet │                                  
 └────────┬────────┘                           └───────┬───────┘                                  
          │                                            │                                          
      ┌rect [rgb(240, 248, 255)]───────────────────────────┐                                      
      │   │                                            │   │                                      
      │   ├───┐                                        │   │                                      
      │   │   │ Ratchet send chain: gen 0 → 1          │   │                                      
      │   ◀───┘                                        │   │                                      
      │   │                                            │   │                                      
      │   ├───┐                                        │   │                                      
      │   │   │ Derive message key (gen 0)             │   │                                      
      │   ◀───┘                                        │   │                                      
      │   │                                            │   │                                      
      │   ├───┐                                        │   │                                      
      │   │   │ Encrypt with message key               │   │                                      
      │   ◀───┘                                        │   │                                      
      │   │                                            │   │                                      
      │   ├───┐                                        │   │                                      
      │   │   │ Delete message key                     │   │                                      
      │   ◀───┘                                        │   │                                      
      │   │                                            │   │                                      
      │   │    [DH_pub, gen=0, idx=0] + ciphertext     │   │                                      
      │   │────────────────────────────────────────────▶   │                                      
      │   │                                            │   │                                      
      │   │                                 ┌─────────────────────┐                               
      │   │                                 │ RECEIVE (Message 1) │                               
      │   │                                 └─────────────────────┘                               
      │   │                                            │   │                                      
      └────────────────────────────────────────────────────┘                                      
          │                                            │                                          
          │                                        ┌rect [r┐                                      
          │                                        │   │   │                                      
          │                                        │   ├───│                                      
          │                                        │   │   │ Verify DH generation matches         
          │                                        │   ◀───│                                      
          │                                        │   │   │                                      
          │                                        │   ├───│                                      
          │                                        │   │   │ Ratchet receive chain: gen 0 → 1     
          │                                        │   ◀───│                                      
          │                                        │   │   │                                      
          │                                        │   ├───│                                      
          │                                        │   │   │ Derive message key (gen 0)           
          │                                        │   ◀───│                                      
          │                                        │   │   │                                      
          │                                        │   ├───│                                      
          │                                        │   │   │ Decrypt                              
          │                                        │   ◀───│                                      
          │                                        │   │   │                                      
          │                                        │   ├───│                                      
          │                                        │   │   │ Delete message key                   
          │                                        │   ◀───│                                      
          │                                        │   │   │                                      
          │                          ┌──────────────────────────────────┐                         
          │                          │ SEND REPLY (triggers DH ratchet) │                         
          │                          └──────────────────────────────────┘                         
          │                                        │   │   │                                      
          │                                        └───────┘                                      
          │                                            │                                          
      ┌rect [rgb(255, 248, 240)]───────────────────────────┐                                      
      │   │                                            │   │                                      
      │   │                                            ├───│                                      
      │   │                                            │   │ Generate new ephemeral DH keypair    
      │   │                                            ◀───│                                      
      │   │                                            │   │                                      
      │   │                                            ├───│                                      
      │   │                                            │   │ DH ratchet: compute new root key     
      │   │                                            ◀───│                                      
      │   │                                            │   │                                      
      │   │                                            ├───│                                      
      │   │                                            │   │ Create new send chain                
      │   │                                            ◀───│                                      
      │   │                                            │   │                                      
      │   │                                            ├───│                                      
      │   │                                            │   │ Encrypt with new chain's key         
      │   │                                            ◀───│                                      
      │   │                                            │   │                                      
      │   │  [NEW_DH_pub, gen=1, idx=0] + ciphertext   │   │                                      
      │   ◀────────────────────────────────────────────│   │                                      
      │   │                                            │   │                                      
┌───────────────────────────────┐                      │   │                                      
│ RECEIVE (triggers DH ratchet) │                      │   │                                      
└───────────────────────────────┘                      │   │                                      
      │   │                                            │   │                                      
      └────────────────────────────────────────────────────┘                                      
          │                                            │                                          
      ┌rect [r┐                                        │                                          
      │   │   │                                        │                                          
      │   ├───│                                        │                                          
      │   │   │ Detect new DH public key               │                                          
      │   ◀───│                                        │                                          
      │   │   │                                        │                                          
      │   ├───│                                        │                                          
      │   │   │ DH ratchet: compute matching root key  │                                          
      │   ◀───│                                        │                                          
      │   │   │                                        │                                          
      │   ├───│                                        │                                          
      │   │   │ Create new receive chain               │                                          
      │   ◀───│                                        │                                          
      │   │   │                                        │                                          
      │   ├───│                                        │                                          
      │   │   │ Decrypt with new chain's key           │                                          
      │   ◀───│                                        │                                          
      │   │   │                                        │                                          
      └───────┘                                        │                                          
          │                                            │                                          
 ┌────────┴────────┐                           ┌───────┴───────┐                                  
 │ Alice's Ratchet │                           │ Bob's Ratchet │                                  
 └─────────────────┘                           └───────────────┘                                  

Out-of-Order Message Handling

┌────────────────┐                 ┌───────┐                ┌──────────────┐                                    
│ Alice's Device │                 │ Relay │                │ Bob's Device │                                    
└────────┬───────┘                 └───┬───┘                └───────┬──────┘                                    
         │                             │                            │                                           
         │  Message 1 (gen=0, idx=0)   │                            │                                           
         │─────────────────────────────▶                            │                                           
         │                             │                            │                                           
         │  Message 2 (gen=0, idx=1)   │                            │                                           
         │─────────────────────────────▶                            │                                           
         │                             │                            │                                           
         │  Message 3 (gen=0, idx=2)   │                            │                                           
         │─────────────────────────────▶                            │                                           
         │                             │                            │                                           
         │                    ┌────────────────┐                    │                                           
         │                    │ Network delays │                    │                                           
         │                    └────────────────┘                    │                                           
         │                             │                            │                                           
         │                             │  Message 3 arrives first   │                                           
         │                             │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                                           
         │                             │                            │                                           
         │                             │                            ├───┐                                       
         │                             │                            │   │ Expected idx=0, got idx=2             
         │                             │                            ◀───┘                                       
         │                             │                            │                                           
         │                             │                            ├───┐                                       
         │                             │                            │   │ Skip chain to idx=2                   
         │                             │                            ◀───┘                                       
         │                             │                            │                                           
         │                             │                            ├───┐                                       
         │                             │                            │   │ Store skipped keys: [idx=0, idx=1]    
         │                             │                            ◀───┘                                       
         │                             │                            │                                           
         │                             │                            ├───┐                                       
         │                             │                            │   │ Decrypt message 3                     
         │                             │                            ◀───┘                                       
         │                             │                            │                                           
         │                             │     Message 1 arrives      │                                           
         │                             │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                                           
         │                             │                            │                                           
         │                             │                            ├───┐                                       
         │                             │                            │   │ Lookup skipped key for idx=0          
         │                             │                            ◀───┘                                       
         │                             │                            │                                           
         │                             │                            ├───┐                                       
         │                             │                            │   │ Found! Decrypt message 1              
         │                             │                            ◀───┘                                       
         │                             │                            │                                           
         │                             │                            ├───┐                                       
         │                             │                            │   │ Delete skipped key                    
         │                             │                            ◀───┘                                       
         │                             │                            │                                           
         │                             │     Message 2 arrives      │                                           
         │                             │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                                           
         │                             │                            │                                           
         │                             │                            ├───┐                                       
         │                             │                            │   │ Lookup skipped key for idx=1          
         │                             │                            ◀───┘                                       
         │                             │                            │                                           
         │                             │                            ├───┐                                       
         │                             │                            │   │ Found! Decrypt message 2              
         │                             │                            ◀───┘                                       
         │                             │                            │                                           
         │                             │                            ├───┐                                       
         │                             │                            │   │ Delete skipped key                    
         │                             │                            ◀───┘                                       
         │                             │                            │                                           
         │                             │               ┌─────────────────────────┐                              
         │                             │               │ All messages processed, │                              
         │                             │               │ skipped keys cleaned up │                              
         │                             │               └─────────────────────────┘                              
         │                             │                            │                                           
┌────────┴───────┐                 ┌───┴───┐                ┌───────┴──────┐                                    
│ Alice's Device │                 │ Relay │                │ Bob's Device │                                    
└────────────────┘                 └───────┘                └──────────────┘                                    

Relay Acknowledgment States

●───●                 ╭──────╮                        ╭────────╮                                    ╭───────────────╮                ╭─────────────────────╮     ╔═══╗
│   │                 │      │                        │        │                                    │               │                │                     │     ║   ║
│   │─Message─created►│ Sent ├Relay─confirms─storage─►│ Stored │       ├────Recipient─connected────►│   Delivered   ├Recipient─acks─►│ ReceivedByRecipient ├────►║   ║
│   │                 │      │                        │        │                                    │               │                │                     │     ║   ║
●───●                 ╰──────╯                        ╰────┬───╯                                    ╰───────┬───────╯                ╰─────────────────────╯     ╚═══╝
                                                           │                                                │                                                         
                                                           │                                          Decrypt error                                                   
                                                           │                                                │                                                         
                                                           │                                                │                                                         
                                                           │                                                ▼                                                         
                                                           │                                        ╭───────────────╮                ╔═════════════════════╗          
                                                           │                                        │               │                ║                     ║          
                                                           └────────────Quota─exceeded─────────────►│     Failed    ├───────────────►║                     ║          
                                                                                                    │               │                ║                     ║          
                                                                                                    ╰───────────────╯                ╚═════════════════════╝          

Wire Protocol

Envelope Format

┌─────────────────────────────────────────────────────────────┐
│                    MESSAGE ENVELOPE                          │
├─────────────────────────────────────────────────────────────┤
│  4 bytes: Length (big-endian)                               │
│  JSON payload:                                               │
│  {                                                           │
│    "version": 1,                                            │
│    "message_id": "uuid",                                    │
│    "timestamp": unix_secs,                                  │
│    "payload": { ... }                                       │
│  }                                                           │
└─────────────────────────────────────────────────────────────┘

EncryptedUpdate Payload

{
  "type": "EncryptedUpdate",
  "sender_id": "anonymous_id (hourly rotation)",
  "recipient_id": "mailbox token (daily rotation)",
  "ratchet_header": {
    "dh_public": "[32 bytes] sender DH public key",
    "dh_generation": 5,
    "message_index": 10,
    "previous_chain_length": 3
  },
  "ciphertext": "base64(encrypted_delta)"
}

Acknowledgment Payload

{
  "type": "Acknowledgment",
  "message_id": "original message uuid",
  "status": "Stored|Delivered|Received|Failed",
  "error": null
}

Timing Estimates

PhaseDurationNotes
Encryption + padding1-5 msXChaCha20 is fast
Network latency50-200 msRelay location
Relay storage1-10 msSQLite insert
Forward to recipient50-200 msIf online
Decryption + verify1-5 ms
Total (online)100-400 msEnd-to-end
Total (offline)VariableUntil recipient connects

Device Linking Sequence

Interaction Type: 🤝 IN-PERSON (Proximity Required)

User links a new device to their existing identity. The new device receives the master seed and syncs all data. A confirmation code and proximity verification prevent unauthorized remote linking.

Participants

  • User - Person owning both devices
  • Device A (Primary) - Existing device with identity
  • Device B (New) - New device to be linked

Sequence Diagram

┌──────┐                                    ┌────────────────────┐         ┌────────────────┐                                                                
│ User │                                    │ Device A (Primary) │         │ Device B (New) │                                                                
└───┬──┘                                    └──────────┬─────────┘         └────────┬───────┘                                                                
    │                                                  │                            │                                                                        
    │      Settings > Devices > Link New Device        │                            │                                                                        
    │──────────────────────────────────────────────────▶                            │                                                                        
    │                                                  │                            │                                                                        
    │                                                  ├───┐                        │                                                                        
    │                                                  │   │ Generate ephemeral link_key (32 bytes)                                                          
    │                                                  ◀───┘                        │                                                                        
    │                                                  │                            │                                                                        
    │                                                  ├───┐                        │                                                                        
    │                                                  │   │ Sign QR fields with identity Ed25519 key                                                        
    │                                                  ◀───┘                        │                                                                        
    │                                                  │                            │                                                                        
    │                                                  ├───┐                        │                                                                        
    │                                                  │   │ Create QR: WBDL | version | identity_pubkey | link_key | timestamp | signature                  
    │                                                  ◀───┘                        │                                                                        
    │                                                  │                            │                                                                        
    │     Display QR code (expires in 5 minutes)       │                            │                                                                        
    ◀──────────────────────────────────────────────────│                            │                                                                        
    │                                                  │                            │                                                                        
    │                           Link to Existing Identity                           │                                                                        
    │───────────────────────────────────────────────────────────────────────────────▶                                                                        
    │                                                  │                            │                                                                        
    │                                                  │                            ├───┐                                                                    
    │                                                  │                            │   │ Scan QR code from Device A                                         
    │                                                  │                            ◀───┘                                                                    
    │                                                  │                            │                                                                        
    │                                                  │                            ├───┐                                                                    
    │                                                  │                            │   │ Validate WBDL magic, version, signature, expiry                    
    │                                                  │                            ◀───┘                                                                    
    │                                                  │                            │                                                                        
    │                                                  │                            ├───┐                                                                    
    │                                                  │                            │   │ Create DeviceLinkRequest (device_name, random nonce, timestamp)    
    │                                                  │                            ◀───┘                                                                    
    │                                                  │                            │                                                                        
    │                                                  │                            ├───┐                                                                    
    │                                                  │                            │   │ Encrypt request with link_key (ChaCha20-Poly1305)                  
    │                                                  │                            ◀───┘                                                                    
    │                                                  │                            │                                                                        
    │                                                  │  Send encrypted request    │                                                                        
    │                                                  ◀────────────────────────────│                                                                        
    │                                                  │                            │                                                                        
    │                                            ┌───────────────────────────────────────┐                                                                   
    │                                            │ CONFIRMATION & PROXIMITY VERIFICATION │                                                                   
    │                                            └───────────────────────────────────────┘                                                                   
    │                                                  │                            │                                                                        
    │                                                  ├───┐                        │                                                                        
    │                                                  │   │ Decrypt request using link_key                                                                  
    │                                                  ◀───┘                        │                                                                        
    │                                                  │                            │                                                                        
    │                                                  ├───┐                        │                                                                        
    │                                                  │   │ Derive confirmation code: HMAC-SHA256(link_key, nonce) → XXX-XXX                                
    │                                                  ◀───┘                        │                                                                        
    │                                                  │                            │                                                                        
    │  Show: "Link device 'Device B'? Code: XXX-XXX"   │                            │                                                                        
    ◀──────────────────────────────────────────────────│                            │                                                                        
    │                                                  │                            │                                                                        
    │                                                  │                            ├───┐                                                                    
    │                                                  │                            │   │ Derive same confirmation code from link_key + nonce                
    │                                                  │                            ◀───┘                                                                    
    │                                                  │                            │                                                                        
    │                      Show: "Confirmation code: XXX-XXX"                       │                                                                        
    ◀───────────────────────────────────────────────────────────────────────────────│                                                                        
    │                                                  │                            │                                                                        
    ├───┐                                              │                            │                                                                        
    │   │ Verify codes match on both screens           │                            │                                                                        
    ◀───┘                                              │                            │                                                                        
    │                                                  │                            │                                                                        
    │                  Confirm link                    │                            │                                                                        
    │──────────────────────────────────────────────────▶                            │                                                                        
    │                                                  │                            │                                                                        
    │                                                  ├───┐                        │                                                                        
    │                                                  │   │ Set proximity verified │                                                                        
    │                                                  ◀───┘                        │                                                                        
    │                                                  │                            │                                                                        
    │                                                  │   ┌───────────────────┐    │                                                                        
    │                                                  │   │ IDENTITY TRANSFER │    │                                                                        
    │                                                  │   └───────────────────┘    │                                                                        
    │                                                  │                            │                                                                        
    │                                                  ├───┐                        │                                                                        
    │                                                  │   │ Derive new device keys from master_seed + device_index                                          
    │                                                  ◀───┘                        │                                                                        
    │                                                  │                            │                                                                        
    │                                                  ├───┐                        │                                                                        
    │                                                  │   │ Add Device B to registry, re-sign                                                               
    │                                                  ◀───┘                        │                                                                        
    │                                                  │                            │                                                                        
    │                                                  ├───┐                        │                                                                        
    │                                                  │   │ Build response: master_seed + display_name + device_index + registry + sync_payload             
    │                                                  ◀───┘                        │                                                                        
    │                                                  │                            │                                                                        
    │                                                  ├───┐                        │                                                                        
    │                                                  │   │ Encrypt response with link_key (ChaCha20-Poly1305)                                              
    │                                                  ◀───┘                        │                                                                        
    │                                                  │                            │                                                                        
    │                                                  │  Send encrypted response   │                                                                        
    │                                                  │────────────────────────────▶                                                                        
    │                                                  │                            │                                                                        
    │                                                  │                            ├───┐                                                                    
    │                                                  │                            │   │ Decrypt response                                                   
    │                                                  │                            ◀───┘                                                                    
    │                                                  │                            │                                                                        
    │                                                  │                            ├───┐                                                                    
    │                                                  │                            │   │ Extract master_seed, registry, sync_payload                        
    │                                                  │                            ◀───┘                                                                    
    │                                                  │                            │                                                                        
    │                                                  │                            ├───┐                                                                    
    │                                                  │                            │   │ Derive own device keys from master_seed + device_index             
    │                                                  │                            ◀───┘                                                                    
    │                                                  │                            │                                                                        
    │                                                  │                            ├───┐                                                                    
    │                                                  │                            │   │ Store identity locally                                             
    │                                                  │                            ◀───┘                                                                    
    │                                                  │                            │                                                                        
    │                                                  │                            ├───┐                                                                    
    │                                                  │                            │   │ Apply sync payload (contacts, card)                                
    │                                                  │                            ◀───┘                                                                    
    │                                                  │                            │                                                                        
    │          Device B linked successfully            │                            │                                                                        
    ◀──────────────────────────────────────────────────│                            │                                                                        
    │                                                  │                            │                                                                        
    │                           Welcome back, [Your Name]                           │                                                                        
    ◀───────────────────────────────────────────────────────────────────────────────│                                                                        
    │                                                  │                            │                                                                        
    │                                          ┌──────────────────────────────────────────┐                                                                  
    │                                          │ Both devices now share the same identity │                                                                  
    │                                          └──────────────────────────────────────────┘                                                                  
    │                                                  │                            │                                                                        
┌───┴──┐                                    ┌──────────┴─────────┐         ┌────────┴───────┐                                                                
│ User │                                    │ Device A (Primary) │         │ Device B (New) │                                                                
└──────┘                                    └────────────────────┘         └────────────────┘                                                                

Data Exchanged

Binary format with WBDL magic bytes, base64-encoded for QR:

WBDL              (4 bytes magic)
version           (1 byte, currently 1)
identity_pubkey   (32 bytes, Ed25519 public key)
link_key          (32 bytes, random ephemeral key)
timestamp         (8 bytes, big-endian u64 unix seconds)
signature         (64 bytes, Ed25519 over all preceding fields)
─────────────────
Total: 141 bytes  (before base64 encoding)

The QR expires after 300 seconds (5 minutes). Signature is verified by the new device using the embedded identity public key.

Confirmation Code

Derived independently by both devices:

HMAC-SHA256(link_key, request_nonce)
  → first 4 bytes as big-endian u32
  → modulo 1,000,000
  → formatted as XXX-XXX

Both devices display the same code. User verifies they match.

Proximity Challenge

For external proximity verification (NFC, ultrasonic, etc.):

HKDF(ikm=link_key, info="vauchi-device-link-proximity-v1", len=16)
  → 16-byte challenge

Both devices derive the same challenge from the shared link key.

DeviceLinkRequest (New → Existing)

Encrypted with ChaCha20-Poly1305 using link_key:

device_name_len   (4 bytes, little-endian u32)
device_name       (variable, UTF-8)
nonce             (32 bytes, random)
timestamp         (8 bytes, little-endian u64)

DeviceLinkResponse (Existing → New)

Encrypted with ChaCha20-Poly1305 using link_key:

master_seed       (32 bytes, zeroized after use)
display_name_len  (4 bytes, little-endian u32)
display_name      (variable, UTF-8)
device_index      (4 bytes, little-endian u32)
registry_json_len (4 bytes, little-endian u32)
registry_json     (variable, signed DeviceRegistry)
sync_payload_len  (4 bytes, little-endian u32)
sync_payload_json (variable, contacts + card)

Security Properties

PropertyMechanism
Seed EncryptionChaCha20-Poly1305 with ephemeral link_key
QR AuthenticationEd25519 signature over QR fields
Confirmation CodeHMAC-SHA256(link_key, nonce) displayed on both devices
Proximity VerificationHKDF-derived 16-byte challenge; enforced before confirm
Replay PreventionRandom 32-byte nonce in each request
Token ExpiryQR expires after 5 minutes
Registry IntegrityEd25519 signature over version + device list
Memory SafetyMaster seed zeroized on Drop
Device LimitMaximum 10 devices per identity

Numeric Code Fallback (No Camera)

┌──────┐                      ┌──────────┐            ┌────────────────────────┐                                         
│ User │                      │ Device A │            │ Device B (Desktop/CLI) │                                         
└───┬──┘                      └─────┬────┘            └────────────┬───────────┘                                         
    │                               │                              │                                                     
    │      Generate link code       │                              │                                                     
    │───────────────────────────────▶                              │                                                     
    │                               │                              │                                                     
    │  Show QR code + data string   │                              │                                                     
    ◀───────────────────────────────│                              │                                                     
    │                               │                              │                                                     
    │                  Link to Existing Identity                   │                                                     
    │──────────────────────────────────────────────────────────────▶                                                     
    │                               │                              │                                                     
    │               Paste data string from Device A                │                                                     
    │──────────────────────────────────────────────────────────────▶                                                     
    │                               │                              │                                                     
    │                               │                              ├───┐                                                 
    │                               │                              │   │ Parse WBDL data, validate signature + expiry    
    │                               │                              ◀───┘                                                 
    │                               │                              │                                                     
    │                          ┌──────────────────────────────────────┐                                                  
    │                          │ Same confirmation code flow as above │                                                  
    │                          └──────────────────────────────────────┘                                                  
    │                               │                              │                                                     
    │         Code: XXX-XXX         │                              │                                                     
    ◀───────────────────────────────│                              │                                                     
    │                               │                              │                                                     
    │                        Code: XXX-XXX                         │                                                     
    ◀──────────────────────────────────────────────────────────────│                                                     
    │                               │                              │                                                     
    │            Confirm            │                              │                                                     
    │───────────────────────────────▶                              │                                                     
    │                               │                              │                                                     
    │                               │  Encrypted identity bundle   │                                                     
    │                               │──────────────────────────────▶                                                     
    │                               │                              │                                                     
    │                               │                              ├───┐                                                 
    │                               │                              │   │ Complete linking                                
    │                               │                              ◀───┘                                                 
    │                               │                              │                                                     
┌───┴──┐                      ┌─────┴────┐            ┌────────────┴───────────┐                                         
│ User │                      │ Device A │            │ Device B (Desktop/CLI) │                                         
└──────┘                      └──────────┘            └────────────────────────┘                                         

Revoking a Device

┌──────┐                                ┌──────────┐         ┌──────────┐              ┌───────┐      
│ User │                                │ Device A │         │ Device B │              │ Relay │      
└───┬──┘                                └─────┬────┘         └─────┬────┘              └───┬───┘      
    │                                         │                    │                       │          
    │  Settings > Devices > Revoke Device B   │                    │                       │          
    │─────────────────────────────────────────▶                    │                       │          
    │                                         │                    │                       │          
    │           Confirm revocation            │                    │                       │          
    │─────────────────────────────────────────▶                    │                       │          
    │                                         │                    │                       │          
    │                                         ├───┐                │                       │          
    │                                         │   │ Mark Device B as revoked in registry   │          
    │                                         ◀───┘                │                       │          
    │                                         │                    │                       │          
    │                                         ├───┐                │                       │          
    │                                         │   │ Re-sign registry with identity key     │          
    │                                         ◀───┘                │                       │          
    │                                         │                    │                       │          
    │                                         ├───┐                │                       │          
    │                                         │   │ Increment registry version             │          
    │                                         ◀───┘                │                       │          
    │                                         │                    │                       │          
    │                                         │     Push updated registry (encrypted)      │          
    │                                         │────────────────────────────────────────────▶          
    │                                         │                    │                       │          
    │                                         │                    │  Forward revocation   │          
    │                                         │                    ◀╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌│          
    │                                         │                    │                       │          
    │                                         │                    ├───┐                   │          
    │                                         │                    │   │ Receive revocation notice    
    │                                         │                    ◀───┘                   │          
    │                                         │                    │                       │          
    │                                         │                    ├───┐                   │          
    │                                         │                    │   │ Wipe all identity data       
    │                                         │                    ◀───┘                   │          
    │                                         │                    │                       │          
    │                                         │                    ├───┐                   │          
    │                                         │                    │   │ Return to welcome screen     
    │                                         │                    ◀───┘                   │          
    │                                         │                    │                       │          
    │            Device B revoked             │                    │                       │          
    ◀─────────────────────────────────────────│                    │                       │          
    │                                         │                    │                       │          
    │                This device has been unlinked                 │                       │          
    ◀──────────────────────────────────────────────────────────────│                       │          
    │                                         │                    │                       │          
┌───┴──┐                                ┌─────┴────┐         ┌─────┴────┐              ┌───┴───┐      
│ User │                                │ Device A │         │ Device B │              │ Relay │      
└──────┘                                └──────────┘         └──────────┘              └───────┘      

Platform Implementation Status

PlatformStatusNotes
Core APICompleteFull protocol with tests
CLIComplete7 commands: list, link, join, complete, finish, revoke, info
Desktop (native)CompleteNative UI (SwiftUI/GTK/Qt) with QR display, confirmation overlay
TUICompleteratatui UI with QR overlay, vim-style navigation
iOSPlannedAwaiting mobile bindings
AndroidPlannedAwaiting mobile bindings

Contact Recovery Sequence

Interaction Type: 🤝 + ☁️ MIXED (In-Person Vouching + Remote Distribution)

When a user loses all devices, they can recover their contact relationships through social vouching. Existing contacts vouch for the user in-person, and the recovery proof is distributed remotely via relay.

Participants

  • Alice - User who lost their device
  • Alice's New Device - Fresh install, new identity
  • Bob, Charlie, Betty - Alice's contacts who will vouch
  • John, David - Alice's contacts who will receive recovery proof
  • Relay - HTTP v2 relay server

Overview

┌────────────────────────────────────────────────────────────────────┐                                 
│                   PHASE 1: Vouching (In-Person)                    │                                 
│                                                                    │                                 
│                                                                    │                                 
│ ┌─────────────────────────────────┐     ┌─────────┐     ┌────────┐ │                                 
│ │                                 │     │         │     │        │ │                                 
│ │                                 │     │         │     │        │ │                                 
│ │               Bob               │     │ Charlie │     │ Betty  │ │                                 
│ │              Vouch              │     │  Vouch  │     │ Vouch  │ │                                 
│ │                                 │     │         │     │        │ │                                 
│ └────────────────┬────────────────┘     └────┬────┘     └────┬───┘ │                                 
│                  │                           │               │     │                                 
│                  │                           │               │     │                                 
│                  ├───────────────────────────┴───────────────┘     │                                 
│                  │                                                 │                                 
│                  ▼                                                 │                                 
│ ┌─────────────────────────────────┐                                │                                 
│ │                                 │                                │                                 
│ │                                 │                                │                                 
│ │           Alice (new)           │                                │                                 
│ │      Threshold: 3 vouchers      │                                │                                 
│ │                                 │                                │                                 
│ └────────────────┬────────────────┘                                │                                 
│                  │                                                 │                                 
└──────────────────┼─────────────────────────────────────────────────┘                                 
                   │                                                                                   
                   │                                                                                   
                   │                                                                                   
┌──────────────────┼──────────────────────────────────────────────────────────────────────────────────┐
│                  │                PHASE 2: Distribution (Remote)                                    │
│                  │                                                                                  │
│                  ▼                                                                                  │
│ ┌─────────────────────────────────┐                                                                 │
│ │                                 │                                                                 │
│ │                                 │                                                                 │
│ │          OHTTP Gateway          │                                                                 │
│ │        (strips client IP)       │                                                                 │
│ │                                 │                                                                 │
│ └─────────────────────────────────┘                                                                 │
│                  │                                                                                  │
│                  │                                                                                  │
│                  ├───────────────────────────┬───────────────┬───────────────────────┐              │
│                  │                           │               │                       │              │
│                  ▼                           ▼               ▼                       ▼              │
│ ┌─────────────────────────────────┐     ┌─────────┐     ┌────────┐     ┌──────────────────────────┐ │
│ │                                 │     │         │     │        │     │                          │ │
│ │                                 │     │         │     │        │     │                          │ │
│ │              Relay              │     │   John  │     │ David  │     │          Others          │ │
│ │ Stores proof under hash(pk_old) │     │  Accept │     │ Verify │     │ Discover via relay query │ │
│ │                                 │     │         │     │        │     │                          │ │
│ └─────────────────────────────────┘     └─────────┘     └────────┘     └──────────────────────────┘ │
│                                                                                                     │
└─────────────────────────────────────────────────────────────────────────────────────────────────────┘

Note: All client↔relay traffic in Phase 2 is routed through an OHTTP gateway per ADR-037. The detailed sequence diagrams below omit the gateway hop for clarity — they describe the protocol layer, not the transport. Operationally, the relay never sees client IP addresses; the gateway never sees request content.

Phase 1: In-Person Vouching

 ┌─────────────────────┐            ┌──────────────────┐         ┌──────────────┐                           ┌─────┐   
 │ Alice (Lost Device) │            │ Alice New Device │         │ Bob's Device │                           │ Bob │   
 └──────────┬──────────┘            └─────────┬────────┘         └───────┬──────┘                           └──┬──┘   
            │                                 │                          │                                     │      
            │  Install Vauchi on new device   │                          │                                     │      
            │─────────────────────────────────▶                          │                                     │      
            │                                 │                          │                                     │      
            │                                 ├───┐                      │                                     │      
            │                                 │   │ Create new identity (pk_new)                               │      
            │                                 ◀───┘                      │                                     │      
            │                                 │                          │                                     │      
            │      I had identity pk_old      │                          │                                     │      
            │─────────────────────────────────▶                          │                                     │      
            │                                 │                          │                                     │      
            │                                 ├───┐                      │                                     │      
            │                                 │   │ Store recovery claim: pk_old → pk_new                      │      
            │                                 ◀───┘                      │                                     │      
            │                                 │                          │                                     │      
            │      Generate recovery QR       │                          │                                     │      
            │─────────────────────────────────▶                          │                                     │      
            │                                 │                          │                                     │      
            │                                 ├───┐                      │                                     │      
            │                                 │   │ Create recovery claim QR                                   │      
            │                                 ◀───┘                      │                                     │      
            │                                 │                          │                                     │      
            │                                 │ ┌────────────────────────┐                                     │      
            │                                 │ │ QR contains:           │                                     │      
            │                                 │ │ - type: recovery_claim │                                     │      
            │                                 │ │ - old_pk: pk_old       │                                     │      
            │                                 │ │ - new_pk: pk_new       │                                     │      
            │                                 │ │ - timestamp            │                                     │      
            │                                 │ └────────────────────────┘                                     │      
            │                                 │                          │                                     │      
            │       Display recovery QR       │                          │                                     │      
            ◀─────────────────────────────────│                          │                                     │      
            │                                 │                          │                                     │      
            │                                 │                          │      Scan Alice's recovery QR       │      
            │                                 │                          ◀─────────────────────────────────────│      
            │                                 │                          │                                     │      
            │                                 │                          ├───┐                                 │      
            │                                 │                          │   │ Decode recovery claim           │      
            │                                 │                          ◀───┘                                 │      
            │                                 │                          │                                     │      
            │                                 │                          ├───┐                                 │      
            │                                 │                          │   │ Lookup pk_old in contacts       │      
            │                                 │                          ◀───┘                                 │      
            │                                 │                          │                                     │      
            │                                 │                          ├───┐                                 │      
            │                                 │                          │   │ Found: "Alice" with pk_old      │      
            │                                 │                          ◀───┘                                 │      
            │                                 │                          │                                     │      
            │                                 │                          │      Alice claims device loss       │      
            │                                 │                          │─────────────────────────────────────▶      
            │                                 │                          │                                     │      
            │                                 │                          │  Show Alice's stored name & photo   │      
            │                                 │                          │─────────────────────────────────────▶      
            │                                 │                          │                                     │      
            │                                 │                       ┌──────────────────────────────────────────┐    
            │                                 │                       │ Bob verifies Alice is physically present │    
            │                                 │                       └──────────────────────────────────────────┘    
            │                                 │                          │                                     │      
            │                                 │                          │    Yes, this is Alice, I confirm    │      
            │                                 │                          ◀─────────────────────────────────────│      
            │                                 │                          │                                     │      
            │                                 │                          ├───┐                                 │      
            │                                 │                          │   │ Create voucher                  │      
            │                                 │                          ◀───┘                                 │      
            │                                 │                          │                                     │      
            │                                 │                          │ ┌─────────────────────┐             │      
            │                                 │                          │ │ Voucher:            │             │      
            │                                 │                          │ │ - old_pk            │             │      
            │                                 │                          │ │ - new_pk            │             │      
            │                                 │                          │ │ - voucher_pk (Bob)  │             │      
            │                                 │                          │ │ - timestamp         │             │      
            │                                 │                          │ │ - Ed25519 signature │             │      
            │                                 │                          │ └─────────────────────┘             │      
            │                                 │                          │                                     │      
            │                                 │  Send voucher to Alice   │                                     │      
            │                                 ◀──────────────────────────│                                     │      
            │                                 │                          │                                     │      
            │                                 ├───┐                      │                                     │      
            │                                 │   │ Store Bob's voucher (1 of 3)                               │      
            │                                 ◀───┘                      │                                     │      
            │                                 │                          │                                     │      
            │    Bob vouched for you (1/3)    │                          │                                     │      
            ◀─────────────────────────────────│                          │                                     │      
            │                                 │                          │                                     │      
            │                            ┌───────────────────────────────────────┐                             │      
            │                            │ Alice now has 1 voucher, needs 2 more │                             │      
            │                            └───────────────────────────────────────┘                             │      
            │                                 │                          │                                     │      
 ┌──────────┴──────────┐            ┌─────────┴────────┐         ┌───────┴──────┐                           ┌──┴──┐   
 │ Alice (Lost Device) │            │ Alice New Device │         │ Bob's Device │                           │ Bob │   
 └─────────────────────┘            └──────────────────┘         └──────────────┘                           └─────┘   

Collecting Multiple Vouchers

┌──────────────────┐  ┌──────────────────┐  ┌────────────────┐                                 
│ Alice New Device │  │ Charlie's Device │  │ Betty's Device │                                 
└─────────┬────────┘  └─────────┬────────┘  └────────┬───────┘                                 
          │                     │                    │                                         
      ┌rect [rgb(240, 248, 255)]────┐                │                                         
      │   │                     │   │                │                                         
      │   │  Show recovery QR   │   │                │                                         
      │   │─────────────────────▶   │                │                                         
      │   │                     │   │                │                                         
      │   │                     ├───│                │                                         
      │   │                     │   │ Verify pk_old is contact "Alice"                         
      │   │                     ◀───│                │                                         
      │   │                     │   │                │                                         
      │   │    Send voucher     │   │                │                                         
      │   ◀─────────────────────│   │                │                                         
      │   │                     │   │                │                                         
      │   ├───┐                 │   │                │                                         
      │   │   │ Store Charlie's vouc│er (2 of 3)     │                                         
      │   ◀───┘                 │   │                │                                         
      │   │                     │   │                │                                         
      │   │          ┌───────────────────┐           │                                         
      │   │          │ Alice meets Betty │           │                                         
      │   │          └───────────────────┘           │                                         
      │   │                     │   │                │                                         
      └─────────────────────────────┘                │                                         
          │                     │                    │                                         
      ┌rect [rgb(240, 255, 240)]─────────────────────────┐                                     
      │   │                     │                    │   │                                     
      │   │            Show recovery QR              │   │                                     
      │   │──────────────────────────────────────────▶   │                                     
      │   │                     │                    │   │                                     
      │   │                     │                    ├───│                                     
      │   │                     │                    │   │ Verify pk_old is contact "Alice"    
      │   │                     │                    ◀───│                                     
      │   │                     │                    │   │                                     
      │   │              Send voucher                │   │                                     
      │   ◀──────────────────────────────────────────│   │                                     
      │   │                     │                    │   │                                     
      │   ├───┐                 │                    │   │                                     
      │   │   │ Store Betty's voucher (3 of 3)       │   │                                     
      │   ◀───┘                 │                    │   │                                     
      │   │                     │                    │   │                                     
┌─────────────────────────────────────┐              │   │                                     
│ THRESHOLD MET: 3 vouchers collected │              │   │                                     
└─────────────────────────────────────┘              │   │                                     
      │   │                     │                    │   │                                     
      └──────────────────────────────────────────────────┘                                     
          │                     │                    │                                         
          ├───┐                 │                    │                                         
          │   │ Create recovery proof                │                                         
          ◀───┘                 │                    │                                         
          │                     │                    │                                         
          │ ┌───────────────────────────────────┐    │                                         
          │ │ Recovery Proof:   │               │    │                                         
          │ │ - old_pk          │               │    │                                         
          │ │ - new_pk          │               │    │                                         
          │ │ - threshold: 3    │               │    │                                         
          │ │ - vouchers: [Bob, Charlie, Betty] │    │                                         
          │ └───────────────────────────────────┘    │                                         
          │                     │                    │                                         
┌─────────┴────────┐  ┌─────────┴────────┐  ┌────────┴───────┐                                 
│ Alice New Device │  │ Charlie's Device │  │ Betty's Device │                                 
└──────────────────┘  └──────────────────┘  └────────────────┘                                 

Phase 2: Remote Distribution

┌──────────────────┐             ┌───────┐                                ┌───────────────┐  ┌────────────────┐                                                             
│ Alice New Device │             │ Relay │                                │ John's Device │  │ David's Device │                                                             
└─────────┬────────┘             └───┬───┘                                └───────┬───────┘  └────────┬───────┘                                                             
          │                          │                                            │                   │                                                                     
          │  Upload recovery proof   │                                            │                   │                                                                     
          │──────────────────────────▶                                            │                   │                                                                     
          │                          │                                            │                   │                                                                     
          │                          ├───┐                                        │                   │                                                                     
          │                          │   │ Store under key: hash(pk_old)          │                   │                                                                     
          │                          ◀───┘                                        │                   │                                                                     
          │                          │                                            │                   │                                                                     
          │         Stored           │                                            │                   │                                                                     
          ◀╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌│                                            │                   │                                                                     
          │                          │                                            │                   │                                                                     
          │            ┌──────────────────────────┐                               │                   │                                                                     
          │            │ Proof stored for 90 days │                               │                   │                                                                     
          │            └──────────────────────────┘                               │                   │                                                                     
          │                          │                                            │                   │                                                                     
          │                          │  Batch query for contact recovery proofs   │                   │                                                                     
          │                          ◀────────────────────────────────────────────│                   │                                                                     
          │                          │                                            │                   │                                                                     
          │                          │                                            │ ┌──────────────────────────────────────────────────┐                                    
          │                          │                                            │ │ Query: [hash(pk1), hash(pk2), hash(pk_old), ...] │                                    
          │                          │                                            │ └──────────────────────────────────────────────────┘                                    
          │                          │                                            │                   │                                                                     
          │                          │       Found proof for hash(pk_old)         │                   │                                                                     
          │                          │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                   │                                                                     
          │                          │                                            │                   │                                                                     
          │                          │                                            ├───┐               │                                                                     
          │                          │                                            │   │ Decode recovery proof                                                               
          │                          │                                            ◀───┘               │                                                                     
          │                          │                                            │                   │                                                                     
          │                          │                                            ├───┐               │                                                                     
          │                          │                                            │   │ Verify: pk_old is contact "Alice"                                                   
          │                          │                                            ◀───┘               │                                                                     
          │                          │                                            │                   │                                                                     
          │                          │                                            ├───┐               │                                                                     
          │                          │                                            │   │ Check vouchers for mutual contacts                                                  
          │                          │                                            ◀───┘               │                                                                     
          │                          │                                            │                   │                                                                     
          │                          │                                        ┌alt [Ha┐               │                                                                     
          │                          │                                        │   │   │               │                                                                     
          │                          │                                        │   ├───│               │                                                                     
          │                          │                                        │   │   │ Bob is my contact                                                                   
          │                          │                                        │   ◀───│               │                                                                     
          │                          │                                        │   │   │               │                                                                     
          │                          │                                        │   ├───│               │                                                                     
          │                          │                                        │   │   │ Charlie is my contact                                                               
          │                          │                                        │   ◀───│               │                                                                     
          │                          │                                        │   │   │               │                                                                     
          │                          │                                        │   ├───│               │                                                                     
          │                          │                                        │   │   │ 2 mutual vouchers ≥ threshold                                                       
          │                          │                                        │   ◀───│               │                                                                     
          │                          │                                        │   │   │               │                                                                     
          │                          │                                        │   ├───│               │                                                                     
          │                          │                                        │   │   │ High confidence recovery                                                            
          │                          │                                        │   ◀───│               │                                                                     
          │                          │                                        │   │   │               │                                                                     
          │                          │                                        ├[No Mut┤               │                                                                     
          │                          │                                        │   │   │               │                                                                     
          │                          │                                        │   ├───│               │                                                                     
          │                          │                                        │   │   │ No vouchers are my contacts                                                         
          │                          │                                        │   ◀───│               │                                                                     
          │                          │                                        │   │   │               │                                                                     
          │                          │                                        │   ├───│               │                                                                     
          │                          │                                        │   │   │ Cannot verify - meet Alice in person                                                
          │                          │                                        │   ◀───│               │                                                                     
          │                          │                                        │   │   │               │                                                                     
          │                          │                                        └───────┘               │                                                                     
          │                          │                                            │                   │                                                                     
          │                          │                   Query for recovery proofs│                   │                                                                     
          │                          ◀────────────────────────────────────────────────────────────────│                                                                     
          │                          │                                            │                   │                                                                     
          │                          │                     Found proof for Alice  │                   │                                                                     
          │                          │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶                                                                     
          │                          │                                            │                   │                                                                     
          │                          │                                            │                   ├───┐                                                                 
          │                          │                                            │                   │   │ Check vouchers: Bob, Charlie, Betty                             
          │                          │                                            │                   ◀───┘                                                                 
          │                          │                                            │                   │                                                                     
          │                          │                                            │                   ├───┐                                                                 
          │                          │                                            │                   │   │ None are David's contacts                                       
          │                          │                                            │                   ◀───┘                                                                 
          │                          │                                            │                   │                                                                     
          │                          │                                            │                   ├───┐                                                                 
          │                          │                                            │                   │   │ Warning: Unknown vouchers                                       
          │                          │                                            │                   ◀───┘                                                                 
          │                          │                                            │                   │                                                                     
          │                          │                                            │                   ├───┐                                                                 
          │                          │                                            │                   │   │ Options: Meet in person / Verify another way / Accept anyway    
          │                          │                                            │                   ◀───┘                                                                 
          │                          │                                            │                   │                                                                     
┌─────────┴────────┐             ┌───┴───┐                                ┌───────┴───────┐  ┌────────┴───────┐                                                             
│ Alice New Device │             │ Relay │                                │ John's Device │  │ David's Device │                                                             
└──────────────────┘             └───────┘                                └───────────────┘  └────────────────┘                                                             

Data Structures

Recovery Claim QR

{
  "type": "recovery_claim",
  "old_pk": "Ed25519 public key (lost)",
  "new_pk": "Ed25519 public key (new)",
  "timestamp": "2026-01-21T10:00:00Z"
}

Voucher

{
  "old_pk": "Alice's old public key",
  "new_pk": "Alice's new public key",
  "voucher_pk": "Bob's public key",
  "timestamp": "2026-01-21T10:05:00Z",
  "signature": "Ed25519 signature of above fields"
}

Recovery Proof

{
  "old_pk": "Alice's old public key",
  "new_pk": "Alice's new public key",
  "threshold": 3,
  "vouchers": [
    { /* Bob's voucher */ },
    { /* Charlie's voucher */ },
    { /* Betty's voucher */ }
  ],
  "expires": "2026-04-21T10:00:00Z"
}

Security Properties

PropertyMechanism
In-Person VouchingVouchers must physically verify the person
Threshold SecurityRequires N vouchers (configurable, default 3)
Mutual Contact VerificationRecipients verify via contacts they trust
Relay PrivacyRelay stores proof under hash, learns nothing
Replay PreventionTimestamps, signatures, 90-day expiry
Attack DetectionConflicting claims trigger warnings

Crypto Key Hierarchy

Visual documentation of Vauchi's cryptographic key hierarchy and derivation paths.

Master Hierarchy

┌────────────────────────────────────┐                                                                                                                                                                                                      
│         Identity Creation          │                                                                                                                                                                                                      
│                                    │                                                                                                                                                                                                      
│                                    │                                                                                                                                                                                                      
│ ┌────────────────────────────────┐ │                                                                                                                                                                                                      
│ │                                │ │                                                                                                                                                                                                      
│ │                                │ │                                                                                                                                                                                                      
│ │          Master Seed           ├─┼───────────────────HKDF────────────┬──────────┐                                                                                                                                                       
│ │       (256-bit, CSPRNG)        │ │      info='Vauchi_Exchange_Seed_v2'          │                                                                                                                                                       
│ │                                │ │                                   │          │                                                                                                                                                       
│ └────────────────┬───────────────┘ │                                   └──────────┼─────────────────────────HKDF─────────────────────────────────────┐                                                                                    
│                  │                 │                                              │              info='Vauchi_Shred_Key_v2'                          │                                                                                    
└──────────────────┼─────────────────┘                                              │                                                                  │                                                                                    
               raw seed                                                             │                                                                  │                                                                                    
         (Ed25519 requirement)                                                      │                                                                  │                                                                                    
                   │                                                                │                                                                  │                                                                                    
┌──────────────────┼─────────────────┐                                 ┌────────────┼────────────┐                                 ┌───────────────────┼───────────────────────────────────────────────────────────────────────────────────┐
│           Signing│Keys             │                                 │      Exchange Keys      │                                 │                   │                      Shredding Hierarchy                                          │
│                  │                 │                                 │            │            │                                 │                   │                                                                                   │
│                  ▼                 │                                 │            ▼            │                                 │                   ▼                                                                                   │
│ ┌────────────────────────────────┐ │                                 │ ┌─────────────────────┐ │                                 │ ┌───────────────────────────────────┐                                                                 │
│ │                                │ │                                 │ │                     │ │                                 │ │                                   │                                                                 │
│ │                                │ │                                 │ │                     │ │                                 │ │                                   │                                                                 │
│ │      Identity Signing Key      │ │                                 │ │ Exchange Secret Key │ │                                 │ │                SMK                ├──────────────────────HKDF───────────────────────┐               │
│ │        (Ed25519 secret)        │ │                                 │ │       (X25519)      │ │                                 │ │       (Shredding Master Key)      │          info='Vauchi_FileKey_Key_v2'           │               │
│ │                                │ │                                 │ │                     │ │                                 │ │                                   │                                                 │               │
│ └────────────────┬───────────────┘ │                                 │ └──────────┬──────────┘ │                                 │ └─────────────────┬─────────────────┘                                                 │               │
│                  │                 │                                 │            │            │                                 │                   │                                                                   │               │
│                  │                 │                                 │            │            │                                 │                 HKDF                                                                  │               │
│                  │                 │                                 │            │            │                                 │     info='Vauchi_Storage_Key_v2'                                                      │               │
│                  │                 │                                 │            │            │                                 │                   │                                                                   │               │
│                  ▼                 │                                 │            ▼            │                                 │                   ▼                                                                   ▼               │
│ ┌────────────────────────────────┐ │                                 │ ┌─────────────────────┐ │                                 │ ┌───────────────────────────────────┐                                   ┌───────────────────────────┐ │
│ │                                │ │                                 │ │                     │ │                                 │ │                                   │                                   │                           │ │
│ │                                │ │                                 │ │                     │ │                                 │ │                                   │                                   │                           │ │
│ │      Identity Public Key       │ │                                 │ │ Exchange Public Key │ │                                ┌┄┄┤                SEK                │                                   │            FKEK           │ │
│ │        (Ed25519 public)        │ │                                 │ │       (X25519)      │ │                                ┆│ │      (Storage Encryption Key)     │                                   │ (File Key Encryption Key) │ │
│ │                                │ │                                 │ │                     │ │                                ┆│ │                                   │                                   │                           │ │
│ └────────────────────────────────┘ │                                 │ └─────────────────────┘ │                                ┆│ └─────────────────┬─────────────────┘                                   └───────────────────────────┘ │
│                                    │                                 │                         │                                ┆│                   ┆                                                                                   │
└────────────────────────────────────┘                                 └─────────────────────────┘                                ┆└───────────────────┆───────────────────────────────────────────────────────────────────────────────────┘
                                                                                                                                  ┆                encrypts                                                                                 
                                                                                                                                  ┆                    ┆                                                                                    
                   ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘                    ┆                                                                                    
┌──────────────encrypts─────────────────────────────────────────────────────────encrypts───────────────────────────────────────────────────────────────┆───────────────────┐                                                                
│                  ┆                                                         Per-Contact Keys                                                          ┆                   │                                                                
│                  ┆                                                                ┆                                                                  ┆                   │                                                                
│                  ▼                                                                ▼                                                                  ▼                   │                                                                
│ ┌────────────────────────────────┐                                     ┌─────────────────────┐                                     ┌───────────────────────────────────┐ │                                                                
│ │                                │                                     │                     │                                     │                                   │ │                                                                
│ │                                │                                     │                     │                                     │                                   │ │                                                                
│ │        CEK (Contact 1)         │                                     │   CEK (Contact 2)   │                                     │          CEK (Contact N)          │ │                                                                
│ │         random 256-bit         │                                     │    random 256-bit   │                                     │           random 256-bit          │ │                                                                
│ │                                │                                     │                     │                                     │                                   │ │                                                                
│ └────────────────────────────────┘                                     └─────────────────────┘                                     └───────────────────────────────────┘ │                                                                
│                                                                                                                                                                          │                                                                
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘                                                                

Key Derivation Details

HKDF Convention

All HKDF derivations use standard RFC 5869 (documented as "DP-5"):

HKDF-SHA256:
  - salt: None (zeros per RFC 5869 §2.2)
  - ikm: master_seed (32 bytes, high-entropy input)
  - info: domain string (e.g., "Vauchi_Exchange_Seed_v2")
  - output: 32 bytes

This follows standard HKDF convention: high-entropy seed as IKM, no salt needed.

Key Sizes

KeySizeAlgorithm
Master Seed256 bitsCSPRNG
Identity Signing32+64 bytesEd25519 (seed+keypair)
Exchange32 bytesX25519
SMK256 bitsHKDF-SHA256
SEK256 bitsHKDF-SHA256
FKEK256 bitsHKDF-SHA256
CEK256 bitsCSPRNG

Double Ratchet Key Hierarchy

┌────────────────────────────────┐                                                                
│ Initial Key Agreement (X3DH)   │                                                                
│                                │                                                                
│                                │                                                                
│ ┌────────────────────────────┐ │                                                                
│ │                            │ │                                                                
│ │                            │ │                                                                
│ │     X3DH Shared Secret     │ │                                                                
│ │         (32 bytes)         │ │                                                                
│ │                            │ │                                                                
│ └──────────────┬─────────────┘ │                                                                
│                │               │                                                                
└────────────────┼───────────────┘                                                                
               HKDF                                                                               
               init                                                                               
                 │                                                                                
┌────────────────┼───────────────────────────────────────────────────────────────────────────────┐
│                │                         Root Chain                                            │
│                │                                                                               │
│                ▼                                                                               │
│ ┌────────────────────────────┐                                                                 │
│ │                            │                                                                 │
│ │         Root Key 0         │                                                                 │
│ │                            │                                                                 │
│ └──────────────┬─────────────┘                                                                 │
│                │                                                                               │
│                │                                                                               │
│                │                                                                               │
│                │                                                                               │
│                │                                                                               │
│                │                                                                               │
│                │                                                                               │
│                │                                                                               │
│                ▼                                                                               │
│ ┌────────────────────────────┐                                                                 │
│ │                            │                                                                 │
│ │ DH(our_secret × their_pub) ├─────────────────┐                                               │
│ │                            │                 │                                               │
│ └──────────────┬─────────────┘                 │                                               │
│                │                             HKDF                                              │
│                │                               │                                               │
│              HKDF                              │                                               │
│                │                               │                                               │
│                │                               │                                               │
│                │                               │                                               │
│                │                               │                                               │
│                │                               │                                               │
│                ▼                               ▼                                               │
│ ┌────────────────────────────┐      ┌─────────────────────┐                                    │
│ │                            │      │                     │                                    │
│ │         Root Key 1         ├───┬──┤   Send Chain Key 0  │                                    │
│ │                            │   │  │                     │                                    │
│ └────────────────────────────┘   │  └──────────┬──────────┘                                    │
│                                  │             │                                               │
│                                  │           HKDF                                              │
│              HKDF────────────────┴──────CHAIN_KEY_INFO─────────────────────────┐               │
│        MESSAGE_KEY_INFO                        │                               │               │
│                ▼                               ▼                               ▼               │
│ ┌────────────────────────────┐      ┌─────────────────────┐     ┌────────────────────────────┐ │
│ │                            │      │                     │     │                            │ │
│ │       Message Key 0        │   ┌──┤   Send Chain Key 1  │  ┌──┤ DH(our_secret × their_pub) │ │
│ │                            │   │  │                     │  │  │                            │ │
│ └────────────────────────────┘   │  └─────────────────────┘  │  └──────────────┬─────────────┘ │
│                                  │                           │                 │               │
│                                  │                           │                 │               │
│                                  │                           │               HKDF              │
│                                  │                           │                 │               │
│                ┌─────────────────┘             ┌─────────────┘                 │               │
│              HKDF                            HKDF                              │               │
│                │                               │                               │               │
│                │                               │                               │               │
│                ▼                               ▼                               ▼               │
│ ┌────────────────────────────┐      ┌─────────────────────┐     ┌────────────────────────────┐ │
│ │                            │      │                     │     │                            │ │
│ │       Message Key 1        │      │      Root Key 2     ├──┬──┤      Recv Chain Key 0      │ │
│ │                            │      │                     │  │  │                            │ │
│ └────────────────────────────┘      └─────────────────────┘  │  └────────────────────────────┘ │
│                                                              │                                 │
│                                                              │                                 │
│              HKDF────────────────────────────HKDF────────────┴─────────────────┐               │
│        MESSAGE_KEY_INFO                 CHAIN_KEY_INFO                         │               │
│                ▼                               ▼                               ▼               │
│ ┌────────────────────────────┐      ┌─────────────────────┐     ┌────────────────────────────┐ │
│ │                            │      │                     │     │                            │ │
│ │       Message Key 0        │      │   Recv Chain Key 1  │     │         Root Key N         │ │
│ │                            │      │                     │     │                            │ │
│ └────────────────────────────┘      └──────────┬──────────┘     └────────────────────────────┘ │
│                                                │                                               │
└────────────────────────────────────────────────┼───────────────────────────────────────────────┘
                                                 │                                                
┌────────────────────────────────────────────────┼───────────────────────────────────────────────┐
│                                         Receive│Chain                                          │
│ ┌────────────────────────────┐                 │                                               │
│ │                            │                 │                                               │
│ │       Message Key 1        │◄─────HKDF───────┘                                               │
│ │                            │                                                                 │
│ └────────────────────────────┘                                                                 │
│                                                                                                │
└────────────────────────────────────────────────────────────────────────────────────────────────┘

Device Key Derivation

┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐                                                                                 
│                                                    Master Identity                                                    │                                                                                 
│                                                                                                                       │                                                                                 
│                                                                                                                       │                                                                                 
│ ┌────────────────────────────┐                                                                                        │                                                                                 
│ │                            │                                                                                        │                                                                                 
│ │        Master Seed         │                                                                                        │                                                                                 
│ │                            │                                                                                        │                                                                                 
│ └────────────────────────────┘                                                                                        │                                                                                 
│                │                                                                                                      │                                                                                 
│                │                                                                                                      │                                                                                 
│                ├────────────────────────────┬─────────────────────────────────────────────────┐                       │                                                                                 
│                │                            │                                                 │                       │                                                                                 
│                ▼                            ▼                                                 ▼                       │                                                                                 
│ ┌────────────────────────────┐     ┌────────────────┐                   ┌───────────────────────────────────────────┐ │                                                                                 
│ │                            │     │                │                   │                                           │ │                                                                                 
│ │                            │     │                │                   │                                           │ │                                                                                 
│ │       Device Index 0       ├──┐  │ Device Index 1 ├──┐                │               Device Index 2├─────┐       │ │                                                                                 
│ │         (Primary)          │  │  │                │  │                │                                   │       │ │                                                                                 
│ │                            │  │  │                │  │                │                                   │       │ │                                                                                 
│ └──────────────┬─────────────┘  │  └────────────────┘  │                └───────────────────────────────────┴───────┴─┼─────HKDF(seed,─device_index=2)────────────────┬──────────────────────┐          
│                │                │                      │                                                              │                                               │                      │          
└────────────────┼────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────┘                                               │                      │          
    HKDF(seed, device_index=0)    │                      │                                                                                                              │                      │          
                 │                │                      │                                                                                                              │                      │          
                 │                └───────────┐          └─────HKDF(seed,─device_index=1)───────┬──────────────────────┐                                                │                      │          
┌────────────────┼────────────────────────────┼─────────┐               ┌───────────────────────┼──────────────────────┼─────────┐                            ┌─────────┼──────────────────────┼─────────┐
│                │    Device 0 Keys           │         │               │                     Device 1 Keys            │         │                            │         │    Device 2 Keys     │         │
│                │                            │         │               │                       │                      │         │                            │         │                      │         │
│                ▼                            ▼         │               │                       ▼                      ▼         │                            │         ▼                      ▼         │
│ ┌────────────────────────────┐     ┌────────────────┐ │               │ ┌───────────────────────────────────┬───────┬────────┐ │                            │ ┌───────────────┐     ┌────────────────┐ │
│ │                            │     │                │ │               │ │                                   │       │        │ │                            │ │               │     │                │ │
│ │       Signing Key 0        │     │ Exchange Key 0 │ │               │ │               Signing Key 1       │ Exchange Key 1 │ │                            │ │ Signing Key 2 │     │ Exchange Key 2 │ │
│ │                            │     │                │ │               │ │                                   │       │        │ │                            │ │               │     │                │ │
│ └────────────────────────────┘     └────────────────┘ │               │ └───────────────────────────────────┴───────┴────────┘ │                            │ └───────────────┘     └────────────────┘ │
│                                                       │               │                                                        │                            │                                          │
└───────────────────────────────────────────────────────┘               └────────────────────────────────────────────────────────┘                            └──────────────────────────────────────────┘

Crypto-Shredding Paths

┌───────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                          Destruction Targets                                          │
│                                                                                                       │
│                                                                                                       │
│ ┌───────────────────────────────┐     ┌───────────────────────────┐     ┌───────────────────────────┐ │
│ │                               │     │                           │     │                           │ │
│ │          Destroy Seed         │     │        Destroy SMK        │     │        Destroy CEK        │ │
│ │                               │     │                           │     │                           │ │
│ └───────────────┬───────────────┘     └─────────────┬─────────────┘     └─────────────┬─────────────┘ │
│                 │                                   │                                 │               │
└─────────────────┼───────────────────────────────────┼─────────────────────────────────┼───────────────┘
    Complete identity destruction             Storage shredding               Per-contact shredding      
                  │                                   │                                 │                
                  │                                   │                                 │                
┌─────────────────┼───────────────────────────────────┼─────────────────────────────────┼───────────────┐
│                 │                              Effect                                 │               │
│                 │                                   │                                 │               │
│                 ▼                                   ▼                                 ▼               │
│ ┌───────────────────────────────┐     ┌───────────────────────────┐     ┌───────────────────────────┐ │
│ │                               │     │                           │     │                           │ │
│ │      All data unreadable      │     │ All local data unreadable │     │ Single contact unreadable │ │
│ │                               │     │                           │     │                           │ │
│ └───────────────────────────────┘     └───────────────────────────┘     └───────────────────────────┘ │
│                                                                                                       │
└───────────────────────────────────────────────────────────────────────────────────────────────────────┘

Key Storage Locations

┌───────────────────────┬────────────────────────────────────────┐                             ┌────────────────────────┐
│   Platform Keychain   │  Memory Only                           │                             │    SQLite Database     │
│                       │                                        │                             │                        │
│                       │                                        │                             │                        │
│ ┌───────────────────┐ │              ┌───────────────────────┐ │                             │ ┌────────────────────┐ │
│ │                   │ │              │                       │ │                             │ │                    │ │
│ │                   │ │              │                       │ │                             │ │                    │ │
│ │        SMK        ├derive─on─boot─►│ SEK (derived at boot) │ │     ├─────encrypt/decrypt───┼►│   Data encrypted   │ │
│ │    (encrypted)    │ │              │                       │ │                             │ │      with SEK      │ │
│ │                   │ │              │                       │ │                             │ │                    │ │
│ └───────────────────┘ │              └───────────┬───────────┘ │                             │ └────────────────────┘ │
│                       │                          │             │                             │                        │
├───────────────────────┘                          │             │                             │                        │
│                                                  │             │                             │                        │
│                                                  │             │                             │                        │
│                                                  │             │                             │                        │
│ ┌───────────────────┐                            │             │                             │ ┌────────────────────┐ │
│ │                   │                            │             │                             │ │                    │ │
│ │                   │                            │             │                             │ │                    │ │
│ │    Message keys   ├────────┐                   ├─────────────┼─encrypt/decrypt─────────────┼►│   CEK encrypted    │ │
│ │    (single use)   │        │                   │             │                             │ │      with SEK      │ │
│ │                   │        │                   │             │                             │ │                    │ │
│ └─delete─after─use──┘        │                   │             │                             │ └────────────────────┘ │
│           ▲                  │                   │             │                             │                        │
│           │                  │                   │             │                             │                        │
│           ├──────────────────┘                   │             │                             │                        │
│        derive                                    │             │                             │                        │
│           │                                      │             │                             │                        │
│ ┌─────────┴─────────┐                            │             │                             │ ┌────────────────────┐ │
│ │                   │                            │             │                             │ │                    │ │
│ │                   │                            │             │                             │ │                    │ │
│ │ Active chain keys │                            └─────────────┼─encrypt/decrypt─────────────┼►│   Ratchet state    │ │
│ │                   │                                          │                             │ │ encrypted with SEK │ │
│ │                   │                                          │                             │ │                    │ │
│ └───────────────────┘                                          │                             │ └────────────────────┘ │
│                                                                │                             │                        │
└────────────────────────────────────────────────────────────────┘                             └────────────────────────┘

Backup Key Derivation

┌────────────────────────────────────────────┐ ┌───────────────────────────────────────────────────────────────────────────────┐
│                User Input                  │ │                                Backup Contents                                │
│                                            │ │                                                                               │
│                                            │ │                                                                               │
│ ┌────────────────────┐     ┌─────────────┐ │ │ ┌──────────────┐     ┌─────────────┐     ┌──────────────┐     ┌─────────────┐ │
│ │                    │     │             │ │ │ │              │     │             │     │              │     │             │ │
│ │                    │     │             │ │ │ │              │     │             │     │              │     │             │ │
│ │      Password      │     │ Random Salt │ │ │ │ Display Name │     │ Master Seed │     │ Device Index │     │ Device Name │ │
│ │                    │     │  (16 bytes) │ │ │ │              │     │             │     │              │     │             │ │
│ │                    │     │             │ │ │ │              │     │             │     │              │     │             │ │
│ └──────────┬─────────┘     └──────┬──────┘ │ │ └───────┬──────┘     └──────┬──────┘     └───────┬──────┘     └──────┬──────┘ │
│            │                      │        │ │         │                   │                    │                   │        │
└────────────┼──────────────────────┼────────┘ └─────────┼───────────────────┼────────────────────┼───────────────────┼────────┘
             │                      │                    │                   │                    │                   │         
             │                      │                    │                   │                    │                   │         
             │                      │                    │                   │                    │                   │         
┌────────────┼───────────┐          │                    │                   │                    │                   │         
│    Key Derivation      │          │                    │                   │                    │                   │         
│            │           │          │                    │                   │                    │                   │         
│            ▼           │          │                    │                   │                    │                   │         
│ ┌────────────────────┐ │          │                    │                   │                    │                   │         
│ │                    │ │          │                    │                   │                    │                   │         
│ │                    │ │          │                    │                   │                    │                   │         
│ │      Argon2id      │◄┼──────────┘                    │                   │                    │                   │         
│ │  m=64MB, t=3, p=4  │ │                               │                   │                    │                   │         
│ │                    │ │                               │                   │                    │                   │         
│ └──────────┬─────────┘ │                               │                   │                    │                   │         
│            │           │                               │                   │                    │                   │         
└────────────┼───────────┘                               │                   │                    │                   │         
             │                                           │                   │                    │                   │         
             │                                           │                   │                    │                   │         
             │                                           │                   │                    │                   │         
┌────────────┼───────────┐                               │                   │                    │                   │         
│        Output          │                               │                   │                    │                   │         
│            │           │                               │                   │                    │                   │         
│            ▼           │                               │                   │                    │                   │         
│ ┌────────────────────┐ │                               │                   │                    │                   │         
│ │                    │ │                               │                   │                    │                   │         
│ │                    │ │                               │                   │                    │                   │         
│ │     Backup Key     │ │                       ┌───────┘            ┌──────┘            ┌───────┘            ┌──────┘         
│ │     (256 bits)     │ │                       │                    │                   │                    │                
│ │                    │ │                       │                    │                   │                    │                
│ └──────────┬─────────┘ │                       │                    │                   │                    │                
│            │           │                       │                    │                   │                    │                
│   XChaCha20-Poly1305   │                       │                    │                   │                    │                
│            │           │                       │                    │                   │                    │                
│            │           │                       │                    │                   │                    │                
│            ▼           │                       │                    │                   │                    │                
│ ┌────────────────────┐ │                       │                    │                   │                    │                
│ │                    │ │                       │                    │                   │                    │                
│ │  Encrypted Backup  │◄┼───────────────────────┴────────────────────┴───────────────────┴────────────────────┘                
│ │                    │ │                                                                                                      
│ └────────────────────┘ │                                                                                                      
│                        │                                                                                                      
└────────────────────────┘                                                                                                      

Security Properties by Key

KeyFwd SecrecyBreak-in Rec.Zeroized
Master SeedN/ANoYes
Identity SigningNoNoYes
Exchange KeyNoNoYes
SMKNoNoYes
SEKNoNoYes (mem)
CEKPer-contactN/AYes
Root KeyVia DH ratchetYesYes
Chain KeyVia sym ratchetN/AYes
Message KeySingle-useN/AYes