TRENDSHIFT DAILY #10 · 2026-07-25 · BITCHAT · 인터넷 없이 도는 블루투스 메시 채팅

permissionlesstech/bitchat 딥다이브
— 계정도, 전화번호도, 서버도 없이 주머니 속 기기들끼리 릴레이로 잇는 암호화 메시 채팅

bitchat탈중앙 P2P 메시징 앱(decentralized peer-to-peer messaging)이다. 한 문장으로 말하면 "가까운 사람끼리는 블루투스로 직접, 멀리 있는 사람과는 Nostr(노스터)라는 열린 인터넷 프로토콜로 이어주되 — 계정·전화번호·중앙 서버가 하나도 없는 채팅앱"이다. 인터넷이 끊겨도, 심지어 통신망 자체가 죽어도 근처 기기들끼리 신호를 바통 넘기듯 릴레이해 메시지를 전한다.

이 프로젝트의 급소는 이것이다: "메시지를 나르는 인프라"를 참여자 기기 그 자체로 대체한다. 기지국도, 채팅 서버도, 로그인도 없다. 각 폰이 동시에 수신기이자 중계기가 되어, 옆 사람 → 그 옆 사람으로 최대 7홉까지 암호문을 실어 나른다. 여기에 Noise 프로토콜(Noise Protocol) 종단간 암호화, Tor 경유 옵션, 그리고 지금 당장 받을 사람이 없어도 나중에 전해지게 하는 "택배원(courier)" 저장-전달 시스템까지 얹혀 있다.

(저장소 permissionlesstech/bitchat · 언어 Swift 5.9(iOS 16+ / macOS 13+) · 소스 ~62,000줄·200+ 모듈 · 테스트 181개 파일 · 이중 전송 BLE 메시 + Nostr · 암호화 Noise XX/X · secp256k1 · Tor Arti(Rust) 내장 · ★ 27.1k · fork 3.3k · 라이선스 Unlicense(퍼블릭 도메인) · 백서 v2.0(2026-07-06) · shallow clone 소스 직접 분석)
목차
  1. 프로젝트 한 줄 요약
  2. 왜 주목받는가 — 인프라 없는 통신
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한 줄 요약

"인터넷이 없어도 대화가 끊기지 않는다"를 실제로 구현한 채팅앱

우리가 쓰는 모든 메신저 — 카카오톡, 텔레그램, 시그널 — 는 공통점이 하나 있다. 중앙 서버를 반드시 거친다는 것. 내 메시지는 항상 어느 회사의 데이터센터를 통과해 상대에게 간다. 그래서 인터넷이 끊기면, 통신사가 막으면, 정부가 차단하면 — 대화도 멈춘다. bitchat은 이 전제를 통째로 뒤집는다: 메시지가 서버를 거치지 않고, 옆에 있는 기기들을 징검다리 삼아 흘러간다.

한 줄 비유

콘서트장에서 "이 쪽지 저 앞사람한테 좀 전해주세요" 하고 사람 손을 거쳐 앞줄까지 보내는 것

보통 메신저는 "우체국에 편지를 맡기는 것"이다 — 중앙(우체국)이 없으면 아무것도 못 보낸다. bitchat은 "손에서 손으로 쪽지를 넘기는 것"이다. 내 폰이 옆 폰에게, 그 폰이 또 다음 폰에게 — 참여한 기기들 자체가 배달망이 된다. 쪽지는 봉인(암호화)돼 있어 중간에서 전달해 준 사람들은 내용을 못 읽고, 받을 사람에게 닿으면 그 사람만 열 수 있다.

bitchat이 하는 일을 순서로 풀면 이렇다. ① 근처 기기를 블루투스로 자동 발견하고 → ② 서로 암호화 세션을 맺은 뒤 → ③ 메시지를 봉인해 controlled flood(통제된 확산)로 뿌리면 → ④ 중간 기기들이 내용을 못 본 채 릴레이하고 → ⑤ 인터넷이 있으면 멀리 있는 사람에겐 Nostr 릴레이로 다리를 놓아준다. 받을 사람이 지금 없으면? 누군가의 폰에 봉인된 채 실려 다니다가 나중에 만나면 전해진다.

TERM
메시 네트워크 (mesh network)
중앙 장비(공유기·기지국) 없이, 참여 기기들이 서로 직접 연결되어 서로의 신호를 대신 중계해 주는 그물망 구조. bitchat에서는 각 폰이 노드(node)이자 릴레이가 된다. 한 노드가 사라져도 다른 경로로 우회되므로 끊기기 어렵다(resilient). 재난·시위·오지처럼 인프라가 없는 곳에서 진가를 발휘한다.

이름의 유래도 성격을 드러낸다. permissionlesstech — "허가가 필요 없는 기술". 누구의 승인도, 가입도, 신원 확인도 없이 그냥 앱을 켜면 곧바로 통신망의 일원이 된다. README의 표현을 빌리면 "the side-groupchat" — 공식 채널 옆에서 조용히 도는 곁가지 단톡방이다.

2왜 주목받는가

"인프라가 사라진 순간에도 작동하는 통신"이라는 희소가치

bitchat은 2025년 7월 공개된 뒤 ★27,000+를 모았고, 2026년 7월 백서 v2.0이 나오며 다시 트렌딩에 올랐다. 인기의 뿌리는 단순한 기능이 아니라 "이런 게 정말 되는구나"라는 개념 자체의 힘이다. 대부분의 앱이 "더 편한 중앙화"를 향할 때, bitchat은 정반대로 "중앙이 아예 없는" 극단을 실제 동작하는 코드로 증명했다.

차별점 ① — 인터넷 0%에서도 대화가 된다

시그널·텔레그램도 종단간 암호화는 하지만, 인터넷이 있어야만 한다. bitchat의 BLE 메시는 인터넷이 완전히 없어도 동작한다. 통신망이 마비된 재난 지역, 신호가 끊긴 지하·산악, 통신이 차단된 시위 현장 — 이런 "인프라 공백" 시나리오가 정확히 bitchat의 무대다. README가 첫 문장부터 "with or without the internet"을 강조하는 이유다.

차별점 ② — 두 개의 전송로를 상황따라 자동 선택

bitchat의 진짜 영리함은 이중 전송(dual transport)에 있다. 가까우면 블루투스(무료·오프라인), 멀면 Nostr(인터넷 릴레이) — 이 둘을 MessageRouter가 알아서 고른다. 라이브 메시 링크가 있으면 그걸 우선하고, 없으면 Nostr로 폴백하고, 둘 다 안 되면 택배원 시스템에 맡겨 나중을 기약한다. 사용자는 어느 길로 갔는지 신경 쓸 필요가 없다.

전송로범위인터넷쓰임새
BLE 메시블루투스 도달거리 × 최대 7홉불필요오프라인·재난·시위·오지의 근거리 통신
Nostr전 세계(290+ 릴레이)필요멀리 있는 상호 즐겨찾기와의 사설 메시지, 지역 채널
택배원(courier)사람의 물리적 이동불필요지금 못 닿는 상대에게 "나중에" 전달(store-and-forward)

차별점 ③ — 프라이버시가 타협이 아니라 기본값

계정이 없으니 수집할 개인정보 자체가 없다. 신원은 전화번호가 아니라 암호 키(cryptographic key)다. 평문 메시지는 디스크에 절대 기록되지 않고, 저장되는 건 봉인된 암호문이거나 이미 공개된 방송 트래픽뿐이며, 이마저도 패닉 와이프(3번 탭) 한 번에 전부 사라진다. Nostr 트래픽은 Tor로 흘려보낼 수도 있다.

GOOD
왜 이 조합이 사람을 끌어당기는가

재난·검열에 강한 회복탄력성(오프라인 메시), 중앙이 없다는 철학적 매력(계정·서버 zero), 그리고 뜯어볼수록 촘촘한 암호·네트워크 엔지니어링(Noise·Nostr·Tor·DTN) — 이 셋이 한 앱에 들어있다. 게다가 코드는 퍼블릭 도메인(Unlicense)이라 누구나 통째로 베껴 만들어도 된다. "쓸모"와 "낭만"과 "교재"를 동시에 갖춘 드문 프로젝트다.

3기술 스택 전체 지도

순수 네이티브 스위프트 위에 암호·근거리무선·분산 프로토콜을 쌓았다

bitchat은 웹앱이 아니라 iOS·macOS 네이티브 앱이다. 그래서 스택의 밑바닥이 자바스크립트가 아니라 Swift + Apple 프레임워크다. 화면(SwiftUI) 위에, 근거리무선(CoreBluetooth), 암호(Noise·secp256k1), 인터넷 분산 프로토콜(Nostr), 익명 라우팅(Tor)이 층층이 얹힌 구조다.

클라이언트 본체 — Swift / SwiftUI

레이어기술역할
언어/런타임Swift 5.9 (iOS 16+ / macOS 13+)단일 코드베이스로 두 플랫폼 동시 지원(Universal App)
UISwiftUI + Combine선언형 화면 · ViewModels/의 30여 코디네이터가 상태 관리
근거리무선CoreBluetooth (BLE)각 기기가 동시에 GATT central+peripheral — 스캔·광고·릴레이
키 보관KeychainCurve25519 정적키 · Ed25519 서명키를 안전 저장
패키징Swift Package ManagerPackage.swift · 로컬 패키지 3개 + 외부 1개

암호화 — Noise 프로토콜 + secp256k1

구성기술역할
라이브 세션Noise XX연결된 두 피어 간 상호인증 + 순방향 비밀성(forward secrecy)
암호 스위트Curve25519 / ChaCha20-Poly1305 / SHA-256키 합의 · 대칭 암호 · 해시 (Noise 표준 조합)
오프라인 봉인Noise X (일방향)택배 봉투를 수신자 정적키로 봉인(단, 순방향 비밀성 없음)
Nostr 키swift-secp256k1 (0.21.1)Nostr 신원(secp256k1 키쌍) · NIP-17 암호화
TERM
Noise 프로토콜 (Noise Protocol Framework)
종단간 암호화 세션을 만드는 "레시피 모음". 시그널 메신저의 기반이기도 하다. XX, X 같은 패턴 이름이 "누가 먼저 키를 보여주고, 몇 번 주고받는지"를 정한다. XX는 양쪽이 서로를 인증하는 대화형 핸드셰이크(라이브 연결용), X는 상대가 없어도 상대의 공개키만으로 봉인하는 일방향(택배용)이다.

인터넷 전송 — Nostr + Tor(Arti)

구성기술역할
분산 릴레이Nostr (NIP-17 / NIP-59)290+ 공개 릴레이로 사설 메시지·지역 채널 전송
메시지 은닉Gift-wrap (kind 1059)rumor→seal→gift-wrap 3중 포장 — 릴레이도 발신자·내용 모름
위치 채널Geohash지오해시 좌표로 "이 동네" 공개 채팅방을 구성
익명 라우팅Arti (Rust Tor)Nostr 트래픽을 Tor로 흘려 메타데이터까지 은닉 · localPackages/Arti
TERM
Nostr (Notes and Other Stuff Transmitted by Relays)
계정 서버가 없는 열린 소셜 프로토콜. 사용자는 서버에 가입하는 게 아니라 키쌍(secp256k1)이 곧 신원이고, 메시지("이벤트")를 여러 릴레이에 뿌린다. 트위터가 회사 서버에 묶인 반면, Nostr는 누구나 릴레이를 돌릴 수 있어 검열이 어렵다. bitchat은 이걸 "블루투스가 안 닿는 먼 거리용 다리"로 쓴다.
비유

bitchat의 스택은 "교통수단 3종 세트"다. 블루투스 메시는 자전거(가깝고, 연료·인프라 필요 없음), Nostr는 국제선 비행기(멀리 가지만 공항=인터넷이 필요), Tor는 그 비행기의 차창 블라인드(누가 탔는지 안 보이게)다. 그리고 택배원 시스템은 "지나가는 사람 편에 부치는 소포" — 정기편이 없을 때 물리적 이동에 얹어 보낸다.

로컬 패키지 — 관심사 분리

본체와 별도로 localPackages/에 재사용 모듈 3개를 분리해 뒀다. Arti(Rust로 짠 Tor 구현을 .xcframework로 감싼 것 — iOS/macOS 바이너리 포함), BitFoundation(공용 타입·유틸), BitLogger(로깅). 외부 의존성은 사실상 swift-secp256k1 하나뿐이라, 공급망 위험을 최소화한다.

4아키텍처 심화 분석

"어떻게 서버 없이 메시지가 목적지에 닿는가"의 전모

bitchat 아키텍처의 모든 질문은 하나로 모인다: "중앙이 없는데 어떻게 배달이 보장되는가?" 답은 네 겹의 안전망이다. 근거리는 메시가 확산으로 잇고, 원거리는 Nostr가 잇고, 지금 못 닿으면 택배원이 나중에 나르고, 공개 대화는 가십 동기화로 뒤늦게 합류한 사람에게도 복원된다.

전체 그림 — 두 전송로와 라우터

┌───────────────────────────┐ │ MessageRouter │ ← 어느 길로 보낼지 결정 │ "라이브 메시 > Nostr > 택배"│ └──────┬─────────────┬────────┘ 선호(가깝고 무료) │ │ 폴백(멀 때/오프라인 상대) ▼ ▼ ┌────────────────┐ ┌────────────────────┐ │ BLE 메시 │ │ Nostr │ │ (오프라인) │ │ (인터넷 릴레이) │ │ 각 폰=중계기 │ │ NIP-17 gift-wrap │ │ 최대 7홉 확산 │ │ (Tor 경유 가능) │ └───────┬────────┘ └─────────┬──────────┘ │ 지금 못 닿으면 │ 둘 다 안 되면 ▼ ▼ ┌─────────────────────────────────────┐ │ Store & Forward (저장-전달) │ │ · 발신 아웃박스(재전송) │ │ · 택배원 봉투(사람이 물리적으로 운반)│ │ · 가십 동기화(공개 이력 복원) │ │ · Nostr 메일박스(릴레이에 대기) │ └─────────────────────────────────────┘

핵심은 Transport라는 공통 인터페이스다. BLE와 Nostr가 같은 프로토콜을 구현하고, MessageRouter는 구체적 전송로를 몰라도 "보내라"고만 지시한다. 덕분에 나중에 새 전송로(예: Wi-Fi Aware)를 추가해도 라우터는 안 바뀐다. 소스의 Services/Transport.swift가 이 추상 경계다.

BLE 메시 — controlled flood(통제된 확산)

근거리 배달의 심장이다. 메시지를 받은 노드는 무작정 다시 뿌리지 않는다. 그랬다간 브로드캐스트 폭풍(broadcast storm)으로 배터리와 대역폭이 순식간에 고갈된다. bitchat은 TTL·중복제거·지터·팬아웃 서브셋 네 장치로 확산을 정교하게 조인다.

원본 메시지 (TTL=7) │ ▼ ┌──────────────┐ 이미 본 패킷? ┌──────────────────┐ │ 수신 노드 │ ──── 예 ─────────▶ │ 버림 (dedup) │ LRU 1000개·5분 │ │ └──────────────────┘ (발신자+시각+타입+해시) │ │ 아니오 │ │ │ │ │ ▼ │ │ ┌──────────────────┐ 밀집도로 TTL 클램프 │ │ │ TTL 감소 & 클램프 │ 링크 ≥6 → 방송 TTL 5로 제한 │ │ │ (relay TTL-1) │ 링크 ≤2 → 들어온 깊이 그대로 │ │ └────────┬─────────┘ │ │ ▼ │ │ ┌──────────────────┐ 10~220ms 무작위 대기 │ │ │ 지터(jitter) 후 │ (그 사이 남이 먼저 릴레이하면 │ │ │ 팬아웃 서브셋에 │ 내 예약은 취소 → 중복 최소화) │ │ │ 만 재전송 │ 방송은 log₂(링크수)만큼만 └──────────────┘ └──────────────────┘ 들어온 링크는 항상 제외(split horizon)

이 설계의 묘미는 "모두가 다 뿌리면 낭비, 아무도 안 뿌리면 단절" 사이의 균형이다. 지터를 두면 근처 여러 노드 중 한 대만 먼저 릴레이하고 나머지는 "어, 벌써 갔네" 하고 자기 예약을 취소한다. 방송 메시지는 모든 링크가 아니라 메시지 ID로 결정되는 부분집합(~log₂)에만 보내, 밀집 구간에서 폭증을 막는다. 반면 핸드셰이크·사설 메시지 같은 지정 트래픽은 서브셋 없이 확실히 TTL−1로 전달한다.

TERM
TTL (Time To Live) · 홉(hop)
패킷이 몇 번이나 더 중계될 수 있는지를 담은 숫자. bitchat은 TTL=7로 출발해, 한 번 릴레이될 때마다 1씩 깎는다. 0이 되면 더는 안 퍼진다. "홉"은 중계 한 번을 뜻한다(내 폰→친구 폰=1홉). TTL이 무한이면 메시지가 영원히 돌며 네트워크를 마비시키므로, 확산에 반드시 필요한 브레이크다. 서명은 TTL 바이트를 제외하고 계산해, 릴레이가 TTL을 깎아도 서명이 깨지지 않는다.

암호화 — 릴레이는 봉투만 보고 내용은 못 본다

연결된 두 피어는 Noise XX 핸드셰이크로 세션을 맺는다. 이후 모든 사설 페이로드(메시지·전달확인·읽음확인)는 이 세션 안에서 타입 있는 암호문(typed ciphertext)으로 흐른다. 중간 릴레이 노드가 보는 건 오직 noiseEncrypted라는 불투명한 봉투뿐 — 발신자·수신자·내용 어느 것도 알 수 없다. 게다가 패킷은 256/512/1024/2048바이트 블록으로 패딩되어, 길이만으로 내용을 추측하는 것도 막는다.

Store & Forward — 지금 못 받는 사람에게 "나중에"

이 프로젝트에서 가장 독창적인 부분이다. 수신자가 지금 범위 안에 없을 때, bitchat은 포기하지 않고 네 가지 방법으로 "나중 배달"을 시도한다. 그중 압권이 택배원(courier) 시스템이다.

핵심 메커니즘 · 택배원(courier)

모르는 사람 폰에 봉인된 편지를 실어 보내기

① 불투명 주소지정: 봉투에 적힌 유일한 정보는 16바이트 회전 태그 — "수신자 정적키 + 오늘 날짜(UTC)"의 HMAC이다. 그 키를 이미 아는 사람만 계산할 수 있어, 택배원은 발신자·수신자·내용을 전혀 모르고, 태그는 날마다 바뀌어 추적도 안 된다.

② 신뢰 등급: 상호 즐겨찾기는 5통씩, 서명 검증된 아무 피어는 2통씩 맡길 수 있되 즐겨찾기 몫을 침범 못 하도록 슬롯이 분리돼 있다(봉투 16KiB·24시간 상한).

③ Spray-and-Wait(분무 후 대기): 봉투엔 복사 예산(처음 4, 최대 8)이 있다. 택배원이 다른 적격 택배원을 만나면 남은 예산의 절반을 나눠준다. 그래서 편지가 한 사람이 아니라 움직이는 군중 속으로 확산된다. 앱을 껐다 켜도 예산·이력이 보존된다.

④ 핸드오버: 수신자의 서명된 직접 announce를 보면 즉시 라이브 링크로 배달·삭제하고, 중계된 announce면 사본만 흘려보낸다(봉투당 10분에 1회로 제한).

비유

Spray-and-Wait는 "소문이 퍼지는 방식"과 같다. 한 사람이 100명에게 직접 말하는 대신(비효율·과부하), 몇 명에게만 말하고 그들이 또 절반씩 퍼뜨리면 — 결국 군중 전체에 닿는다. 편지가 특정 배달부 한 명에게 의존하지 않으니, 그 사람이 집에 가버려도 다른 복사본이 목적지로 향한다. "중복은 해롭지 않다"(수신 시 메시지 ID로 중복 제거)는 전제가 이 대담한 확산을 가능케 한다.

가십 동기화(Gossip Sync) — 뒤늦게 온 사람도 대화를 복원

공개 채팅은 또 다른 문제가 있다: 방에 늦게 합류한 사람은 앞 대화를 못 본다. bitchat은 공개 방송 1000개를 캐시해두고, 약 15초마다 이웃과 GCS 필터(Golomb-Coded Set)로 "나는 이걸 갖고 있어 / 너는 이게 없네"를 압축 비교(set reconciliation)한 뒤 빠진 것만 주고받는다. 메시지는 6시간 동안 동기화 가능하고 캐시는 디스크에 남아, 두 무리 사이를 걸어서 오간 기기가 양쪽 대화를 실어 나르는 다리가 된다.

5디렉토리 구조 해부

200여 개 스위프트 파일이 "전송 · 암호 · 동기화 · 화면" 네 덩어리로
bitchat/ ├── bitchat/ ← 앱 본체 (~62,000줄 Swift) │ │ ┌─ [1] 전송 계층 (가장 두껍다) ────────────────────── │ ├── Services/BLE/ (45개) ★블루투스 메시의 전부 │ │ BLEService.swift CoreBluetooth 조율(central+peripheral) │ │ BLEFanoutSelector.swift 방송 서브셋(팬아웃) 결정 │ │ BLEFragmentHandler.swift MTU 초과 패킷 분할/재조립 │ │ BLEConnectionScheduler.swift RSSI 게이팅·듀티사이클(배터리) │ │ BLERouteForwardingPolicy... 소스 라우팅·릴레이 정책 (외 40여 개) │ ├── Services/Transport.swift ★BLE·Nostr 공통 추상 인터페이스 │ ├── Services/MessageRouter.swift ★어느 전송로로 보낼지 결정 │ ├── Services/NostrTransport.swift Nostr 쪽 Transport 구현 │ │ ┌─ [2] 암호 계층 ──────────────────────────────────── │ ├── Noise/ (10개) Noise XX/X 세션·핸드셰이크 │ │ NoiseSession.swift · NoiseSessionManager.swift · Rate/Security… │ ├── Services/NoiseEncryptionService.swift 세션 위 페이로드 암복호 │ ├── Identity/ 정적키(Curve25519)+서명키(Ed25519) │ │ ┌─ [3] 인터넷 전송 (Nostr) ────────────────────────── │ ├── Nostr/ (11개) NostrProtocol·RelayManager·PoW │ │ Bech32.swift · GeoRelayDirectory.swift · NostrIdentity… │ │ ┌─ [4] 저장-전달·동기화 ───────────────────────────── │ ├── Sync/ ( 7개) GossipSyncManager·GCSFilter(집합 조정) │ ├── Services/Courier/ 택배원 봉투(spray-and-wait) │ ├── Services/Board/·Groups/·Prekeys/·Gateway/ 게시판·사설그룹·프리키 │ │ ┌─ [5] 프로토콜 정의 ──────────────────────────────── │ ├── Protocols/ (11개) BitchatProtocol·Packets(바이너리 포맷) │ │ Geohash.swift · LocationChannel.swift · VouchAttestation… │ │ ┌─ [6] 화면·상태 (SwiftUI) ────────────────────────── │ ├── ViewModels/ (30개) Chat*Coordinator 다수(관심사 분리) │ ├── Views/ (16개) ContentView·MessageList·컴포넌트 │ ├── App/ (13개) AppRuntime(구성 루트)·ConversationStore │ ├── Features/voice/ (10개) 푸시투토크 라이브 음성 │ └── Models/ ( 6개) BitchatMessage·BitchatPeer·ReadReceipt ├── localPackages/ ← 분리된 재사용 모듈 │ ├── Arti/ Rust Tor를 .xcframework로(iOS/macOS) │ ├── BitFoundation/ · BitLogger/ 공용 타입 · 로깅 ├── bitchatTests/ (181개 파일) 단위·통합·Noise 테스트벡터 ├── docs/ ARCHITECTURE_V2·SOURCE_ROUTING·TOR-INTEGRATION… ├── WHITEPAPER.md ★프로토콜 백서 v2.0 (필독) ├── Package.swift · Justfile SPM 매니페스트 · macOS 빌드 헬퍼 └── README.md · PRIVACY_POLICY.md 개요 · 프라이버시 정책

구조가 말해주는 이야기는 분명하다. 전송 계층, 그중에서도 Services/BLE/가 압도적으로 두껍다(45개 파일). 이는 이 프로젝트의 진짜 난이도가 "채팅 UI"가 아니라 "불안정한 블루투스 위에서 배달을 보장하는 것"에 있음을 보여준다. 파일 하나하나가 BLEFanoutSelector, BLEScanDutyPolicy, BLESourceRouteFailureCache처럼 단일 책임(single responsibility)으로 잘게 쪼개져 있어, 각 정책을 독립적으로 테스트할 수 있다.

비유

이 코드베이스는 "항공 관제탑"을 닮았다. 승객이 보는 건 좌석과 안내방송(SwiftUI 화면)뿐이지만, 뒤에는 이착륙 순서·항로·연료·기상·충돌회피를 각각 담당하는 수십 개의 관제 규칙이 돌아간다. bitchat의 BLE/ 폴더가 바로 그 관제 규칙 뭉치다 — 언제 스캔하고(배터리), 누구에게 릴레이하고(팬아웃), 중복을 어떻게 거르고(dedup), 경로가 끊기면 어떻게 우회할지를 파일별로 나눠 맡는다.

6학습 포인트

이 레포 하나에 근거리무선·암호·분산시스템이 다 들어있다

배울 것 ① — 메시 네트워킹과 확산 제어

"그냥 다 뿌리면 되지 않나?"라는 순진한 생각이 왜 안 되는지를 Services/BLE/가 실물로 가르쳐준다. TTL 클램프, LRU 중복제거, 지터 기반 억제, 팬아웃 서브셋, split horizon — 이건 사실 인터넷 라우팅 프로토콜(OSPF·BGP)이 쓰는 개념을 블루투스 스케일로 축소한 것이다. 분산 시스템의 고전 문제를 손에 잡히는 코드로 볼 수 있는 드문 기회다.

배울 것 ② — Noise 프로토콜로 보는 실전 암호화

"종단간 암호화"를 말로만 듣다가, Noise/에서 XX 패턴 핸드셰이크가 실제로 몇 번 메시지를 주고받는지를 코드로 본다. 순방향 비밀성(forward secrecy)이 왜 라이브 세션엔 있고 오프라인 봉인(X 패턴)엔 없는지, 그 트레이드오프가 왜 생기는지까지 백서가 정직하게 설명한다. 시그널·와이어가드가 쓰는 바로 그 계열이다.

배울 것 ③ — DTN(지연 감내 네트워킹)의 정수

택배원 시스템은 DTN(Delay-Tolerant Networking)이라는 학문 분야의 실전 구현이다. Spray-and-Wait는 우주 통신·재난망 연구에서 나온 실제 알고리즘이고, bitchat은 여기에 "불투명 태그로 프라이버시 보장"이라는 현실적 장치를 더했다. "연결이 간헐적일 때 어떻게 배달을 보장하나"라는 질문의 모범 답안이다.

배울 것 ④ — 대형 SwiftUI 앱의 구조화

ViewModels/Chat*Coordinator가 20개 넘게 있는 걸 보고 놀랄 수 있다. 이건 거대해진 ChatViewModel기능별 코디네이터로 쪼개는 리팩터링의 흔적이다(docs/ARCHITECTURE_V2.md에 그 의도가 상세히 적혀 있다). "한 파일에 다 넣으면 안 되는 이유"와 "점진적으로 쪼개는 방법"을 실제 진행형 프로젝트에서 관찰할 수 있다.

주의
"오프라인이라 무조건 안전"은 아니다 — 한계를 알고 쓰기

bitchat은 강력하지만 만능이 아니다. 백서가 스스로 밝히듯, 택배 봉인(Noise X)엔 순방향 비밀성이 없어 수신자 정적키가 털리면 미배달 봉투가 노출된다. 또 블루투스 근접 자체는 관찰 가능하다 — 누가 근처에 있었는지의 메타데이터는 전파 특성상 완전히 숨길 수 없다. 실제 고위험 상황(내부고발·활동가)에서 생명을 걸 도구로 쓰기 전엔 이 한계와 위협 모델(docs/privacy-assessment.md)을 반드시 이해해야 한다.

실습 아이디어(맛보기)

7하드웨어 / 시스템 요구사항

특별한 장비는 없다 — 다만 "진짜 블루투스"는 실기기가 필요하다
항목요구사항
플랫폼iOS 16+ / macOS 13+ (Universal App · 하나의 코드베이스)
빌드 도구Xcode (전체 설치 필요) · macOS는 just run으로 소스 실행
언어Swift 5.9 · Swift Package Manager(Package.swift)
무선BLE(Bluetooth Low Energy) 지원 기기 — 메시는 실기기 권장(시뮬레이터는 BT 제한)
설치(사용자)iOS App Store("bitchat mesh") · 소스 빌드 시 개발자 Team ID 필요
서명 설정Configs/Local.xcconfig에 Team ID 기입 · Bundle ID chat.bitchat.<team_id>
인터넷(선택)Nostr·Tor·지역채널 사용 시에만 · BLE 메시만 쓰면 완전 오프라인
저장평문 미저장 · 봉인 암호문·공개이력만 · Keychain에 키 보관 · 패닉 와이프로 전삭제

요구사항이 "평범한 iPhone/Mac"이라는 게 오히려 강점이다. 특수 무전기도, 유심도 필요 없이 이미 모두 주머니에 넣고 다니는 기기가 그대로 메시 노드가 된다. 재난 시 "기지국이 죽었는데 통신 장비가 따로 없다"는 상황을 정확히 겨냥한 설계다. 다만 배터리는 유한하므로, BLEScanDutyPolicy가 스캔을 듀티사이클로 조절해 소모를 억제한다.

8직접 해볼 수 있는 실습 과제

난이도별 5개 — 읽기만 말고 메시를 직접 띄워 보기
난이도 ★ · 입문

1. 백서 한 장으로 "배달 4중 안전망" 그리기

WHITEPAPER.md의 6장(Store and Forward)을 읽고, 아웃박스 · 택배원 · 가십동기화 · Nostr 메일박스가 각각 "어떤 상황"을 커버하는지 한 장짜리 표로 정리한다. 코드 없이도 "왜 하나로는 부족하고 넷이 필요한지"가 보인다. 시스템 설계 감각을 기르는 최고의 워밍업.

난이도 ★★ · 초급

2. 두 기기로 오프라인 메시 띄우기

macOS에서 just run, iOS는 시뮬레이터/실기기로 빌드해 실행한다. 그런 다음 Wi-Fi·셀룰러를 끄고 블루투스만 켠 채 메시지가 오가는지 확인한다. IRC 스타일 명령(/who, /msg, /slap)도 쳐본다. "서버 없는 채팅"을 처음으로 눈앞에서 목격하는 경험.

난이도 ★★★ · 중급

3. 확산 파라미터를 바꿔 네트워크 거동 관찰

Services/BLE/에서 TTL 클램프·지터 범위·팬아웃 서브셋 크기가 정의된 정책 파일을 찾아, 값을 조금 바꿔본다(예: 지터를 없애면?). 중복 릴레이가 얼마나 늘어나는지를 로그로 관찰하며 "왜 원래 값이 그렇게 정해졌는지"를 역설계한다. 파라미터 하나가 전체 거동을 바꾸는 분산 시스템의 민감성을 체감.

난이도 ★★★★ · 중상급

4. 바이너리 패킷 포맷 파서 만들기

Protocols/Packets.swift·BitchatProtocol.swift의 헤더 구조(버전·타입·TTL·타임스탬프·플래그 + 발신자ID + 페이로드 + 서명)를 읽고, 패킷 바이트를 사람이 읽는 표로 디코드하는 작은 파서를 Swift/Python으로 짠다. 네트워크 프로토콜이 "왜 텍스트 대신 촘촘한 바이너리를 쓰는지"(BLE MTU 제약)를 손으로 이해한다.

난이도 ★★★★★ · 고급

5. Spray-and-Wait를 시뮬레이터로 재현

택배원의 복사 예산(4→8, 만날 때마다 절반 분배) 규칙만 떼어내, 랜덤 워크하는 N개 노드의 만남을 이산 이벤트로 시뮬레이션한다(Python + 간단한 이동 모델). "복사 예산을 늘리면 배달률·오버헤드가 어떻게 변하나"를 그래프로 그려, bitchat이 왜 상한 8을 뒀는지 정량적으로 검증한다. 논문 속 DTN 알고리즘을 내 손으로 실험하는 단계.

9관련 기술 심화 학습 로드맵

bitchat을 완전히 소화하기 위한 4주 코스
주차주제학습 내용
1주차BLE와 근거리무선Bluetooth LE의 GATT·central/peripheral 개념 / CoreBluetooth 기본 / 왜 MTU가 작아 분할이 필요한지 / RSSI·듀티사이클과 배터리. 대응 소스: Services/BLE/
2주차메시 라우팅·확산 제어controlled flood / TTL·홉 / 중복제거(LRU) / split horizon / 소스 라우팅 / 집합 조정(GCS filter). 개념 뿌리: OSPF·에피데믹 라우팅. 대응: Sync/·docs/SOURCE_ROUTING.md
3주차실전 암호학Noise 프로토콜 프레임워크(XX vs X) / Curve25519·ChaCha20-Poly1305 / 순방향 비밀성 / 서명(Ed25519)과 신원. 대응: Noise/·Identity/·백서 5장
4주차분산 소셜·익명 전송Nostr 프로토콜(이벤트·릴레이·NIP-17/59 gift-wrap) / geohash 위치 채널 / Tor·Arti 기초 / DTN·Spray-and-Wait. 대응: Nostr/·docs/TOR-INTEGRATION.md·백서 6장

이 순서를 밟으면 bitchat은 단순한 "신기한 앱"이 아니라 근거리무선·라우팅·암호·분산시스템을 잇는 살아있는 교과서가 된다. 각 주차 개념이 정확히 어느 소스 폴더·백서 절에 대응되므로, "이론 → 실제 코드 → 백서의 설계 근거"를 삼각 대조하며 왕복 학습할 수 있다.

10핵심 키워드 사전

이 레포를 읽을 때 걸려 넘어지는 용어를 한 번에
용어의미
메시 네트워크중앙 장비 없이 참여 기기끼리 서로 중계하는 그물망. bitchat의 근간
BLE (Bluetooth LE)저전력 블루투스. 각 폰이 central+peripheral로 동시 동작하며 릴레이
controlled flood메시지를 통제된 방식으로 확산. TTL·dedup·지터·팬아웃으로 폭주 억제
TTL / 홉(hop)패킷이 더 중계될 수 있는 횟수(7에서 시작, 릴레이마다 −1) / 중계 1회
Noise XX / X종단간 암호 세션 패턴. XX=상호인증 라이브용, X=일방향 오프라인 봉인용
순방향 비밀성과거 세션키가 나중에 털려도 옛 메시지는 못 여는 성질. 라이브엔 있고 봉인엔 없음
정적키 / 지문(fingerprint)Curve25519 장기 키 / 그 SHA-256 해시가 피어의 안정적 신원
Nostr계정 서버 없는 분산 소셜 프로토콜. 키가 곧 신원, 메시지를 릴레이에 뿌림
gift-wrap (NIP-17/59)Nostr 메시지를 rumor→seal→wrap 3중 포장. 릴레이가 발신자·내용 모름
geohash위경도를 짧은 문자열로 인코딩. 정밀도에 따라 블록~국가 단위 위치 채널 구성
Tor / Arti익명 라우팅망 / 그 Rust 구현. Nostr 트래픽의 메타데이터까지 은닉
store-and-forward지금 못 닿는 상대에게 "보관했다 나중에" 전달하는 방식(4중 안전망)
courier(택배원)봉인된 봉투를 물리적으로 실어 나르는 중간 피어. 내용·상대 모름
Spray-and-Wait복사 예산을 만날 때마다 절반씩 나눠 군중으로 확산하는 DTN 배달 알고리즘
회전 태그(rotating tag)수신자키+날짜의 HMAC(16바이트). 아는 사람만 계산, 날마다 바뀌어 추적 불가
가십 동기화 / GCS filter이웃과 "가진 것/없는 것"을 압축 비교해 빠진 메시지만 교환(집합 조정)
패닉 와이프(panic wipe)3번 탭으로 키·즐겨찾기·봉투·이력·통계를 즉시 전삭제하는 비상 소거

11참고 링크

원문으로 더 파고들기