walkytalky

Private messaging,
you host it

A self-hosted, end-to-end encrypted 1:1 chat app for iOS, built on Firebase. No central operator reads your messages or media - encryption keys are derived on-device via an offline QR handshake and never leave the device.

iOS / SwiftUI

Native client

Firebase

Auth + Firestore + Storage

X25519 ECDH

On-device key exchange

AES-256-GCM

Per-message encryption

Anonymous auth

No email or phone

MIT licensed

Self-host it yourself

View on GitHub

There is no shared, hosted walkytalky service. Every deployer stands up their own Firebase project and owns their own data.

How it works

Keys that never touch the server

The client is SwiftUI on iOS with no custom backend - Firebase is the only infrastructure. Auth is anonymous-only, data lives in Firestore and Storage, and every cryptographic primitive (X25519, HKDF-SHA256, AES-256-GCM, PBKDF2-HMAC-SHA256) runs through Apple’s CryptoKit.

Firestore and Storage security rules scope every read and write to real participant membership, verified server-side - never trusting client-supplied claims. The rules files are deliberately readable.

01

Pair over QR

Two devices exchange X25519 public keys via an offline QR scan. The shared secret is derived entirely on-device through an ECDH exchange - it is never transmitted or seen by the server.

02

Derive the key

HKDF-SHA256 over the ECDH output, salted with the conversation ID, produces a 32-byte conversation key. Re-pairing generates a new key version and discards the old one - old messages become permanently undecryptable, by design.

03

Encrypt every message

AES-256-GCM with a random 12-byte nonce, per message. The nonce is stored separately in Firestore; the ciphertext and GCM tag are concatenated and base64-encoded.

04

Strip metadata from media

EXIF and GPS data are removed from every image before it leaves the device. Images are downscaled to a 4K long edge and normalized to JPEG before encryption.

05

Admin pairs, not the app

There is no in-app "add contact" flow. Conversations and per-conversation display names are created directly in the Firebase console - a deliberate scope boundary, not a missing feature.

Feature set

What’s in the app

Identity & access

  • •Anonymous sign-in via Firebase Auth - no email, phone number, or personal identifiers
  • •Single-device enforcement, tracked via a Firestore transaction
  • •Local 6-digit passcode, hashed with PBKDF2-HMAC-SHA256 (100k iterations) and stored only in the iOS Keychain
  • •Exponential lockout after failed passcode attempts (5s up to a 30min cap)
  • •The app locks immediately when minimized - no grace period

Messaging

  • •End-to-end encrypted text and media between two paired participants
  • •Delete for me / delete for everyone, sender-only, unrestricted by time
  • •Read receipts on the sender’s own messages only
  • •Links open in a private in-app browser - a non-persistent WKWebView with no disk history
  • •No real push notifications: an opportunistic background check posts a fixed "You have a new message" alert, since it never decrypts anything

Media

  • •Camera capture and picking from the photo library or Files app, plus GIFs
  • •EXIF/GPS metadata stripped from every image before it leaves the device
  • •Decrypted media renders from an app-private sandbox only, never the system Photos library
  • •"No Face" mode - on-device Vision face detection blurs every face before a photo is encrypted and sent, local to the device that enables it, and fails closed if blurring fails

Architecture

  • •SwiftUI client for iOS - Firebase is the only backend, no custom server
  • •CryptoKit for all cryptographic primitives
  • •Firestore security rules scope every read/write to real participant membership, verified server-side
  • •Self-hosted: every deployer stands up their own Firebase project and owns their own data

Known limitations

Deliberate scope boundaries

Two participants per conversation, by design

The end-to-end key is a single AES key derived from a pairwise ECDH exchange - it cannot extend past two people without a different key-agreement scheme.

No in-app contact discovery or conversation creation

A deliberate scope boundary. Conversations are created by an admin directly in the Firebase console.

No real push notifications, on purpose

The background message check is opportunistic only - iOS decides if and when it runs. This avoids requiring a paid Apple Developer Program membership just to self-host an instance.

No group chat. No video support yet.

iOS only, for now

The wire protocol (QR payload, key derivation, message encryption) is documented in the README for anyone building a compatible client.

Self-hosting

Run your own instance

Self-hosting means standing up your own Firebase project - there is no separate server to run. You’ll need a Mac with Xcode, a free Apple ID (a paid Apple Developer Program membership is only required for TestFlight or App Store distribution), and a Google account for Firebase. The full walkthrough - creating the Firebase project, deploying security rules, configuring the iOS app, and pairing your first conversation - is in the README.

Read the self-hosting guide on GitHub

walkytalky is open source under the MIT license. There is no walkytalky service operated by withpreet - every self-hosted instance is independent, and the person who deploys it owns their own Firebase project and data.