우리 가족 앱은 대부분 엔진이야. 자기 포트를 가진 서버 프로세스가 있고, 그걸 살려두는 launchd 작업이 있고, 자기 데이터베이스가 있어. Kiln도 포트 하나에서 답하고, Bellows도 자기 포트가 있지. Pathfinder엔 그게 하나도 없어. 이 레포의 하우스 사인 표에 한 줄로 적혀 있어. Port / service: none, no engine. Pathfinder가 태어날 때 아빠가 정한 거야. 앱만 있고, 위치 저장소는 피파 걸 같이 쓴다.
미니멀리즘 취향이 아니야. Pathfinder가 서버를 둬야 할 이유는 전부 cwkPippa에 이미 주인이 있어. 주인이 둘이 되는 순간, 아빠가 어디 있는지에 대한 진실도 둘이 되기 시작해.
대신 기대는 곳
위치 레이어. 측위는 전부 보고자 pathfinder로 거기 들어가고, 옆에 내비 보고가 붙어. 피파가 고른 목적지는 같은 길로 거꾸로 내려와. 핸드오프 방식이라, 폰이 받고 나서 받았다고 답해. 아빠의 집이랑 즐겨찾기도 피파의 목록이야. 그래서 피파가 "집"을 알고, 아빠를 거기로 보낼 수 있어. 폰은 사본을 들고 있어서 네트워크 없이도 목록이 열려.
지도 문. 카카오 검색, 경로, 소요 시간은 REST 키가 있는 cwkPippa를 거쳐. Apple 스택의 한글 장소 검색은 피파의 Apple Maps Server API 문을 거치고. 서버 키는 앱에 하나도 안 실려.
핀. 피파가 채팅에서 추천한 장소는 피파 위치 레이어에 남아. 폰은 복제본을 들고 그걸 그려.
대화. 대화는 평범한 cwkPippa 대화야. origin_surface: pathfinder로 태어나서 두 시간 동안 이어져. 한 번 운전하는 동안의 대화가 한 스레드가 되는 거지. Pathfinder가 직접 모델이랑 말하는 일은 없어. 두뇌는 cwkPippa 거야.
피파 목소리. 안내 문장은 cwkBellows에서 조각조각 받아서 폰 캐시에 쌓아.
클라이언트도 자격 증명은 들고 있어야 해서, 명시해 둔 예외가 딱 하나 있어. 원래 형제 앱 폰은 피파 세션을 직접 들고 있지 않아. 근데 Pathfinder엔 세션을 대신 맡아줄 엔진이 없잖아. 그래서 폰이 킷의 PippaDoor로 세션을 직접 들고 있어. 가족 PIN은 Keychain에, 세션은 가족 인증 서버가 발급하고. Tidings, Rekindle 폰이랑 같은 모양이야. 슬쩍 끼워 넣은 게 아니라 예외라고 적어두고 쓰는 거야.
클라이언트로 묶어두는 불변식
피파는 주행 소식을 위치 레이어로만 들어. 옆길도 없고, 두 번째 저장소도 없어.
안내는 순수한 값이야.PathfinderKit 타깃엔 MapKit도, Core Location도, 오디오도, 네트워크도 없어. 그래서 아빠가 뭘 들을지 정하는 규칙은 전부 Mac에서 테스트로 붙잡혀 있어.
자체 교통 데이터는 없어. 지도 스택마다 자기가 가진 걸 가져와.
위치 권한은 When In Use만. Always는 절대 안 써. 백그라운드 측정은 아빠가 직접 시작한 안내 구간 동안만이야.
화면에 숫자 안 띄워. 좌표는 피파랑 저장소로만 가고, 화면엔 지도가 위치를 보여줘.
보고는 버리지, 쌓아두지 않아. 늦게 온 위치는 지금 위치가 아니니까.
다르게 망가지는 건 떼어 놓고, 자원 하나를 같이 써야 하는 건 한데 모아. 내비는 피파 앱에서 나왔어. 한쪽이 죽거나 빌드가 꼬여도 다른 쪽까지 끌고 가지 않게. 대화는 내비 앱 안으로 들어왔어. 둘 다 같은 스피커, 같은 마이크가 필요한데, 그걸 심판 볼 수 있는 건 앱 하나뿐이거든.
그럼 왜 피파 앱 안에 안 넣었어?
첫 번째 결정은 안정성이었어. 내비는 자기 앱에서 산다. 피파 앱의 보이스 모드가 흔들리지 않게. 내비는 덩치 크고 빨리 바뀌는 코드야. 아빠가 하루 종일 쓰는 대화랑 같이 죽으면 안 돼.
하루 뒤에 나온 두 번째 결정은 대화 쪽에선 방향이 반대였어. 이유는 오디오 경로야. 아빠는 안내를 받는 동안 피파랑 얘기하고 싶어 했어. 이걸 앱 두 개로 그려봐. 피파 앱은 피파 목소리를 틀고 자기 마이크로 들어. 내비 앱은 다른 프로세스에서 "300미터 앞에서 우회전"을 말해. 피파 앱이 잡아 둔 적 없는 순간에. 시스템 에코 캔슬링이 그 소리를 얼마나 지워 주든, 피파 앱은 회전 안내가 온다는 걸 몰라. 그래서 안내 위에 말을 덮어 버리고, 안내가 마이크에 조금이라도 들어가면 아빠가 끼어든 걸로 알아들어.
앱 하나 안이면 심판이 있어. Pathfinder의 안내는 대화 엔진이 아니라 자기 플레이어로 나가. 그러니 대화의 에코 캔슬링이 그 소리를 지워 줄 거라고 기대하지 않아. 대신 대화가 문지기가 돼. 안내 문장이 나오는 동안이랑 그 뒤 0.5초는 귀를 닫아. 급한 문장은 피파 말을 그 자리에서 끊어. 보통 안내는 피파가 하던 구절이 3초 안에 끝날 때만 기다리고, 아니면 역시 끊어. 이 규칙들은 PathfinderKit의 TalkInterplay에 있고 테스트로 묶여 있어. 아키텍처 문서는 이걸 한 문장으로 말해. 길이 피파 앱으로 가는 게 아니라, 대화가 길로 온다.
그러니까 경계는 "피파는 밖, 내비는 안"이 아니야. 피파의 정체성, 기억, 두뇌는 밖, cwkPippa에. 길이랑 차 안 오디오에 닿는 건 전부, 그걸 소유한 앱 하나 안에.
Code
cwkPathfinder, clients/apple/PathfinderMobile/Sources/Pippa/PippaLink.swift: 링크의 계약 (원문 그대로, 7-26행)·swift
/// Pathfinder's two-way link to Pippa's location layer (Dad's ruling,
/// 2026-10-01).
///
/// Out: each fix, as reporter `pathfinder`, with the navigation report beside
/// it, so Pippa answers "am I on the right road?" from the route itself.
/// In: a destination Pippa picked (her `location` tool's `navigate`), taken
/// and acknowledged.
///
/// Pathfinder has no engine, so its phone holds a Pippa session through the
/// kit's `PippaDoor` (the Tidings and Rekindle shape): the PIN in the Keychain,
/// the session minted by the family authority, `X-Pippa-Session` on every call.
actor PippaLink {
static let reporter = "pathfinder"
struct ReportBody: Encodable {
var fix: GeoFix
var reporter: String
var device: String
var navigation: PippaNavigationReport?
}
누가 뭘 소유하나 (이 레슨용으로 그린 도식)·text
Dad's iPhone: Pathfinder (no port, no server) office: cwkPippa
+-------------------------------------------+ +----------------------------------+
| PathfinderKit guidance, voice chunks, | | location layer |
| drive log (pure, tested) | | fixes from every reporter |
| | fix + | navigation report (< 120 s) |
| Pippa link reporter "pathfinder" | ----> | handoffs from her location tool|
| | <---- | saved places, recommended pins |
| Apple child MapKit on the phone | | |
| Kakao child asks the maps door ------> | ----> | maps door: Kakao REST key, |
| | | Apple Maps Server API search |
| Google child Navigation SDK guides | | |
| its own routes | | |
| the talk one conversation, 2 hours | ----> | conversation (origin_surface: |
| PippaDoor PIN in Keychain, session | | pathfinder); the brain is here |
+-------------------------------------------+ +----------------------------------+
|
| chunk text
v
cwkBellows: Pippa's voice, cached on the phone chunk by chunk
만들고 있는 앱이나 서비스 하나를 골라서 들고 있는 상태를 전부 적어봐. 각각 정식 주인이 누군지 이름을 달아. 이 앱인지, 시스템의 다른 무언가인지. 주인은 다른 데 있는데 이 앱도 쓰기 가능한 사본을 들고 있다면, 둘이 어긋날 때 무슨 일이 생기는지 적어. 그다음 다른 프로그램이랑 같이 쓰는 하드웨어나 OS 자원(스피커, 마이크, 카메라, 포트, 파일)을 전부 적고, 각각 누가 심판을 보는지도 적어봐.
Hint
신선도 규칙이 달린 읽기 전용 캐시는 괜찮아. Pathfinder도 저장 장소랑 핀에 하나씩 두고 있어. 문제는 누가 이기는지 규칙 없는 쓰기 가능한 두 번째 사본이야. 공유 자원엔 Pathfinder 대화의 자리를 정한 질문을 던져봐. 두 쪽이 실시간으로 맞춰야 해? 그렇다면 대개 한 프로세스 안에 있어야 해.
Progress
Progress is local-only — sign in to sync across devices.