The short version
Everything you tuck in is sealed on your Mac before it goes anywhere. The sealed copies live in your own iCloud, in CloudKit’s private database. The key that opens them travels between your Macs in iCloud Keychain, which Apple encrypts end to end. Carriel has no server, and we never receive your things or your keys.
Without a pouch password, Carriel trusts your Apple Account the way Apple Passwords does: whoever can restore your iCloud Keychain can open your pouch. With a pouch password, they would also need the password, and each guess at it is slow on purpose.
Taking anything out asks for Touch ID, your Apple Watch or your Mac’s password. Putting things in never asks.
Who could get what
| Who | Without a pouch password | With one |
|---|---|---|
| Someone who restores your iCloud Keychain: your Apple Account plus the passcode of one of your devices | Everything in your pouch, as with Apple Passwords. | Nothing, unless they guess the password. Each guess costs a slow, memory-hard computation, so a long passphrase is out of reach. |
| Someone who phishes your Apple Account, without a device passcode | Sealed records, their sizes and times. They can delete what’s in iCloud, but your Macs keep their own sealed copies. iCloud Keychain won’t join their Mac, so they get no key. | The same. |
| Apple, or a legal request to Apple | Sealed records, their padded sizes, when they change, and which of your Macs signed them. No names and no values. | The same. |
| A thief with your Mac, locked | With FileVault on, nothing without your login password. Carriel’s copy on that Mac is sealed with keys that work only there. | The same. |
| Someone at your unlocked Mac | The names in your pouch. Taking anything out asks for Touch ID, your Apple Watch or your password. | The same. |
| Malware, or a misled AI agent, running as you | Whatever you copy, while it sits on the clipboard. Nothing else without passing a prompt it can’t answer. | The same. |
| Screen sharing and recording | Names. A value only while you show it, and only to some recorders. | The same. |
| Someone who swaps the download, or changes an update on its way | Nothing. macOS won’t open an app whose signature or notarization doesn’t check, Carriel refuses an update that isn’t signed with LonelyDuck’s release key, and /download/ lists the SHA-256 to compare. | The same. |
| Someone with LonelyDuck’s release key and Developer ID | Everything, as with any app’s maker. The release key lives in the Secure Enclave of one Mac and asks for Touch ID for every signature. | Everything. |
| LonelyDuck, or a legal request to LonelyDuck | With a free pouch, nothing: it never talks to LonelyDuck. With the bigger pouch, the email address you bought it with and the Macs that use your license, by model (like “MacBook Air · M2”, never their names) and when each last checked. Never anything in your pouch. | The same. |
| Someone who copies your license | The bigger pouch on Macs of their own, while your license has places free. Nothing in your pouch: a license opens no thing. Macs… shows every Mac that uses it, and Release takes one off. | The same. |
One rule follows from the first row. If your pouch held your Apple Account’s own recovery key, the account that opens the pouch would be the account that key recovers. So Carriel refuses to tuck one in until you’ve added a pouch password, and says so: “Add a pouch password first, or keep this key on paper.”
The keys
In plain words: a root key in iCloud Keychain, an optional password mixed into it, and from those one pouch key with two halves, one that opens names and one that opens values. In detail:
| Key | What it is | Where it lives |
|---|---|---|
| R, the root | 256 random bits. | iCloud Keychain, in Carriel’s own access group, readable once your Mac has been unlocked after starting. |
| P, the pouch password | Optional. Stretched with Argon2id: 256 MiB of memory, 3 passes, 4 lanes, a 128-bit salt. Carriel suggests a passphrase of six random words from the BIP-39 list of 2,048 words, 66 bits, and you can type your own. | Typed only in Carriel’s own native password field. Never stored. |
| KEK | HKDF-SHA256 over R and the stretched password (or R alone without one), salted with your pouch’s id, labelled carriel/pouch/kek/v1|pw or carriel/pouch/kek/v1|nopw. | Derived when needed. Never stored. |
| G, the pouch key | Random. Wrapped under the KEK, under each of your Macs’ Secure Enclave keys, and under your recovery key if you make one. | The wraps sit in your iCloud. Each Mac keeps its own wrap, which its Secure Enclave opens only after you answer Touch ID or your password. |
| The names key | HKDF over G, labelled carriel/pouch/names/v1. | Kept on each Mac, so the list, search and sync never ask. |
| The values key | HKDF over G, labelled carriel/pouch/values/v1: a private key whose public half is shared. | Opened only for a take-out, after you answer. |
A thing in iCloud is one record with a random name. Inside it, the value is sealed with HPKE (RFC 9180) to the values key’s public half, and that, with the thing’s name and facts, is sealed again under the names key, padded to a size bucket and signed by the Secure Enclave key of the Mac that wrote it. A fresh key seals every revision. Because sealing needs only the public half, tucking in never asks, even while Carriel is locked.
Where Touch ID is enforced. With a pouch password, the values key can be reached on a Mac only through that Mac’s Secure Enclave, which asks for you: the gate is cryptographic. Without one, Carriel’s own signed code could derive the values key from R, so the gate is Carriel’s policy, held by its code signature and the hardened runtime that keep other code out.
A new Mac gets R from iCloud Keychain. Without a pouch password it opens the pouch at once; with one, you type it once on that Mac, or use your recovery key. Each Mac then keeps its own Secure Enclave wrap and doesn’t need the password again unless the pouch key is replaced.
A new pouch key comes when you turn on or change the pouch password, replace the recovery key or remove a Mac. Everything in your pouch is then sealed again under it, so an older copy of the keyring opens nothing you keep now. A copy of the old records kept elsewhere, such as a backup, stays as safe as your iCloud Keychain was when it was made.
Removing a Mac makes a new pouch key that the removed Mac doesn’t get. While that Mac is still on your Apple Account it can still read iCloud Keychain, so also remove it there, and change the pouch password if it was stolen unlocked. A recovery key made on that Mac stops working; make a new one.
What Apple sees
In your account’s CloudKit private database: how many records there are, their padded sizes, when they change, and which of your Macs signed each one. Record names are random, so they say nothing about what’s inside. Names, kinds, categories, lengths, file names and values are all inside the sealed envelope.
Your pouch doesn’t depend on Apple’s CloudKit encryption: it is sealed before CloudKit sees it. If you turn on Advanced Data Protection, CloudKit’s own layer becomes end to end too. Either way, you trust Apple for iCloud Keychain, the Secure Enclave and macOS itself, and we’d rather say so than hide it.
Nothing else leaves this Mac. Carriel sends no usage data, no analytics and no crash reports of its own, and has no server of its own that learns what you keep in it. What leaves this Mac lists every service Carriel can reach.
When Carriel asks
| What you do | Asks for Touch ID, your Watch or your password |
|---|---|
| Open the panel or the window, search, tuck in | Never. |
| Copy, show, edit a value, save a copy, preview a file | Yes. The prompt names the action and the thing: “copy Stripe live secret”. |
| Delete | No: it waits 30 days in Recently Deleted. Deleting for good, and emptying Recently Deleted, ask. |
| Make a setting weaker, add or change the pouch password, make a recovery key, remove a Mac | Yes. Making a setting stronger never asks. |
Thirty seconds, not more. One answer covers the next 30 seconds after your last take-out while the panel or the window stays open. Closing the panel, the window losing focus, sleep, screen lock, switching users and the screen saver end it at once. Unlocking your Mac with Touch ID never counts as an answer.
Two lock modes. Settings › Security offers With your Mac, the default, which works as described here: your pouch locks when your Mac does, and one answer covers 30 seconds. Every time asks for every copy and every Show, however soon after the last one.
When it locks. On sleep, screen lock, a user switch and quit, Carriel forgets every answer and every opened key, closes any panel showing or editing a value, deletes preview files, and clears its copy from the clipboard if it’s still there. The names key stays, so your list is there after you unlock, without a prompt.
Where a value is in the clear
- In Carriel’s native part, briefly, in memory buffers it zeroes after use where Swift allows.
- In a text field while you edit a note or a value. AppKit’s text storage can’t be zeroed, so Carriel lets it go when the panel closes. Spelling, completion, inline prediction and Writing Tools are off in those fields.
- On the clipboard, from your copy until it clears. Any app running as you can read it there.
- On screen, while you show it.
- In a file you save, and in a temporary file Quick Look reads while you preview one, deleted when the preview closes.
Copies, showing and files
- A copy is for this Mac only (
currentHostOnly), so Universal Clipboard doesn’t hand it to your other devices. It’s markedorg.nspasteboard.ConcealedTypeandTransientType, so clipboard managers and clipboard history that follow the convention skip it. It clears after 30 seconds (15, 30, 60 or 90 in Settings), and on lock, sleep, screen lock and quit, but only if the clipboard still holds Carriel’s copy. - Showing a value opens a small native panel: monospaced, in groups of four, with 0 and O told apart. Carriel asks macOS to keep it out of screenshots and screen sharing (
sharingType = .none), which some recorders ignore. It closes after 10 seconds, when you let go, or when it loses focus. - A file is read once when you drop it: regular files only, no links, its size checked first. Carriel never opens, parses or thumbnails it. Saving a copy writes it through the save panel at 0600, marked as downloaded so Gatekeeper checks anything you open later. Quick Look previews it outside Carriel.
The window never holds a value
Carriel has two parts. A small native part, written in Swift, is always on: the menu bar, ⌃⌥⌘V, the card and the panel, Touch ID, the keys and iCloud. The window is a separate app, built with Electron, that opens when you ask and quits when you close it.
The window talks to the native part over a local connection that checks the window is Carriel’s own signed app. It receives names, kinds, lengths and facts, never a value, a note’s text, a file’s bytes or a password. When you ask it to show a value, the native part shows it in its own panel. The window refuses file drops; “Add a file…” opens the native file panel instead. Carriel’s tests put a marker in every test value and fail if one ever reaches the window, a log or iCloud outside the sealed envelope.
Downloads, updates and builds
Carriel comes from carriel.app, signed with LonelyDuck Software’s Developer ID and notarized by Apple, and so does every update. To check the copy you have, run codesign -dv /Applications/Carriel.app in Terminal: it shows TeamIdentifier=RD9TC9MV83, LonelyDuck Software. spctl -a -vv /Applications/Carriel.app says source=Notarized Developer ID. /download/ lists each release’s SHA-256.
Updates come from an update description on dl.carriel.dev that LonelyDuck signs with its release key (ECDSA P-256). That key lives in the Secure Enclave of the Mac Carriel is released from, asks for Touch ID for every signature, and has a backup kept offline. Before Carriel installs an update it checks that signature, the archive’s SHA-256, that the new app is signed by team RD9TC9MV83, Apple’s notarization, and that it is newer than any version this Mac has seen, so nobody can send an older one back. If the new version doesn’t start, Carriel puts the old one back.
The sandbox. Carriel’s menu bar part, which holds every value, runs in the App Sandbox with the hardened runtime and has no network access of its own. The window and Carriel Updater run with the hardened runtime outside the sandbox: the window holds no value and talks only to the menu bar part, and the updater must be able to replace the app. The updater talks to two hosts, both built in: dl.carriel.dev, and api.lonelyduck.io for the license.
No crash reporter and no analytics. If Carriel crashes, macOS keeps the report on your Mac (Console › Crash Reports), and it reaches us only if you send it.
The bigger pouch’s license
A license is a line of text from LonelyDuck, signed with Ed25519 (RFC 8032) by LonelyDuck’s key, which Carriel has built in. Carriel checks that signature on your Mac before it uses a license, and refuses one for another app or one that was changed.
- It is handled like a value. It’s kept in iCloud Keychain, in Carriel’s own access group, so your other Macs get it, and it never reaches the window, a log, History or a notification. The window only learns how many of your 10 places are taken, when a Mac’s check runs out, and in Macs… each Mac’s model and last check.
- Each Mac checks its own place about twice a week, with a key of its own (Ed25519, kept in this Mac’s Keychain only), and keeps a 30-day lease sealed with the rest of its state. The menu bar part builds and checks every request and answer; Carriel Updater only carries them to api.lonelyduck.io.
- A lease that runs out, or a signed refusal (a refund, a released Mac), only stops that Mac adding past 25. No thing is removed or hidden, and every feature stays.
The record format
Everything below is built from published standards: AES-256-GCM (NIST SP 800-38D), HKDF-SHA256 (RFC 5869), HMAC-SHA256, ECDSA and ECDH on P-256 (FIPS 186-4, SEC 1), HPKE (RFC 9180), and Argon2id (RFC 9106) with BLAKE2b inside it (RFC 7693). Apple’s CryptoKit and Security framework supply all of them but Argon2id and BLAKE2b, which Carriel computes in Swift and checks against the RFCs’ own test vectors. The format is how Carriel arranges these, not an algorithm of its own. This is version 2; integers are big-endian.
A record (a thing’s head, its body, a Mac) is one envelope:
| Bytes | What |
|---|---|
| 4 | CRL2 |
| 4 + n | The header’s length, then the header: canonical JSON with sorted keys, in the clear. It names the record’s type and random name, its revision and time, the Mac that wrote it, its size bucket and, for a file in parts, which part. |
| 4 + n | The wraps’ length, then the wraps: a fresh 32-byte content key, sealed with AES-256-GCM under the names key of the pouch key’s generation, with carriel/wrap/v2|, the header and the generation as associated data. |
| 12 | The content’s nonce. |
| 4 + n | The content, sealed with AES-256-GCM under the content key, with CRL2 and the header as associated data. Before sealing it is padded: a 4-byte length, the content, then zeros to the smallest size that fits of 2,048, 8,192, 32,768, 131,072, 524,288 or 716,800 bytes (the format also knows 512, which the reference vector uses). |
| 2 + n | The signature: ECDSA P-256 with SHA-256 over every byte before it, DER-encoded, by the Secure Enclave key of the Mac that wrote it. |
Inside. A thing’s head (i- and 26 random base32 letters) holds PCH1 and JSON of its name and facts. Its body (b-…, in up to 16 parts for a file) holds PCB1 and the value sealed again with HPKE base mode (DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-256-GCM) to the values key’s public half, with the info carriel/pouch/value/v1 and the pouch, the thing, the record and the value’s generation as associated data. A Mac (d- and its id) holds PCD1 and its name and dates. Every revision has a new content key and nonce, and a file’s parts must share one revision and all carry their own signature before any opens.
The keyring (d-keyring) has its own shape: CPK1, then canonical JSON with the pouch’s id, the key’s generation, the revision and the Mac that wrote it in the clear, an HMAC-SHA256 over it, and an ECDSA P-256 signature. Inside, sealed with a key from R: G under the KEK, G under each Mac’s Secure Enclave key, every earlier G under a key from the newest one, and the values key’s public half. In the signed part beside it: R under a key from G, and G under the recovery key’s public half if you made one. A wrap to a public key is ephemeral ECDH on P-256, HKDF-SHA256 with both public keys as the salt and carriel/ec-wrap/v2 as the info, then AES-256-GCM bound to its purpose, such as carriel/pouch/device-wrap/v1| with the pouch, the generation and the Mac.
| Key | How it’s made |
|---|---|
| The password key | Argon2id version 19 over the password (UTF-8, NFC): 256 MiB, 3 passes, 4 lanes, a 16-byte random salt, 32 bytes out. |
| KEK | HKDF-SHA256 over R and the password key (R alone without a password), salt the pouch’s id, info carriel/pouch/kek/v1|pw or |nopw. It seals G with the pouch’s id, the generation and the mode (and the Argon2id settings) as associated data. |
| The names key | HKDF-SHA256 over G, salt carriel/pouch/v1, info carriel/pouch/names/v1. |
| The values key | 48 bytes of HKDF-SHA256 over G, salt carriel/pouch/v1, info carriel/pouch/values/v1, made a P-256 private key as FIPS 186-4 B.4.1 does: (x mod (n − 1)) + 1. |
| The keyring’s keys | Its seal: HKDF over R, salt the pouch’s id, carriel/pouch/keyring/v1. Its HMAC: HKDF over the names key, carriel/pouch/keyring-mac/v1. R’s wrap and the earlier keys’: HKDF over G, carriel/pouch/root-wrap/v1 and carriel/pouch/prev-wrap/v1. |
| The recovery key | 32 random bytes, written as BIP-39’s 24 English words (with an 8-bit SHA-256 checksum). Its key pair: 48 bytes of HKDF-SHA256 over it, salt carriel/pouch/recovery/v1, info p256-key, made a private key the same way. |
What stays in the clear, for CloudKit and anyone with your iCloud data: for each record, its type, random name, revision, time, size bucket, part count and the id of the Mac that wrote it; for the keyring, the generation, its revision and whether a recovery key exists. Names, kinds, categories, lengths, file names, notes and values are all sealed.
Test vectors
Fixed, made-up inputs, so anyone can check the format: none is a real key, password or thing anyone keeps. The whole set, with every wrap, the keyring, a head and a sealed value, is vectors.json. The site’s tests open every one with Node’s own crypto and Carriel’s TypeScript reference on each build, and Carriel’s Swift tests open the reference envelope and check the RFC vectors.
| From | Value |
|---|---|
| G, the pouch key | 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f |
| R, the root | 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f |
| The pouch’s id | 00000000-0000-4000-8000-000000000001 |
| The names key | e568e1d85e017a3cec73a6b9b49de39953fdddad8df650cbec79249a7d15661a |
| The values key (private) | bdb272a358217d6d0067841fb9ac59009eea9a462784231bfc5e32420b8625c3 |
| The values key (public) | 04d4ac45e0eb56e90bddbbf1848056e545bf6839c64438cb697505272dc43d2a98f42c111c98eb7b06ca139d68c6ab21771d9d7b951335d85130b50c15f7d30e38 |
| KEK, no password | 27347e11379338fd8745c7283d4be1222a8ef3be90a463e3920f626cfbdd23ec |
The password key for “correct horse battery staple”, salt 02 × 16 | 8884716d371c945e36d6b83ce6497917697e41f92ec7595f6e42988661e860b3 |
| KEK, with that password | 0b866a3554a86e9d6d8be9bb39ff0d1c3a562ca20e950c58a2427c91d60174e2 |
| The keyring’s seal key | 52ee74c6452fce35f760e45084219782166eafd6a323bc7c112e6d0bc2466313 |
The recovery key 00 × 32, as words | abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art |
| Its private key | f80408fd5bd01aeecdc81d5598337bb2e7928cc3355023b2fa2b5c476043c430 |
| SHA-256 of the reference envelope’s signed bytes | a43dabb1ec867d3315826564cb9bef5d28809245a07fa2a7f2d73cd7aabf1338 |
| SHA-256 of a head’s signed bytes | 4f9a2e38bde3959d79b4d5ce6282ca54c143158e7aa95e12222e12e5ae73e90d |
| SHA-256 of the keyring’s signed part | a64e62e98ed29aa96d3d94cbfce6b0d827429f4eb8fac54359b72584ac908f0b |
| A sealed value opens to | “carriel test vector value, not a secret” |
A signature is ECDSA, so it differs every time; the vectors give the signing key’s public half to check it with. A value’s HPKE seal draws a fresh key each time too, so its vector is one sealed value and what it opens to.
What Carriel can’t protect
- Your Apple Account, without a pouch password. Whoever can restore your iCloud Keychain can open your pouch, as with Apple Passwords. Turn on Stolen Device Protection on your iPhone, and consider security keys for your Apple Account.
- A new Mac’s first pouch. A Mac joining your pouch trusts the first one it finds that opens with your iCloud Keychain. Someone with both your iCloud Keychain and your iCloud data could show it a pouch of their own before your other Macs put yours back. Macs already in your pouch never take one.
- A forgotten pouch password with no Mac that still opens your pouch and no recovery key. Then nobody can open it, us included.
- The clipboard. Any app running as you can read a copy until it clears.
- Screen recording. Some recorders capture the panel that shows a value.
- Your Mac’s login password. Someone who knows it can answer the prompt that Touch ID falls back to.
- The copy you started from. A key you copied from Notes or a chat is still there after you tuck it in. Carriel tells you where it came from, so you can delete it.
- A leaked key. Carriel can’t revoke a key at the service that issued it. If one may have leaked, replace it there.
Not done yet
- An independent review. This design hasn’t been reviewed by anyone outside LonelyDuck. When it has, the review’s summary goes on this page, unaltered.
Report a problem
Write to support@lonelyduck.io, or see security.txt. We read every report and answer in English or Korean. Please never send a real key, password or code: we don’t need one to understand the problem.
Changes to this page
- Version 2 · 24 October 2026. Carriel comes from carriel.app, not the Mac App Store: signing, notarization, updates, the license and crash reports say so. The record format and its test vectors are published.
- Version 1 · 24 October 2026. First edition, for Carriel 1.0.
