For the curious
Technicalities
What Peers is made of, how a message gets from one phone to another, and what each part of the way can and cannot see. Written from the code.
No server in the middle.
Phones talk to each other directly when their networks allow it. When they don’t, a relay passes the sealed packets along. A sealed copy also waits in a mailbox for phones that are off. Peers has no server of its own that holds anything.
The parts
The relay, the mailbox and the call relay (STUN/TURN) are one ird p2p app, reached over wss://. Phones find each other there by a room id derived from the chat’s secret, then open WebRTC data channels where they can.
The mailbox keeps every sealed packet for 72 hours, read or not. Messages are trimmed to about 40 KB before sealing, so photos and files never rest there: a phone that comes back later asks an online member for the file and checks it against its SHA-256.
The relay is a setting in the app. The defaults point at ird’s network.
Signed once. Sealed twice.
Everything a chat is made of, messages, reactions, edits, polls and calls, is signed by whoever wrote it and sealed twice before it leaves the phone. The relay and the mailbox only ever hold the outer seal, and inside it is another one.
The inner seal, in detail
Sender keys. Each device keeps a hash ratchet per chat (HMAC-SHA256) and seals each packet under a one-time AES-256-GCM key, bound to the chat, the sender and the index. Used keys are deleted.
A chain reaches each member’s device sealed to that device alone: X25519, ephemeral-static plus static-static, and hybrid with X-Wing when the device has one. Chains start over after 2,000 messages, after a week, when someone joins a chat that has history, and when someone leaves or is removed.
Signatures cover a canonical form of each packet, so history replayed from the mailbox is checked against its author too. Files travel under a key of their own, carried inside the sealed message, and are checked against their SHA-256.
People are keys.
There is no account to look up. Your identity is a key pair made on your phone, and a contact is someone’s public key with a nickname you gave it. No phone number, no email, no directory.
Four emoji are the fingerprint of a key. Compare them with a friend, or hold two iPhones back to back and let them check over ultra-wideband. Until you do, the first key you see for someone is the one Peers holds them to, and a different key shows up as a different person.
More than one device
A Mac or a second phone gets the account in a one-time room, from a sixteen-character code, sealed to a key the new device made for that transfer. Both screens show four emoji for that key, so a stranger answering the code would show. Each device keeps its own agreement key, which never leaves it.
| Key | Kind | Lives |
|---|---|---|
| Identity | Ed25519 and ML-DSA-65 | Your devices |
| Device | X25519 and X-Wing | That device only |
| Chat | Room secret | Members’ devices |
| Message | AES-256-GCM, one use | Gone once used |
| On disk | AES-GCM | Keychain, this device |
A link opens a door, not the chat.
An invite link never contains a chat’s key. It points at a door. The newcomer knocks, the inviter’s phone answers, and the key travels sealed to the newcomer’s device. Someone else holding the link sees that a note was left, not whose.
Rules at the door
The link carries the inviter’s key and their device’s key, signed with the expiry, so an edited link stops working. A private chat’s link lets in the first person who knocks and then closes; a later knock is told the place was taken, which is how an intercepted link shows. A group’s link can be set to ask first. Every link ends after a week, and a new link closes the old door.
Whoever made a group can remove people and name others who can. A removal moves the group to a new room whose key is sealed to everyone who stays. The person removed keeps what they already had and is left with a room nobody is in.
Waking a locked phone.
A locked iPhone runs no apps, and only a push from Apple wakes it, sent with the key of whoever published the app. So there is one small program in the picture: Peers Helper, a menu-bar app for Mac that turns sealed wake requests into pushes. OPUS runs the one the App Store build uses.
It sees
- The number Apple uses for a phone
- A time
- A call or a message
It never sees
- Who asked
- Which chat
- What was said
How a request is checked
Your phone never hands its push token to the people you chat with. Each chat gets a ticket instead: the token sealed by your phone to the helper, valid 30 days. It is withdrawn when someone leaves or is removed, when you delete the chat, or when you block the person.
A request is signed by a throwaway key and sealed to the helper, with the time sealed inside. The helper ignores copies it has seen, rings older than a minute, messages older than an hour, and withdrawn tickets. It sends at most two rings in ten seconds and one message alert in thirty to a phone, and holds message alerts back by a random 5 to 45 seconds. The alert carries no words of yours; a call’s details are sealed to the phone and checked against the caller’s key before it rings.
It writes down two things: where it stopped reading, and which tickets were withdrawn, until they would have run out. Once a minute it signs a heartbeat that the app checks under Settings › Advanced.
If it is not running, nothing is lost: messages wait in the mailbox until the phone next opens Peers. Calls to a sleeping phone do not ring.
Who learns what.
Every party that touches Peers, what it can make out, and what it never sees. Written from the code; if the two ever disagree, the code is right and this is a bug.
| Party | Learns | Never |
|---|---|---|
| The relayrun on the ird network | A random chat number, when a device connected, its internet address, the size of each sealed packet | Your name, who you write to, a word of it |
| The mailboxpart of the relay | The chat number, a sealed copy of each message of about 40 KB at most, when it was dropped in; kept 3 days | Who dropped it in, who it is for, what it says |
| The call relayTURN, only when no direct path exists | That an encrypted stream is passing, its size and timing | A word or a frame of the call |
| The helperrun by OPUS | The number Apple uses for a phone, a time, a call or a message | Who asked, which chat, what was said |
| Apple’s push service | That a push went to a phone at a time | What it says: the body is sealed for that phone |
| Your network provider | That your device talks to the relay, and how much | Anything inside |
| Strangers nearby | That a device announces a Peers service under a random name, and the sizes of sealed frames | Who you are, who you talk to, what crosses |
| Someone holding an invite link | That notes were left at that door, when, and how big; whether their own knock was let in | Who else knocked, the chat’s key, its members, anything said |
| A site you share a link fromoff with Settings › Link cards in posts | One visit from your internet address when your phone makes the preview | Who you shared it with; their phones never contact it |
| OPUS | Only what the helper learns, while OPUS runs it | Who you are, who you talk to, what you said, that you exist |
Only if you turn it on
| Apple PasswordsSettings › Account, off by default | That a sealed item and a passkey for peers.opus.ro exist in your iCloud Keychain, its size, when it changes; on install, that a Peers build was installed on some device | What is inside, or which identity is yours |
|---|---|---|
| Apple Intelligence | A draft you ask the writing tools to work on, on the phone or Apple’s private compute, per your Apple settings | Anything else; Peers sends drafts nowhere itself |
| Wi-Fi Aware pairing | iOS keeps the pairing; phones nearby can tell a Peers service is around | Who you are; the same signed handshake decides who is on the other end |
Local, and nothing more
These run on your devices and change nothing above: the quantum-safe second lock, holding phones together to verify, Siri, Shortcuts and Spotlight, widgets, Live Activities and Control Center (drawn from a small note of counts and titles; no push token is requested), voice-note transcripts, the Mac menu bar, haptics and tips.
The two rules, and what changed when
No feature may hand any party something it does not already receive, unless you turn it on knowingly and this list says exactly what changes. And no feature may ask you to understand keys, pairing codes or network modes to benefit from it.
3 October 2026. Added the site a link is shared from: with link cards on, the phone that posts a link opens the page once, and the friends who see it contact nobody. The helper: the people in your chats no longer receive the number Apple uses for your phone; each chat gets a sealed ticket only the helper can open, withdrawn when someone leaves or is removed. The helper keeps withdrawn tickets until they would have run out and ignores a request it has already seen.
2 October 2026. Groups: removing someone moves the group to a new room, so the relay sees the phones that stay turn up under a new room number, and nothing about who or why. Invitations: a link no longer holds a chat’s key; someone holding one learns only that notes were left at its door. The mailbox keeps each sealed copy for three days, not one, and a copy is about 40 KB before sealing.
11 September 2026. The mailbox keeps a sealed copy of every message, not only for phones that are off, and collecting one does not remove it.
10 September 2026. Added Apple Passwords, Apple Intelligence and Wi-Fi Aware pairing as opt-in rows, and the local-only list; noted the association file fetched on install.
9 September 2026. First version: the relay, the mailbox, the doorbell, Apple’s push service, the Keychain, strangers nearby, your network provider.
At a glance.
| Signatures | Ed25519 on everything a chat keeps; ML-DSA-65 as well on member cards |
|---|---|
| Key agreement | X25519, hybrid with X-Wing (ML-KEM-768 and X25519) wherever a key is handed over |
| Room seal | AES-GCM, key from the room secret by HKDF-SHA256 (ird p2p SDK) |
| Message seal | Sender keys: HMAC-SHA256 chain, AES-256-GCM per message |
| Files | AES-256-GCM under their own key, checked by SHA-256 |
| Transport | WebRTC data channels, or wss:// through the relay |
| Calls | WebRTC, DTLS-SRTP; signalling sealed inside the chat; TURN when no direct path exists |
| Nearby | Bonjour, AWDL and Wi-Fi Aware; a signed handshake, frames sealed under the chat’s key |
| On the device | Encrypted store, key in the Keychain, never synced; no analytics, no crash reporting |
| Built with | Swift and CryptoKit, for iOS 26 and macOS 26; GPLv3 |
The rest, folded.
Open what you are curious about.
Nearby, without internet
Bonjour over the local network and Apple’s peer-to-peer Wi-Fi (AWDL), and on iPhone Wi-Fi Aware between phones paired once through Apple’s sheet, published only while a pairing exists. A pairing is transport trust only: the signed handshake with your identity key decides who is on the other end, and every frame is sealed under a key derived from the chat’s secret. When a device is reachable two ways, the better carrier wins.
Calls
WebRTC with DTLS-SRTP, a full mesh in groups, signalling sent as sealed packets inside the chat. On iPhone, LiveCommunicationKit puts calls on the lock screen and in Recents under an opaque handle derived from the chat, never a number; PushKit wakes the phone. The caller’s signature is checked against your contacts before anything rings. On the Mac, calls ring inside the app.
The second lock
Every device has an X-Wing key (ML-KEM-768 with X25519) next to its X25519 key. Chain hand-offs, wake payloads and device transfers are sealed hybrid: HPKE with XWingMLKEM768X25519_SHA256_AES_GCM_256 for anonymous envelopes, and an X-Wing encapsulation combined with the static X25519 secret in one HKDF for pairwise ones, so the sender stays authenticated. Each identity also derives an ML-DSA-65 key; member cards carry both signatures, and a contact’s fingerprint covers both keys once theirs verifies, which is when the shield appears. Packets stay Ed25519-signed, since an ML-DSA signature is 3.3 KB. About 1.1 KB more per envelope.
Siri, widgets and the Dynamic Island
App Intents for send, call, ding, open and nearby, with contacts and chats indexed on the device. Widgets, Live Activities and Control Center controls are drawn from a small snapshot in the app group: counts, titles, whether nearby is on. The extension never opens the encrypted store, no push token is requested for an activity, and nothing runs while the app is locked. Voice notes transcribe on the device with the system’s speech model, and the transcript is never sent.
The vault in Apple Passwords
Off by default. A passkey for peers.opus.ro is made inside the app, with no server: a random challenge and a hash of the identity key as the user handle. Its PRF extension derives a secret inside the authenticator that wraps the account (identity, contacts, chats, room secrets, settings; never messages) with AES-GCM, stored as a synchronizable Keychain item, so it travels through iCloud Keychain end to end encrypted. Restoring is one Face ID. Turning it off deletes the item; the passkey stays until you remove it in Apple Passwords.
Where it stops.
Every design draws a line somewhere. These are Peers’.
- The relay sees traffic, not words. Your internet address, when you connect, a random room number, and the size of each sealed packet. Peers hides what you say, not that you use it.
- Friends you connect to directly see your internet address, as on any call.
- Your phone is your account. Whoever has it unlocked, or a copy of its keys, has your chats. The app lock adds Face ID or a PIN.
- Disappearing messages disappear from devices, not from memory. A screenshot or a second camera still works, and Peers does not detect either. Sealed copies stay in the mailbox for three days, with no key left on any device that read them.
- Removing someone does not unsend. What they already received stays with them.
- An unverified contact is taken on first sight. Comparing emoji or holding phones together is what settles who is who.
- No independent security audit yet. The source is there to read in the meantime.
Every part OPUS runs can be swapped.
The relay
A setting in the app. Point it at any ird p2p app, including your own.
The helper
Runs on any Mac left switched on. Its keys are a setting too.
The app
Build it under your own Apple account. It still talks to everyone on the App Store build.
One Apple rule
Apple ties pushes to the account that published the app. Waking a phone on the App Store build needs OPUS’s Apple key, so that one piece stays with OPUS. Phones on your own build are woken with your own key.
