Android phone ↔ Mac · local network only

Your phone and your Mac. Nothing in between.

Linked Mac connects an Android phone to a Mac directly over your Wi-Fi. No cloud, no account. One mutually authenticated TLS channel carries notifications, messages, calls, photos, files, clipboard and camera, and nothing is trusted until you have checked a six-digit code with your own eyes.

TLS 1.3, both sides authenticatedEd25519 identity keys, pinnedNo server, no sign-in

Live: the pairing check, computed in your browser

Mackey 7F3A 91C2 …

SHA-256(ctx ‖ mac, phone ‖ nonce) → first 4 bytes mod 10⁶

Phonekey C04E 1B77 …

SHA-256(ctx ‖ mac, phone ‖ nonce) → first 4 bytes mod 10⁶

The codes match: both devices hold each other’s real key.

This runs the project’s real derivation: SHA-256("linked-mac-SAS-v1" ‖ lo ‖ hi ‖ nonce), with the first 4 bytes read as a big-endian integer, mod 1,000,000. The keys and nonce are random demo bytes made in this page. A relay has to swap in its own key, and that changes the code the Mac shows.

Watch

A pairing in twenty seconds

The Mac shows a QR code, the phone scans it, both screens show 481 207, both people confirm, and the features switch on. The keys and addresses in the film are made up.

How it works

From a QR code to an open channel

Pairing takes one scan and one comparison. Both machines check each other before either shows a code, and the link opens only after both people confirm.

  1. 1
    Shows a QR codeIt carries the Mac’s public key, address, port and a fresh nonce: { v, pk, host, port, nonce }
    ──▶
    Scans and validates itThe scanned key is the only Mac the phone will accept.
  2. 2
    TLS 1.3, mutual, with throwaway self-signed certificatesThis keeps the traffic private and tamper-proof. It does not yet prove who is on the other end.
  3. 3
    Says hello, looks up the pinAn unknown key is rejected. There is no trust on first use.
    ◀─▶
    Says hello, checks the pinThe Mac’s key must equal the one it scanned.
  4. 4
    Answers a challengeSigns a fresh nonce bound to the hash of its own TLS certificate.
    ◀─▶
    Answers a challengeSame proof, checked against the certificate its session actually received.
  5. 5
    Both screens show the same six digitsEach device derives the code from both keys and the nonce. You compare them and confirm on each side.
  6. 6
    Sends link.readyOnly once the person at the Mac has agreed too.
    ──▶
    Shows “Linked”The authenticated channel opens and features start.

Why the code appears after the handshake

The Mac learns the phone’s key only at hello, and the code needs both keys. Connecting first is what puts the same digits on both screens at the same moment. The final link.ready exists because each person confirms on their own device, and neither can see the other’s answer. The phone waits for it so it never reports a link the Mac is about to drop.

Trust model

No certificate authority, so three checks of its own

On a home network no authority can vouch for 192.168.1.20. There is no name to validate and nothing to revoke. So TLS here provides confidentiality and integrity, and Linked Mac proves identity itself. Every check runs before a single app message is delivered, and any failure closes the connection.

Machine check

Identity pinning

Each device has a long-lived Ed25519 key. The peer’s key is compared in constant time against the one confirmed at pairing. Unknown keys are refused.

Machine check

Channel-bound challenge

Each side signs a fresh nonce together with the SHA-256 of its own certificate. A relay must present a different certificate, so a signature replayed from the real device fails.

Human check

The six-digit code

Derived from both keys and the pairing nonce. Anyone in the middle has to substitute a key, which changes the code, which you see.

For developers reading the code

Certificate verification is switched off on purpose, and the checks above are the only reason that is safe. Never copy that setting into code that talks to a normal internet server: there it removes the only protection you have.

Wire format · both apps must agree byte for byte
SAS input"linked-mac-SAS-v1" ‖ lo ‖ hi ‖ nonce, keys sorted ascending as unsigned bytes
SAS outputFirst 4 digest bytes as a big-endian uint32, mod 1_000_000, zero-padded to 6
Auth transcript"linked-mac-auth-v1" ‖ len32(challenge) ‖ challenge ‖ len32(binding) ‖ binding
Channel bindingSHA-256 of the peer’s certificate DER
Pairing offerJSON { v, pk, host, port, nonce }, base64 with padding
Frame[4-byte big-endian length][JSON], max 1 MiB
Pairing verdictA data frame on channel link.ready

Features

Seven features, one link

Every feature is a module that rides the same authenticated channel. There is no second connection. Nothing runs until you switch it on, and each one can be turned off on its own.

  • NotificationsMirror phone notifications on the Mac; reply, dismiss or tap an action.on by default
  • MessagesRead SMS threads and reply from the Mac.on by default
  • Call controlSee call state; dial and answer from the Mac. No audio.off by default
  • PhotosBrowse the phone’s gallery; new shots sync over.off by default
  • FilesBrowse the folders you grant access to.off by default
  • ClipboardSend a clip either way from the menu-bar panel, ⇧⌘V.off by default
  • CameraUse the phone as a webcam preview while the app is open.off by default

Switched on is not the same as allowed

The app shows its own toggle and Android’s permission side by side. Turning a feature off stops it but keeps the permission, so turning it back on is instant.

Your clipboard moves only when you say so

Clipboards hold passwords and one-time codes, so nothing is mirrored in the background. Copy on the phone and it asks “Send to your Mac?” without reading what you copied.

Your SMS app stays your SMS app

Messages reads and sends alongside your default messaging app instead of replacing it, so your texts are always stored by the app you already use.

Stays connected in your pocket

A foreground service keeps the link alive through Android’s battery saving. Camera and clipboard reads pause when the app is in the background.

Where keys live

The private key stays in the device’s vault

macOS

Stored in the Keychain under com.linkedmac.identity.

Android 13 and later

An Ed25519 key generated inside the Android Keystore.

Android 8 to 12

A 32-byte seed sealed with AES-GCM under a hardware-backed Keystore key.

The private key never reaches the app’s interface code and never appears in a log. Only public keys and fingerprints are shown on screen.

Boundaries

What it deliberately doesn’t do

  • No call audioCall control is state and buttons only.
  • No hanging upAndroid lets only the default phone app end calls. The phone says so instead of showing a dead button.
  • No system webcamThe camera shows inside Linked Mac; it does not appear in other Mac apps yet.
  • No MMSMessages handles SMS only.
  • No large file transfersFiles shows up to 512 KiB per file; larger ones are marked as truncated.
  • No backdoorsNo ADB, no root, no accessibility-service control of your phone.