Security

How Desmaptic protects your Mac and your iPhone or iPad

A tap on your iPhone or iPad runs things on your Mac, so Desmaptic guards the way in: only devices you paired can connect, the connection between them is encrypted, and nothing new runs through Desmaptic without your answer. Plain words first; the details for developers are at the end.

In short

Desmaptic Agent puts it in four points, under How Desmaptic protects this Mac in its Security section. Each one is explained below.

An encrypted connection

Your iPhone or iPad and your Mac talk directly, over your local network or, if you allow it, your own VPN. There is no Desmaptic account, and no Desmaptic server sits in between.

Everything they send each other is encrypted, with new keys for every connection; there is no unencrypted mode. Others on the same Wi-Fi can’t read it, and a message changed on the way closes the connection instead of reaching the other side.

No password or token travels over the connection for someone to catch and reuse. Each device proves who it is with its own key, made on that device and never synced to iCloud.

Pairing takes your click and a code

A new iPhone or iPad connects only after you let it, on the Mac:

  1. On the phone, tap your Mac. The phone asks you to “Confirm on your Mac”: “On your Mac, click Show Code in Desmaptic Agent.”
  2. A Pairing Request appears in Desmaptic Agent: “‹name›” wants to pair with this Mac, the address it comes from, the device’s fingerprint and how long ago it asked. Click Show Code within 60 seconds, or Decline.
  3. Type the code on the phone, under “Enter the code shown on your Mac”, within 90 seconds.

Fingerprints

Every device makes its own key, on the device. A fingerprint is a short form of that key: four groups of characters, such as 3f2a 91c0 7b11 04de. The Mac’s request card shows the phone’s fingerprint. This Mac’s fingerprint, in the agent’s Security section, is the one your iPhone or iPad shows under that Mac in Paired Macs.

After pairing

The phone reconnects by itself, but only to a Mac whose key it remembers, and it checks that key before it shows its own. If a computer claims to be your Mac but can’t prove it, the phone doesn’t connect and says:

Couldn't confirm this is “MacBook Pro”. It may be another user account on that Mac, a reinstalled Desmaptic Agent, or a different computer. Pair again only if you can see the code on your Mac.

You can cut a device off at any time. Remove or Remove All in the agent’s Devices section works at once, and the phone then says the Mac removed it and that it needs to pair again. On the phone, Forget in Paired Macs works the other way round: “To use it again, pair with the code shown on that Mac.”

While another user’s session is active on the Mac, the agent lets no phone in. Its menu then reads “Paused — another user is using this Mac”.

Away from home, over your VPN

Away from home, a paired iPhone or iPad can reach your Mac through your own VPN, such as Tailscale. The agent lets it in only while Allow Connections over VPN is on in its Security section, and it starts off. Even then:

How to set it up, a Tailscale rule that lets only your phone reach the Mac, and what to do if you lose the phone: Away from home.

Nothing new without your answer

Three things can bring something new through Desmaptic: a change to your deck, a command from an AI and a command in a notification. Each one waits for you.

Changes to your deck: Accept on the phone

A change from an AI app, or from any other program that sends buttons to Desmaptic Agent, arrives on your iPhone or iPad as a review card. It opens in its own window above everything in the app, even the editor, the Gallery and Settings, and says which Mac sent it.

  • Every script on it can be read: new code in full, changed code as a diff under Show changes. Code with a warning starts open.
  • Warnings point at what deserves a closer look, such as “Talks to the internet: …”, “Downloads something from the internet and runs it” or “Contains hidden characters”. Characters that would otherwise be invisible, or would reorder the text, show up as visible marks. Next to its warnings, the card adds: “These checks can't catch everything.”
  • Under “In the AI's words:” is how the AI describes the change. Check it against the code.
  • Accept applies the whole change, never a part of it. It works a second after the card appears, and that second starts over when the card is rebuilt or you come back to the app, so a tap meant for something else doesn’t approve it.
  • If your buttons change while the card is open, it is rebuilt and says so: “Your buttons changed while this was open, so the list was updated. Take another look.”
  • Reject “Tells the AI no. Nothing changes.” Decide Later puts the change aside: for a few minutes, you can answer from the banner above your buttons.
The review card on the iPhone: “The AI wants to add the button “CPU””, from MacBook Pro, page “Main”, the AI’s words, a CPU row marked NEW with “In the background: runs on your Mac while this iPhone is connected” and Show code (201 lines), “1 script will run in the background on your Mac”, and at the bottom Accept, Decide Later and Reject.
A change from an AI waits on the iPhone.

What an AI can do through Desmaptic

Desmaptic Agent lists it in its AI Assistants section, under “What AI can and can’t do without you”:

Commands from an AI: Allow, one at a time

An AI can ask Desmaptic to run a command on your Mac to check something. That is off until you turn on Allow AI to Run Commands in the agent’s Security section, and it starts off.

With it on, each command comes to the phone first: “The AI wants to run a command on MacBook Pro”, the whole command, and the AI’s reason under “In the AI's words:”. You have 35 seconds to tap Allow or Deny; no answer counts as no. Only one command waits at a time. An allowed command runs for 15 seconds at most, and “What the command prints goes back to the AI that asked for it.”

Commands in notifications

The scripts of your buttons, and other programs on the Mac, can show notifications with buttons on the phone and on the Mac. A notification button that opens a web page or an app, or does something light such as play/pause, the volume or the brightness, runs when you click it.

Anything else, such as a command, a shortcut, typing text, sleep or emptying the Trash, runs at a click without a question only if the scripts of your own buttons sent the notification: that is like pressing the button. From any other program, Desmaptic shows the whole command first and asks:

  • on the Mac, “Run this from a notification?”, where both Return and Escape choose Cancel;
  • on the phone, “Run on your Mac?”, with Run on Mac and Cancel. Like Accept, Run on Mac works only a second after the sheet appears.

An action runs only on the Mac that sent the notification, and a click on the notification itself runs nothing.

The phone’s “Run on your Mac?” sheet: “The notification action “Open Log” will run on MacBook Pro:”, the notification “Backup finished”, the command “open -a Console /tmp/desmaptic-demo/backup.log”, “Only run it if you expected this notification and recognize this command.”, and the buttons Run on Mac and Cancel.
A command from a notification asks first.

What is kept out

Besides your iPhone or iPad, Desmaptic Agent answers programs on the same Mac, in two ways:

That keeps out:

On the iPhone or iPad

For developers

The same protection in technical terms.

Transport
TCP port 9847 on the local network, found through Bonjour (_desmaptic._tcp). The phone connects and the Mac listens. From a VPN, only an already paired device, and only while Allow Connections over VPN is on; pairing only from the local network; from the internet, never. No Desmaptic server is involved, and there is no unencrypted mode.
Handshake
Noise_XX_25519_ChaChaPoly_SHA256 with the prologue Desktap-SC/1, the phone as the initiator. Fresh ephemeral keys on every connection give forward secrecy. No tokens or other reusable secrets go over the wire.
Identity
One long-term X25519 key per iPhone or iPad and per user account on a Mac, made there and pinned at pairing. Trust comes only from the key proven in the handshake; names, addresses and Bonjour records are hints, never proof. When reconnecting, the phone checks the Mac’s static key after the second handshake message and, unless it is a key it paired with, stops before the third, which would carry its own.
Fingerprints
Lowercase hex of the SHA-256 of the public key. The apps show the first 16 characters, in four groups.
Pairing
CPace (CPace-X25519-SHA512, draft-irtf-cfrg-cpace-20) inside the Noise session, bound to both static keys, with key confirmation. The 6-digit code comes from the system’s secure random generator, only after the click on the Mac, lives 90 seconds and never crosses the network. Three tries per code; five failed tries since the last successful pairing lock pairing until Allow Pairing.
Messages
After the handshake, every message is a Noise transport message, authenticated and encrypted with ChaCha20-Poly1305. A bad tag, a malformed frame, record or control message, or a message that isn’t allowed at that moment closes the connection at once: no resync and no fallback to an older protocol. Application data flows only after the Mac admits the phone. An application message that passes the tag check but that the receiver doesn’t understand, such as one from a newer version, is skipped.
Keys at rest
On the Mac, in the login keychain, with access tied to the agent’s code signature. On iOS, in the Keychain, this device only, available after the first unlock. Neither is synchronizable.
Local API
Button scripts reach the agent at 127.0.0.1:9848 and [::1]:9848 with Authorization: Bearer and a token of 32 random bytes (dtw_ and 43 base64url characters), new on every run, handed to scripts in DESMAPTIC_TOKEN and in ~/.desmaptic-mcp-token (mode 0600). Six routes: status, view, update-button, notify, error and audio/levels. Everything else, delivering a change and asking to run a command included, is served only on the Unix socket in ~/Library/Application Support/Desmaptic/LocalAPI/ (socket 0600, folder 0700), without a token: owning the path is the check. A folder that is a symlink or belongs to another user isn’t served.
Gateway
Checked after the headers, before the body is read: an Origin or Sec-Fetch-Site header gets 403; over TCP, a Host other than localhost, 127.0.0.1 or [::1] gets 421 (DNS rebinding) and a missing or wrong token gets 401; an unknown route, 404; an admin route over TCP, 403; a body over the route’s limit, 413. Every response carries Cache-Control: no-store and X-Content-Type-Options: nosniff.
Notifications
Over TCP, a notification with an action that runs something is accepted only from the process group of a button script the agent started; anyone else gets 403, even with the token. Where a notification came from is set by the agent, never read from the request (a body with origin gets 400), and reaches the phone inside the encrypted connection, so both ask by the same rule. A sensitive notification can’t carry a command.
Commands
No route runs a command on its own: an AI’s command waits for Allow on the phone, and restarting a startup script restarts only one already on your deck.
Updates
Desmaptic Agent is signed with a Developer ID, notarized by Apple and runs with the hardened runtime. Updates come through Sparkle with a signed feed, and every download is checked against its EdDSA signature before it is unpacked.
Logs
Keys and pairing codes are never logged. Scripts appear in the agent’s log only as their length and a short digest, and the AI’s words about a change aren’t logged.

More in the docs: pairing, the agent’s Security section, away from home over a VPN, what an AI can and can’t do, when a notification asks first, the local API and its status codes.