Technical White Paper

How AFK Works

Technical architecture of real-time encrypted terminal streaming.

System Overview

AFK has three components: the AFK CLI captures your terminal on the desktop, the Backend routes messages between devices, and the Client displays the terminal on your phone. The backend is a blind relay — it routes encrypted bytes it cannot read.

Session Streaming

How terminal data flows in real-time between your desktop and mobile device.

1 PTY Capture

The AFK CLI spawns a pseudo-terminal around your shell (or Claude Code). It captures output in 1KB chunks and maintains a 1MB scrollback buffer so clients can catch up on reconnect without missing any output.

2 Binary Protocol

Messages use MessagePack over WebSocket — a binary serialization format that's 33% smaller than JSON. Connections set TCP_NODELAY to disable Nagle's algorithm for the lowest possible latency on each keystroke.

3 Message Routing

The backend is message-type-agnostic — it reads the envelope header (connection_id) to determine the routing target and forwards the encrypted payload as an opaque blob. New message types work automatically without backend changes. Each connected client has its own output stream and reads at its own pace.

4 Bidirectional I/O

Input flows back from the mobile app to the terminal: Client → AFK Backend → AFK CLI → PTY stdin. The same E2E encryption protects input, so the backend cannot see what you type.

5 Reconnection

On reconnect, the AFK CLI replays its scrollback buffer so the client instantly sees the full terminal state. Warm reconnect detection avoids redundant replays, and exponential backoff prevents reconnect storms during transient network issues.

End-to-End Encryption

The zero-knowledge encryption layer between the AFK CLI and client. The backend relays encrypted bytes without the ability to decrypt them.

Key Exchange

Both sides generate ephemeral X25519 keypairs and exchange public keys via the backend. The backend transports the public keys but cannot derive the shared secret — that requires a private key it never sees.

# AFK CLI side

cli_sk = X25519.generate() # private, never leaves device

cli_pk = cli_sk.public_key() # sent to client via backend

 

# Client side

client_sk = X25519.generate() # private, never leaves device

client_pk = client_sk.public_key() # sent to AFK CLI via backend

 

# Both compute the same shared secret independently

shared = ECDH(my_private, their_public) # identical on both sides

Encryption

Terminal data is encrypted with AES-256-GCM using the shared secret. Nonces use a counter-based scheme (not random), eliminating the risk of nonce reuse even across millions of messages. Each message carries a 16-byte authentication tag that prevents tampering.

# Encrypt (AFK CLI or client)

nonce = counter_to_nonce(msg_counter++) # deterministic, no reuse

ciphertext = AES-256-GCM.encrypt(

key = shared_secret,

nonce = nonce,

plaintext = terminal_data

) # includes 16-byte auth tag

 

# Decrypt (other side)

plaintext = AES-256-GCM.decrypt(

key = shared_secret,

nonce = nonce,

ciphertext = ciphertext

) # fails if any byte was altered

What the Backend Sees

The backend is message-type-agnostic. It reads only the envelope header for routing and never parses message payloads. New message types are forwarded automatically without any backend changes.

Visible to Backend (envelope header only)

  • user_id, session_id
  • connection_id (routing target)
  • Message type tag (for logging, not routing)
  • Encrypted payload bytes (opaque blob)
  • Message size

Invisible to Backend

  • Terminal output text
  • Keyboard input
  • File contents
  • Commands executed
  • Any payload content

Context Isolation

A second, independent encryption layer on the backend. While E2EE protects against a compromised server, context isolation protects against routing bugs in the backend code itself.

Why This Exists

Even if a backend bug routes session A's data to user B, user B cannot decrypt it. The data is encrypted with user A's context key, which user B doesn't have. The failure mode is garbled data, not a data leak.

Key Hierarchy

Context isolation uses a three-level key hierarchy:

MK

Master Key

A single key stored outside the database (environment variable). Encrypts all per-user keys. Acts as a Key Encryption Key (KEK).

UK

Per-User Keys

Randomly generated per user, encrypted with the master key (AES-256-GCM), stored in PostgreSQL, cached in an LRU memory cache.

SK

Per-Session Keys

Derived as HMAC-SHA256(user_key, session_id) — unique per session, deterministic, never stored. Recomputed on demand.

Cipher Choice

Context isolation uses ChaCha20 (stream cipher, no authentication tag). An auth tag is unnecessary here because the E2EE layer already provides authenticated encryption — if anyone tampers with the data, GCM decryption on the client will fail. ChaCha20 is fast, constant-time, and avoids the overhead of a second authentication tag.

Double Encryption Flow

The step-by-step journey of terminal output through both encryption layers.

1

AFK CLI encrypts with E2EE

Terminal output is encrypted with the shared secret (AES-256-GCM). Only the client can decrypt.

2

Backend adds context encryption

The already-encrypted data is encrypted again with the user's session key (ChaCha20) before storage.

3

Backend strips context encryption

When delivering to the client, the backend decrypts its own layer, restoring the E2EE ciphertext.

4

Client decrypts E2EE

The client decrypts with the shared secret, recovering the original terminal output. If any byte was tampered with, GCM authentication fails.

Edge Network & Multi-Region

How Cloudflare and multi-region deployment minimize latency for terminal streaming.

Cloudflare Edge

All connections hit Cloudflare's global network first. SSL terminates at the nearest Point of Presence, so the TLS handshake is fast regardless of origin location. Cloudflare also provides DDoS protection, WAF rate limiting, and auto-compression.

Geo-Steering

Cloudflare routes each connection to the nearest region automatically. A developer in the US connects to SFO, Europe to FRA, Asia to BLR. No client-side configuration required.

Region-Aware Sessions

When the AFK CLI creates a session, it registers in the nearest region. The mobile app discovers the session via the API, gets a region_url, and connects its WebSocket directly to that region. No cross-region traffic.

Multi-Region

Each region runs its own AFK Backend, so session data stays local. Currently deployed in Bangalore (BLR), expanding to San Francisco (SFO) and Frankfurt (FRA).

Why This Matters for Terminal Streaming

Without multi-region: SF user → BLR backend = ~250ms RTT per keystroke

With multi-region: SF user → SFO backend = ~20ms RTT per keystroke

Cloudflare edge SSL: TLS handshake completes at nearest PoP, not origin

Security Properties

The two encryption layers protect against different threat models. Together, they provide defense-in-depth.

E2E

E2EE Protects Against

  • Compromised backend server
  • Network eavesdropping (even without TLS)
  • Database breach
  • Malicious infrastructure provider
CTX

Context Isolation Protects Against

  • Routing bugs in backend code
  • Cross-user data leakage
  • Session ID confusion or collision
E2E
+CTX

Combined: Defense-in-Depth

No single point of failure. Even if one layer is bypassed, the other still protects your data. The system is fail-closed: if anything goes wrong, the GCM authentication tag prevents garbled data from being displayed — you get a decryption error, not a data leak.

Cryptographic Primitives

Summary of all cryptographic algorithms used in AFK.

Purpose Algorithm Standard
Key exchange X25519 ECDH RFC 7748
E2EE cipher AES-256-GCM NIST SP 800-38D
Context cipher ChaCha20 RFC 8439
Key derivation HMAC-SHA256 RFC 2104
Master key encryption AES-256-GCM NIST SP 800-38D

For a higher-level overview of our security approach, see the Security page.

Security Overview