<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>TermOnMac — Engineering Notes</title><description>Technical notes on building TermOnMac: cryptography, PTY sessions, Cloudflare Durable Objects, and the rest of a zero-knowledge Mac terminal relay.</description><link>https://termonmac.com/</link><language>en-us</language><item><title>How TermOnMac Works: A Zero-Knowledge Mac Terminal Relay</title><link>https://termonmac.com/blog/how-termonmac-works/</link><guid isPermaLink="true">https://termonmac.com/blog/how-termonmac-works/</guid><description>How an iPhone controls a Mac terminal over the internet — iOS app, Mac CLI daemon, Cloudflare Workers relay. End-to-end encrypted; the relay forwards ciphertext it cannot read.</description><pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate><category>architecture</category></item><item><title>Using Durable Objects as a WebSocket Session State Machine</title><link>https://termonmac.com/blog/durable-objects-websocket-state-machine/</link><guid isPermaLink="true">https://termonmac.com/blog/durable-objects-websocket-state-machine/</guid><description>How TermOnMac&apos;s Cloudflare Durable Object manages Mac and iPhone WebSocket slots, handles quota checks at connection time, and survives hibernation via tagged socket recovery.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>relay</category></item><item><title>Surviving Cloudflare DO Hibernation Without Dropping WebSocket Connections</title><link>https://termonmac.com/blog/durable-objects-hibernation/</link><guid isPermaLink="true">https://termonmac.com/blog/durable-objects-hibernation/</guid><description>Cloudflare Durable Objects hibernate when idle, losing in-memory state. How TermOnMac rebuilds WebSocket role mappings from storage-backed UUID tags on every wake.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>relay</category></item><item><title>Per-User Strong Consistency: Subscription State in a Durable Object</title><link>https://termonmac.com/blog/subscription-durable-object/</link><guid isPermaLink="true">https://termonmac.com/blog/subscription-durable-object/</guid><description>Why TermOnMac&apos;s subscription tier lives in a per-user Durable Object instead of KV — serialized writes, lazy expiry, notification dedup, and stale downgrade protection.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>relay</category></item><item><title>Building a Credit-Based Quota System with 5-Hour Rolling Windows</title><link>https://termonmac.com/blog/credit-quota-rolling-window/</link><guid isPermaLink="true">https://termonmac.com/blog/credit-quota-rolling-window/</guid><description>TermOnMac charges 1 credit per relay message and 2 per minute of DO runtime. How the 5-hour window, welcome bonus, and split KV keys protect admin grants from stale writes.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>relay</category></item><item><title>Enforcing Per-User Room Limits Without a Database</title><link>https://termonmac.com/blog/room-limit-without-database/</link><guid isPermaLink="true">https://termonmac.com/blog/room-limit-without-database/</guid><description>TermOnMac caps concurrent Mac rooms per tier using a Cloudflare KV prefix scan with 15-minute TTLs — no counters, no cleanup job, reconnects always allowed.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>relay</category></item><item><title>X25519 Key Exchange Without a Central Key Server: How QR Pairing Works</title><link>https://termonmac.com/blog/x25519-device-pairing/</link><guid isPermaLink="true">https://termonmac.com/blog/x25519-device-pairing/</guid><description>QR pairing, persistent identity keys, and per-connection ephemeral keys. How two devices derive a shared AES-256-GCM session key without a central key server.</description><pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate><category>crypto</category></item><item><title>Why We Don&apos;t Use Raw ECDH Output: HKDF Session Key Derivation</title><link>https://termonmac.com/blog/hkdf-session-key-derivation/</link><guid isPermaLink="true">https://termonmac.com/blog/hkdf-session-key-derivation/</guid><description>Why we don&apos;t use the raw X25519 shared secret as an AES key. HKDF-SHA256 with a versioned, nonce-salted construction gives per-connection key separation and protocol versioning.</description><pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate><category>crypto</category></item><item><title>Channel Binding with HMAC: Preventing Relay-Level MITM Attacks</title><link>https://termonmac.com/blog/channel-binding-hmac/</link><guid isPermaLink="true">https://termonmac.com/blog/channel-binding-hmac/</guid><description>An HMAC over both sorted ephemeral public keys — keyed by the room secret — prevents a compromised relay from substituting keys during the handshake.</description><pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate><category>crypto</category></item><item><title>Forward Secrecy in a Reconnectable Protocol: Two Key Pairs Per Session</title><link>https://termonmac.com/blog/forward-secrecy-ephemeral-keys/</link><guid isPermaLink="true">https://termonmac.com/blog/forward-secrecy-ephemeral-keys/</guid><description>Why TermOnMac uses a persistent identity key for TOFU and a per-connection ephemeral X25519 key for ECDH — so a stolen identity key cannot decrypt past recorded traffic.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>crypto</category></item><item><title>Trust On First Use: How TermOnMac Verifies Your Mac&apos;s Identity on Reconnect</title><link>https://termonmac.com/blog/tofu-mac-identity/</link><guid isPermaLink="true">https://termonmac.com/blog/tofu-mac-identity/</guid><description>The first identity key registered for a room is trusted. How TermOnMac rotates the room secret and replaces sockets without breaking the active session.</description><pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate><category>crypto</category></item><item><title>forkpty() on macOS: The Login Shell Setup</title><link>https://termonmac.com/blog/forkpty-login-shell/</link><guid isPermaLink="true">https://termonmac.com/blog/forkpty-login-shell/</guid><description>How TermOnMac spawns a login shell via forkpty() on macOS — argv[0] tricks, controlling TTY setup, and why SHELL_SESSIONS_DISABLE matters for a programmatic PTY.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>pty</category></item><item><title>SHELL_SESSIONS_DISABLE and Other macOS Shell Environment Setup</title><link>https://termonmac.com/blog/shell-sessions-disable/</link><guid isPermaLink="true">https://termonmac.com/blog/shell-sessions-disable/</guid><description>The four environment variables TermOnMac sets before execvp — TERM, LANG, SHELL_SESSIONS_DISABLE, TERMONMAC_SESSION — and why each matters for a programmatic PTY.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>pty</category></item><item><title>Non-Blocking PTY I/O with GCD Dispatch Sources</title><link>https://termonmac.com/blog/nonblocking-pty-dispatch-source/</link><guid isPermaLink="true">https://termonmac.com/blog/nonblocking-pty-dispatch-source/</guid><description>How TermOnMac reads from the PTY master FD without blocking a thread — O_NONBLOCK plus a DispatchSourceRead on the userInteractive queue, with EAGAIN-aware writes.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>pty</category></item><item><title>PTY Output Buffering: 32KB or 200ms, Whichever Comes First</title><link>https://termonmac.com/blog/pty-output-buffering/</link><guid isPermaLink="true">https://termonmac.com/blog/pty-output-buffering/</guid><description>Why TermOnMac doesn&apos;t send PTY output on every read — a dual-trigger buffer plus a local terminal query interceptor that answers vim&apos;s cursor queries without a round trip.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>pty</category></item><item><title>RingBuffer for Terminal Replay: Restoring State After Reconnect</title><link>https://termonmac.com/blog/ringbuffer-terminal-replay/</link><guid isPermaLink="true">https://termonmac.com/blog/ringbuffer-terminal-replay/</guid><description>Each TermOnMac PTY session keeps a per-session RingBuffer so the iPhone can replay the current terminal state after a network drop, with incremental offset-based delta delivery.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>pty</category></item><item><title>Integrating SwiftTerm into a SwiftUI App</title><link>https://termonmac.com/blog/swiftterm-ios-integration/</link><guid isPermaLink="true">https://termonmac.com/blog/swiftterm-ios-integration/</guid><description>How TermOnMac wraps SwiftTerm&apos;s CustomTerminalView in a UIViewRepresentable for SwiftUI — feeding PTY bytes, handling resets on reconnect, and blocking accidental pastes.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>ios-ux</category></item><item><title>PTY Resize: Buffer Switch and Reinit Triggers in SwiftTerm</title><link>https://termonmac.com/blog/pty-resize-debounce/</link><guid isPermaLink="true">https://termonmac.com/blog/pty-resize-debounce/</guid><description>How TermOnMac propagates iPhone terminal size changes to the Mac via TIOCSWINSZ — buffer switches, reinit callbacks, Ctrl-L prompt redraws, and debounced resize events.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>pty</category></item><item><title>Building a Terminal Keyboard Toolbar on iOS</title><link>https://termonmac.com/blog/ios-keyboard-terminal/</link><guid isPermaLink="true">https://termonmac.com/blog/ios-keyboard-terminal/</guid><description>iOS keyboards don&apos;t have Ctrl, Esc, or arrow keys. How TermOnMac builds a custom inputAccessoryView with debounced shortcut search, modifier toggles, and focus handling.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>ios-ux</category></item><item><title>Preserving Scroll Position While Terminal Output Keeps Arriving</title><link>https://termonmac.com/blog/scroll-position-terminal/</link><guid isPermaLink="true">https://termonmac.com/blog/scroll-position-terminal/</guid><description>How TermOnMac keeps your reading position when you&apos;re scrolled up and new output arrives — saved scroll ratios, reset-before-replay, and local scroll shortcuts.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>ios-ux</category></item><item><title>Reconnecting Without Hammering the Server: Exponential Backoff with NetworkMonitor</title><link>https://termonmac.com/blog/reconnect-exponential-backoff/</link><guid isPermaLink="true">https://termonmac.com/blog/reconnect-exponential-backoff/</guid><description>How TermOnMac&apos;s iOS app handles lost WebSocket connections — exponential backoff, NWPathMonitor-driven immediate reconnect, input buffering, and a 5-minute total timeout.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>networking</category></item><item><title>Heartbeat Design: Keeping Durable Objects Awake</title><link>https://termonmac.com/blog/heartbeat-design/</link><guid isPermaLink="true">https://termonmac.com/blog/heartbeat-design/</guid><description>Why TermOnMac&apos;s Mac heartbeat flips between 30s and 1s during active use — Cloudflare DO hibernation, not dead-connection detection, is the real driver behind the cadence.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>networking</category></item><item><title>Batching WebSocket Messages: How relay_batch Reduces Overhead</title><link>https://termonmac.com/blog/relay-batch-coalescing/</link><guid isPermaLink="true">https://termonmac.com/blog/relay-batch-coalescing/</guid><description>Why TermOnMac coalesces up to 20 encrypted payloads per WebSocket message — not for framing overhead, but because the relay charges 1 credit per batch instead of per payload.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>relay</category></item><item><title>The attach/detach Model: PTY Keeps Running While You&apos;re Away</title><link>https://termonmac.com/blog/attach-detach-session-model/</link><guid isPermaLink="true">https://termonmac.com/blog/attach-detach-session-model/</guid><description>How TermOnMac separates PTY session lifetime from iOS attachment — two session lists, buffer-only mode, draft input preservation, and full-remove destruction via ptyDestroy.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>architecture</category></item><item><title>Session Takeover: When a Second Device Connects to the Same PTY</title><link>https://termonmac.com/blog/session-takeover/</link><guid isPermaLink="true">https://termonmac.com/blog/session-takeover/</guid><description>How TermOnMac handles two iOS devices racing to attach to the same PTY — an isTakenOver flag, explicit takeover callbacks, and relay-enforced single-iOS-socket per room.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>architecture</category></item><item><title>Unix Domain Socket IPC: How the Mac CLI Talks to the Helper Daemon</title><link>https://termonmac.com/blog/unix-socket-ipc/</link><guid isPermaLink="true">https://termonmac.com/blog/unix-socket-ipc/</guid><description>How TermOnMac&apos;s short-lived CLI talks to the long-running helper daemon — a chmod 0600 Unix socket with a length-prefixed JSON protocol and explicit IPC versioning.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>integration</category></item><item><title>Multi-Provider OAuth with Account Linking: GitHub, Google, and Apple Sign In</title><link>https://termonmac.com/blog/multi-provider-oauth/</link><guid isPermaLink="true">https://termonmac.com/blog/multi-provider-oauth/</guid><description>How TermOnMac links GitHub, Google, and Apple logins to a single user by email — KV key layout, account linking, pending-delete reversal, and email index backfill.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>integration</category></item><item><title>API Key Lifecycle: 30-Day TTL, Refresh Tokens, and Sliding Expiry</title><link>https://termonmac.com/blog/api-key-lifecycle/</link><guid isPermaLink="true">https://termonmac.com/blog/api-key-lifecycle/</guid><description>TermOnMac&apos;s auth model uses 128-bit API keys with 30-day sliding TTLs and 512-bit refresh tokens that rotate on use, with a 5-minute grace period for client crash recovery.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>integration</category></item><item><title>QR Code as a Secure Pairing Mechanism: One-Time Token and TOFU</title><link>https://termonmac.com/blog/qr-code-pairing/</link><guid isPermaLink="true">https://termonmac.com/blog/qr-code-pairing/</guid><description>How TermOnMac uses a single-use QR pairing token plus a rotated room secret and TOFU identity key — so even a photographed QR code becomes useless after the legitimate pair completes.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>integration</category></item><item><title>Verifying Apple JWS Receipts Without a Third-Party Library</title><link>https://termonmac.com/blog/storekit2-jws-verification/</link><guid isPermaLink="true">https://termonmac.com/blog/storekit2-jws-verification/</guid><description>StoreKit 2 JWS receipts carry an x5c cert chain, not a JWK. How TermOnMac hand-rolls a DER parser to extract SPKI and verify ECDSA P-256 signatures using only Web Crypto.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>integration</category></item><item><title>Reliable Apple Subscription Notifications with Durable Objects</title><link>https://termonmac.com/blog/apple-subscription-notifications/</link><guid isPermaLink="true">https://termonmac.com/blog/apple-subscription-notifications/</guid><description>Three layers of protection against Apple&apos;s retrying, out-of-order App Store Server Notifications — UUID dedup, stale-downgrade rejection, and per-user DO serialization.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>integration</category></item><item><title>The R-M-W Race in Cloudflare KV: A Quota Bug and How We Fixed It</title><link>https://termonmac.com/blog/kv-rmw-race-condition/</link><guid isPermaLink="true">https://termonmac.com/blog/kv-rmw-race-condition/</guid><description>A concrete Cloudflare KV read-modify-write race that let stale Room DO flushes silently consume admin-issued quota grants — and the split-key plus version-marker fix.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>relay</category></item><item><title>Recurring Architectural Patterns in TermOnMac</title><link>https://termonmac.com/blog/lessons-learned/</link><guid isPermaLink="true">https://termonmac.com/blog/lessons-learned/</guid><description>Ten recurring patterns across the TermOnMac relay, Mac agent, and iOS app — strong consistency in DO, split KV keys, version markers, lazy expiry, and channel binding.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><category>meta</category></item></channel></rss>