DNSChat is a React Native (Expo dev-client) app that sends short chat prompts as
DNS TXT queries (default DNS server: llm.pieter.com). The app includes:
- A native DNS TXT resolver module for iOS/Android (
modules/dns-native/) - JavaScript fallback transports (UDP/TCP) for constrained networks
- An in-app Logs screen to inspect attempts, failures, and fallbacks
- Chat with LLMs over DNS - no API keys, no accounts, no tracking
- Native DNS resolution on iOS and Android with JS fallback transports
- Encrypted local chat history (AES-GCM)
- Multi-server support with automatic transport fallback
- Bilingual UI (English / Portuguese)
- Full offline chat history with search
- NativeTabs with SF Symbols (iOS) and Material Symbols (Android)
- Platform colors via expo-router Color API (auto light/dark, Android 12+ dynamic)
- Native iOS toolbars with share, clear, and new-chat actions (liquid glass)
- Zoom transitions (iOS 18+) from chat list to thread
- App version:
4.0.13(build43) - Expo workflow: Expo Router + dev-client + EAS-compatible native config
- Expo SDK:
55.0.24 - React:
19.2.0 - React Native:
0.83.6 - TypeScript:
5.9.x - Hermes: enabled
- New Architecture: enabled by default on SDK 55
- React Compiler: enabled (
experiments.reactCompiler: true) - Typed routes: enabled (
experiments.typedRoutes: true)
User prompts are validated and sanitized into a single DNS label (RFC 1035
constraints) before building a query name. Practical constraints (see
modules/dns-native/constants.ts):
- Prompts are capped at 120 characters before sanitization (hard fail, no silent truncation)
- DNS labels are capped at 63 characters after sanitization
TXT responses are parsed as either:
- Plain TXT records (concatenate non-empty records), or
- Multipart
n/N:segments that must form a complete sequence
The transport order used by src/services/dnsService.ts is:
- Native DNS (iOS/Android native module)
- UDP (JavaScript transport via
react-native-udp) - TCP (DNS-over-TCP via
react-native-tcp-socket) - Mock (optional development fallback)
Web preview uses the Mock path by default because browsers cannot do raw DNS on port 53.
Prereqs:
- Node.js 18+
- iOS: Xcode 15+ (macOS only), iOS 16+ device/simulator
- Android: Java 17 + Android SDK
Install:
git clone <repository-url>
cd dnschat
bun installRun:
# Dev server (Expo dev-client)
bun run start
# iOS
bun run ios
# Android (auto-selects Java 17 when available)
bun run android
# Web preview (uses Mock DNS)
bun run webNotes:
- iOS simulator builds work out of the box; device builds require you to pick your own signing team in Xcode (the repo keeps
DEVELOPMENT_TEAMempty). - For a full physical-device Expo dev-client install, build the native
DNSChattarget for the device identifier and install the compiled.app; Expo Go is not a valid substitute for this repo because the app depends on native DNS modules. Keep device names, local paths, and signing identifiers out of public docs.
# Quick DNS check (no React Native runtime required)
node test-dns-simple.js "test message"
node test-dns-simple.js "test message" --local-server
# Full harness (builds scripts/ ts -> js, then runs UDP/TCP transports)
bun run dns:harness -- --message "test message"
bun run dns:harness -- --message "test message" --local-server
# Debug output artifacts
bun run dns:harness -- --message "test" --json-out harness-output.json --raw-out raw-dns.bin# Lint (ast-grep rules)
bun run lint
# Unit tests
bun run test
# AXe simulator E2E
bun run e2e:axe:doctor
bun run e2e:axe:release
# Public-doc redaction gate
bun run verify:public-redaction
# Keep Expo iOS pods aligned with installed node_modules (iOS)
bun run verify:ios-pods
# Sanity checks for Android tooling/device expectations
bun run verify:android
# Full verification gate before committing/release work
bun run verify:all
# Sync app + native module versions (use :dry to preview)
bun run sync-versions
bun run sync-versions:dry
# CocoaPods cleanup helpers
bun run fix-pods
bun run clean-iosThis repo installs a pre-commit hook that blocks commits when the ast-grep lint fails.
Mechanism:
bun installrunsbun run preparepreparerunsscripts/install-git-hooks.js- that script writes
.git/hooks/pre-committhat runsverify:ios-pods,lint(ast-grep), and unit tests
If you do not want repo-managed hooks, remove .git/hooks/pre-commit locally.
Start here:
docs/README.mdβ full documentation indexdocs/INSTALL.mdβ setup, build, verificationCLAUDE.md/AGENTS.mdβ guidance for AI coding agents
Architecture & spec:
docs/architecture/SYSTEM-ARCHITECTURE.mdβ what talks to whatdocs/technical/DNS-PROTOCOL-SPEC.mdβ query/response rulesdocs/technical/SPECIFICATION.mdβ product behavior + repo invariantsdocs/technical/EXPO-DOCTOR-CONFIGURATION.mdβ intentional Expo Doctor warnings
Operational:
docs/troubleshooting/COMMON-ISSUES.mdβ known issues + fixesdocs/data-inventory.mdβ on-device data storage + retentiondocs/model-registry.mdβ model usage policy
Release:
docs/ANDROID_RELEASE.mdβ Android release checklistdocs/ANDROID_GOOGLE_PLAY_STORE.mdβ Play Store publishingdocs/App_store/Apple_App_Store/AppStoreConnect.mdβ App Store listing materialsdocs/App_store/Apple_App_Store/TESTFLIGHT.mdβ TestFlight upload steps
Last full source/security sweep: 2026-05-17.
Last AXe simulator E2E feature pass: 2026-05-17 for version 4.0.13 build
43.
Last iOS signed release archive/export: 2026-05-17 for version 4.0.13
build 43.
bun run verify:allpasses (expo-doctor17/17, SDK alignment, typed routes, DNS resolver sync, iOS pods, React Compiler, Android setup, lint, and Jest).- AXe E2E baseline: 10 feature groups passed in one owned release-simulator run.
- Jest baseline: 95 suites passed, 1 skipped; 816 tests passed, 13 skipped.
bun audit,npm auditinmodules/dns-native, andgitleaks detectreport no vulnerabilities or leaks.xcodebuild clean buildpasses for Debug on an iOS 26.5 simulator.xcodebuild clean buildandxcodebuild clean archivepass for generic iOS Release when code signing is disabled (CODE_SIGNING_ALLOWED=NO).- Physical-device compiled Expo dev-client install passed.
- Signed App Store archive/export passed, TestFlight upload completed, the
processed build is
VALID, and TestFlight validation reports0errors and0warnings. Internal App Store Connect IDs are intentionally omitted from public docs. asc validate testflightand App Store version validation pass with0errors and0warnings; the remaining App Store validation notes are informational: manual release type and API-unverifiable App Privacy publish state.xcodebuild testis not a native gate yet because theDNSChatscheme has no XCTest bundles.- DNS transport is observable. Public copy and tests intentionally avoid claiming that DNS prompts are private or end-to-end encrypted.
- DNS is observable infrastructure. Do not send secrets or personal data.
- DNS servers are validated/whitelisted (see
modules/dns-native/constants.ts). - Local chat/log payloads are encrypted at rest; Android backup/device-transfer rules exclude SecureStore key material.
- Store submission credentials are not committed. Keep
eas submit/App Store Connect identifiers local (do not add them toeas.json). - Public release docs use placeholders for local/device/account identifiers.
Run
bun run verify:public-redactionbefore committing release notes or store runbooks. Exact release evidence belongs in private notes outside git.
See CONTRIBUTING.md for development setup and guidelines.
Report security vulnerabilities via SECURITY.md.
MIT. See LICENSE.