DNSChat is a React Native Expo 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
- 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.4.9(build94, internal TestFlightv4.4.9-beta1); distribution status is recorded in the TestFlight runbook. - Expo workflow: Expo Router + EAS-compatible native config
- Expo SDK:
57.0.x - React:
19.2.3 - React Native:
0.86.3 - TypeScript:
6.0.x - Hermes: enabled
- New Architecture: enabled by default on SDK 57
- 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.
DNS packet encoding, DNS-over-TCP framing, decoded-response validation, and TXT
record extraction live in src/services/dnsWire.ts; dnsService.ts owns the
transport chain, retries, logging, and server fallback orchestration.
Prereqs:
- Node.js 22.13+ or 24 LTS (use the
.node-versionpin, currently24; React Native and Metro require Node 22.13+ on the Node 22 line, and Node 26 is outside the supported engine range) - iOS: Xcode 26.4+ (macOS only), iOS 16.4+ device/simulator
- Android: Java 17 + Android SDK
Install:
git clone <repository-url>
cd dnschat
pnpm installRun:
# Dev server
pnpm run start
# iOS
pnpm run ios
# Android (auto-selects Java 17 when available)
pnpm run android
# Web preview (uses Mock DNS)
pnpm 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 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)
dig @llm.pieter.com "hello" TXT +short
# Full harness (builds scripts/ ts -> js, then runs UDP/TCP transports)
pnpm run dns:harness --message "test message"
pnpm run dns:harness --message "test message" --local-server
# Debug output artifacts
pnpm run dns:harness --message "test" --json-out harness-output.json --raw-out raw-dns.bin# Check or apply deterministic formatting with Oxfmt
pnpm run fmt:check
pnpm run fmt
# Lint (Oxlint correctness + ast-grep structural rules)
pnpm run lint
# Unit tests
pnpm run test
# Native UI QA uses Argent MCP; see docs/agents/development.md.
# Public-doc redaction gate
pnpm run verify:public-redaction
# Keep Expo iOS pods aligned with installed node_modules (iOS)
pnpm run verify:ios-pods
# Sanity checks for Android tooling/device expectations
pnpm run verify:android
# Full verification gate before committing/release work
pnpm run verify:all
# Sync app + native module versions (use :dry to preview)
pnpm run sync-versions
pnpm run sync-versions:dry
# CocoaPods cleanup helpers
pnpm run fix-pods
pnpm run clean-iosThis repo installs a pre-commit hook that blocks commits when formatting, lint, pods, or unit tests fail.
Mechanism:
pnpm installrunspnpm run preparepreparerunsscripts/install-git-hooks.js- that script resolves Git's hooks path (including linked worktrees and
core.hooksPath) and writes an executable pre-commit hook that runsverify:ios-pods,fmt:check,lint(Oxlint + ast-grep), and unit tests
Reinstall the hook with pnpm run prepare. Run pnpm run verify:all before
committing; the hook is the shorter safety net, not the entire release gate.
Start here:
docs/README.md— full documentation indexdocs/INSTALL.md— setup, build, verificationdocs/agents/development.md— agent setup, worktrees, debug access and verification loopsSeptember audit plan— findings, alternatives and acceptance criteriaCLAUDE.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
The 4.4.9 beta preserves ciphertext when its device-only key is missing,
rejects ambiguous DNS wire names, and removes Android's unowned legacy lookup.
The harness uses the app's response validation. Unsaved sends retain their
drafts and settings failures show localized errors. The dependency lockfile
contains security updates and a hardware-capability patch for Expo Haptics;
refresh dependencies and rebuild native code to apply it. For this candidate,
the frozen installation and full gate pass: 755 application and 68 native tests.
The signed iOS Release build passes. A prior candidate installed in place;
final SDK hardware verification needs exclusive device access. TestFlight reports
VALID and IN_BETA_TESTING, with strict validation at zero errors/warnings and
bilingual test notes. Production prerequisites remain unresolved.
The 4.4.8 gate (pnpm run verify:all, Node 24) passed 707 root tests and 67
native tests, with react-doctor at 92, Expo Doctor 20/20, a clean dependency
audit and no leaks found. Release builds were run on an iOS 27 simulator
(stable Xcode 27.0) and an Android 16 emulator. Both completed onboarding and
got a real DNS reply over the native transport; iOS rendered accented text,
kept its encrypted history across a cold restart and showed 4.4.8 (93) in
About. The compiled iOS Info.plist carries no local-networking exception.
Earlier releases hold the fuller end-to-end record; see the
changelog.
Release builds use xcodebuild archive, -exportArchive, and
asc publish testflight. The TestFlight runbook
records the latest uploaded binary separately from source and simulator proof.
The App Store production version observed on 2026-09-05 is 4.0.23;
the existing submission draft has an expired build and incomplete age-rating
fields. Provider privacy declarations and unsigned-response risk acceptance
remain production prerequisites. No hardware performance improvement is claimed.
The paragraphs below are release history.
Version 4.3.6 build 84 is the previous TestFlight release, processed
VALID on 2026-08-31 with bilingual notes and a physical Release install.
Version 4.2.3 build 77 is the previous TestFlight release. It carries
the iOS 27 UIScene startup fix proven on a physical device in build 76;
final pre-archive gates passed on 2026-07-10 (Jest 983, native DNS 65, React
Compiler 101/101, Expo Doctor 19/19, audit and secret scan clean). Its signed
archive and IPA export succeeded, App Store Connect processed it VALID, and
strict TestFlight validation completed cleanly. Physical-device launch evidence
remains scoped to build 76; build 77 was not separately installed locally.
Version 4.1.2 build 69 is a premium-feel pass on top of 4.1.1: a semantic
palette.isDark signal for the loading-skeleton tint (replacing a hard-coded-hex
check), a new isDark palette flag, removal of unused errorShake dead code, Expo
SDK 56 dependency patch alignment, plus interactive keyboard drag-to-dismiss on the
chat list, a calmer (no-overshoot) input auto-grow spring, and deletion of the dead
GlassTabBar component. It was built into a Development-signed binary that installed
and launched cleanly on a physical iOS device; it was not uploaded to TestFlight.
Version 4.1.3 build 70 is a release-readiness and documentation-integrity pass
on top of 4.1.2 (a structured-review-flagged changelog release-state correction
and a keyboard drag-to-dismiss regression test; no app-behavior change versus
4.1.2). It carries all of the 4.1.2 premium-feel work to TestFlight, since
4.1.2 itself was only Development-signed onto a device and never uploaded.
4.3.6build84SHIPPED to TestFlight —VALIDon2026-08-31after the complete pre-production audit. Signed archive/export, physical Release install/launch, bilingual test notes, strict validation (0errors,0warnings), and tagv4.3.6-beta1are verified. No production submission is claimed because no matching App Store version exists.4.2.3build77SHIPPED to TestFlight —VALIDon2026-07-10for the iOS 27 scene-lifecycle startup fix. Signed archive/export succeeded, bilingual test notes are present, and strict validation reported0errors and0warnings. Build76supplied the separate physical-device launch proof; build77was not separately installed locally.4.2.0build73SHIPPED to TestFlight —VALIDon2026-07-04(bilingual "What to Test" notes,asc validate testflight --strict0/0/0); supersedes4.1.3build70. Local gates before archive on2026-07-04(iOS 26 HIG redesign + DNS correctness hardening + SDK 57 patch alignment): typecheck, ast-grep lint, react-compiler healthcheck (103/103), Jest (121suites passed,1skipped,964tests passed,13skipped), dns-native workspace tests (8suites,65tests passed), DNSResolver sync, iOS pods sync, SDK alignment, Expo Doctor (19/19), typed-routes, public-redaction, and security (pnpm auditNo vulnerabilities found;gitleaksno leaks found) all passed. The backend/transport work was implemented via a dispatchedcodexrun and reviewed in the main session; native runtime, Android native gates, and TestFlight upload remain separate claims not covered by these gates.4.1.5build72SDK 57 gates on2026-06-30:pnpm run verify:allpassed; Expo Doctor reported19/19;pnpm auditreportedNo vulnerabilities found;gitleaks detectreportedno leaks found; Jest reported122suites passed,1skipped,959tests passed, and13skipped.- Native DNS module tests pass on
2026-06-30(8suites passed,1skipped;64tests passed,13skipped). - Xcode
26.6(17F113) local iOS evidence on2026-06-30: Debug simulator build passed, unsigned generic Release build passed, and unsigned generic Release archive passed. - Runtime smoke on
2026-06-30: the compiled simulator app installed and launched with Argent after clearing a stale Metro process from another repo; accessibility and React component discovery showed the DNSChat onboarding screen. Argent screenshot/gesture backend failed withsimulator-server exited with code before becoming ready; that is a tooling gap, not an app launch failure. - TestFlight, App Store version attachment, and physical-device install remain
separate evidence claims for
4.1.5build72and must not be inferred from the local simulator/archive checks. 4.1.0build67(a staging milestone; later superseded by4.1.1build68as the latestVALIDTestFlight build); the build number was advanced from4.0.32build66. Build67completedVALIDon2026-06-22using the GM Xcode26.6(17F109) toolchain via the archive -> export ->asc publish testflightlane (build66was alsoVALID). Internal App Store Connect IDs and tester group names are intentionally omitted from public docs.- Compiled Release simulator runtime evidence on
2026-06-22for4.1.0build67: the app launches cleanly (noErrorBoundary) and the new adaptive launcher icon renders on the Home Screen, verified via Argent MCP (describegesture-tap+ Home-screen capture). The in-app DNS-message path was not exercised on this build because it was compiled unsigned (CODE_SIGNING_ALLOWED=NO), which makesexpo-secure-store's keychain unavailable ("Encryption key is unavailable"); DNS end-to-end is covered by the Node harness (real TXT answer) and by the TestFlight device build.
- Historical
asc validate testflightevidence is superseded by each uploaded build validation. App Store submission validation is not applicable until a matching App Store version record exists. 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. Native builds keep encryption key material in SecureStore, and Android backup/device-transfer rules exclude that SecureStore data. Web preview uses same-origin browser storage for the local-only preview key because SecureStore is not available in browsers; do not treat Web preview storage as a secure production at-rest boundary.
- 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
pnpm 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.