Tendril never writes to your address book. Everything it adds is stored in one JSON file on your Mac. You merge duplicates and add new cards in Contacts.app yourself, on purpose.
What it does
Install
Requires macOS 26 or later on Apple silicon, and a Swift 6.2 toolchain.build.sh compiles a release build, assembles and signs the bundle, and copies it to /Applications if Tendril is already installed there. Pass INSTALL=1 to install it the first time.
First launch
The first launch shows the macOS Contacts permission prompt. If you dismiss it, turn access back on under System Settings › Privacy & Security › Contacts, then reopen Tendril. To confirm that your contacts loaded, check Tendril’s log:access granted=true status=3, then loaded N contacts.
Two macOS problems the build already handles
Two macOS problems the build already handles
Contacts access needs an entitlement, not just a usage string. The app is signed with the hardened runtime, which only allows Contacts access with
com.apple.security.personal-information.addressbook. Without that entitlement, CNContactStore.requestAccess throws CNError 100 and the permission prompt never appears, so the app looks like it has no contacts.SwiftPM apps don’t bring their own window to the front. When launched with open, the window gets created but never shows. AppDelegate activates the app and orders the window front explicitly. Tendril remembers where you put the window, and only positions it itself on first run or when its saved display is no longer connected.The screens
All Contacts
The address book, searchable and sortable. Selecting a contact shows its interactions, details, online profiles, groups, reminders, notes, connections and enrichment suggestions.
Unsaved
Correspondents with no card, busiest first. You can filter by at least 1, 3, 10 or 25 interactions, and dismiss people you don’t want to see.
Duplicates
Clusters of cards that share a name, email or phone. Clusters that share an email or phone count as high confidence and are listed first.
Reminders
Every open reminder across all contacts, soonest first.
Connections
A force-directed graph of your contacts. Links you drew are solid, and inferred ones are dashed.
Sources
Connect mail, Google Calendar, iMessage and Beeper, check each source’s status, and rebuild the interaction index.
Storage
All of Tendril’s own data is in one file:What leaves your Mac
Tendril builds its index locally and keeps the results on your Mac. It only makes network requests to services you connect, plus Gravatar when you run enrichment:Limits
- Contacts can’t be merged in the app. Merging would need
CNSaveRequestand write access to Contacts, which Tendril doesn’t request. Use Contacts.app. - Accepted enrichment values stay in Tendril. They are not written back to the contact card.
- “Introduced by” can’t be saved on a link yet. A link stores two contacts and a kind, so converting an inferred introduction into a link loses who made it.
- The store rewrites the whole file on every change. That’s fine at the current size. Once notes get long, it should move to SQLite or SwiftData.
- There’s no DMG build yet. For now you build from source.
Next steps
Sources
Connecting mail, calendar, iMessage and Beeper, and how each one is counted.
Connections
How Tendril infers who knows whom, and what the confidence levels mean.
Enrichment
Where suggestions come from, and how accepting and rejecting them works.
