How it works
A menu-bar app and a web page.
No App Store, no jailbreak, nothing to install on the iPad. A small Swift app on the Mac serves the interface to Safari over your local network.
iPad
Safari, iOS 12 and later
- Home-screen web app, full screen
- TypeScript + Preact, bundled by esbuild
- Widget grid engine and editor
- Keeps itself awake, recovers from crashes
HTTPInterface, pairing, app icons, website icons, artwork
WebSocketMetrics every second, actions, trackpad, Pomodoro, layout
Mac
Menu-bar app, Swift
- Hand-written HTTP + WebSocket server, port 8765
- Collectors: IOKit, CoreAudio, libproc, EventKit
- Actions: CGEvent, Accessibility, AppleScript, Shortcuts
- No third-party dependencies
Built for old iPads
An iPad Air from 2013 tops out at iOS 12.5 and 1 GB of RAM. The interface is written for exactly that:
- The build refuses anything iOS 12 can't run. Every bundle is checked as ES2017 JavaScript, for post-Safari-12 APIs, and for CSS that iOS 12 doesn't support. A self-test makes sure the checker itself catches the classic traps.
- Small and steady: about 190 KB of JavaScript (64 KB gzipped), no framework beyond Preact, no heavy effects like backdrop blur.
- Made to run all day: it keeps the screen awake, reconnects on its own, and if something crashes it shows a short notice and reloads itself.
- One grid engine places, moves and resizes widgets, with tests for every edge case.
The Mac side
The menu-bar app is a Swift package with no dependencies. It serves the interface, keeps the paired devices, sends the Mac's state once a second, and runs the actions the iPad asks for.
| Area | How |
|---|---|
| CPU, memory, disk, network | Mach host statistics, libproc, IOKit |
| Temperatures, fans, power | The SMC, read without root |
| Batteries | IOKit for the Mac and accessories, Bluetooth for AirPods, libimobiledevice for iPhone and iPad |
| Audio | CoreAudio for outputs and volume; a system audio tap and an AirPlay 1 sender for speakers |
| Calendar, reminders | EventKit |
| Music, Spotify, dark mode | AppleScript |
| Keys, typing, trackpad | CGEvent and Accessibility |
What stays on the Mac
Your layout, paired devices (names, dates and only a hash of each token) and Pomodoro stats, in ~/Library/Application Support/MacDeck/. There's no account, no analytics and no cloud service.
The only requests that leave your network:
- the weather and city search, from Open-Meteo, for the city you set;
- track artwork for what's playing;
- the icons of the websites your link keys open.
Security model
Home network only. The iPad and the Mac talk over plain HTTP and WebSocket, without TLS. On a café, hotel or office network, someone could capture a paired device's token. Quit the app on networks you don't trust.
- Pairing needs the 6-digit PIN shown on the Mac, accepted only while the Pair window is open. It works once and expires after 3 minutes or 5 wrong tries.
- Tokens are random, sent as HttpOnly cookies, and stored on the Mac only as SHA-256 hashes.
- Local only: the server answers only clients on the Mac's own subnets, and WebSocket connections must come from the app's own origin.
- Revoke any time: Paired Devices in the menu bar. A revoked token stops working at once.
- A paired iPad is trusted: it can press keys, type, move the pointer and quit apps. Treat it like an unlocked keyboard.
Found a problem? Please report it privately, as described in the security policy.
Credits
- OldPhoneDeck by GLAUCOMA82, for the idea of turning an old iPhone into a deck. OldPadDeck is a separate implementation, written from scratch for the iPad.
- Preact (MIT), bundled in the iPad interface.
- Weather data by Open-Meteo.com, under CC BY 4.0.