원문 캡처 · sidhanthp.pages.dev
원문 캡처 · sidhanthp.pages.dev

서버가 동기화 내용을 저장한 뒤 맥이 확인 응답을 기록하기 전에 실패해도, 다음 실행에서 같은 데이터를 다시 보내 중복 반영을 막도록 시험했다. 예약 실행 업로더가 항목 수를 바꾸지 않고 복구하는 것을 확인했다.

나는 맥의 iMessage 기록을 사설 보관소에 올리고 Muse가 검색하도록 구성했다. 맥을 닫아 둔 동안에도 과거 기록을 조회할 수 있다. 이 기록으로 일정 관리와 할 일 목록 갱신, 약속과 계획 기억을 돕게 한다.

이 구성은 어떻게 작동하나?

Muse는 내 메시지를 대신 찾아 주는 사서에 빗대면 이해하기 쉽다. 맥이 깨어 있고 온라인일 때 5분마다 메시지를 사설 Railway 보관소에 올리고, Muse는 클라우드 가상 머신에서 그 기록을 검색한다. 새 메시지는 10분마다 확인하며, 맥이 다시 연결되면 빠진 내용이 따라잡힌다.

Go 서비스에는 Tailscale의 tsnet이 들어간다. 이를 쓰면 Railway 컨테이너가 커널 VPN 인터페이스 없이 사설 네트워크에 참여할 수 있다. 네트워크 권한과 노드 신원 검사를 통해 Muse에는 읽기 전용 권한을, 맥에는 인증된 업로드 권한을 분리해 부여한다.

SQLCipher는 보관소와 검색 색인을 암호화한다. 암호화 키는 데이터 볼륨 바깥에 둔다. 실행 중인 서비스는 데이터를 복호화할 수 있으며, 검색해 가져온 발췌문은 메타에 전달된다. 이름은 메시지에 이미 등장한 연락처 정보를 맥의 연락처 앱에서 찾아 붙인다. Muse에는 검색 방식과 최신성 확인법을 설명하는 지속형 스킬을 설정했다.

능동적인 제안은 작동하지만, 사람이 지켜보지 않는 상태에서 캘린더와 Todoist에 내용을 쓰는 기능은 여전히 백그라운드 승인 규칙에 달려 있다.

직접 구성하려면 무엇을 준비하나?

이 글과 맥, 호스팅 계정에 접근할 수 있는 에이전트에게 먼저 아키텍처와 비용을 제안하게 한다. 기록을 올리기 전에 합성 데이터로 시험하고, 소스 코드와 설치 안내, 복구 테스트를 함께 마련하도록 요청한다. 설정은 수동으로 해야 한다.

  1. 비공개 HTTP 요청과 지속형 스킬을 지원하는지 어시스턴트를 확인한다. 클라우드 노드를 Tailscale에 등록한다.
  2. tsnet, SQLCipher, FTS5, 지속형 볼륨을 사용하는 상시 실행 Go 서비스를 배포한다. 네트워크 신원을 보존하고 읽기 권한과 업로드 권한을 분리한다. 기존 권한 부여도 점검한다.
  3. 디코더와 재개 가능한 동기화를 갖춘 맥용 업로더를 만든다. 전체 디스크 접근 권한과, 선택 사항인 연락처 접근 권한을 부여한다. 5분 간격의 사용자 LaunchAgent로 실행한다.
  4. 범위가 제한된 읽기 전용 검색·기록·문맥·상태 엔드포인트를 제공한다. 인증, 페이지 나누기, 연락처 조회, 시간대, 최신성을 다루는 어시스턴트 스킬을 설치한다.
  5. 도착 위치 커서, 배타적 잠금, 중복 방지를 포함한 모니터링을 추가한다. 작업 실행 권한은 별도로 승인하고 백그라운드 도구가 이를 지원하는지 확인한다.
  6. 과거 기록 범위, 예약 업로드, 중단된 동기화 복구, 암호화 데이터 복원, 맥이 꺼진 상태에서의 검색을 시험한다. 작업 실행은 폐기 가능한 항목으로 테스트한다.

어떤 함정을 확인해야 하나?

메시지 본문은 message.text가 아니라 message.attributedBody에 들어 있는 경우가 많다. text가 없으면 네이티브 Foundation 도우미로 디코딩한다. 여기서는 NSUnarchiver가 작동했지만, 실제 기록을 사용 중인 macOS 버전에서 확인해야 한다. 디코딩 실패와 첨부 파일만 있는 메시지도 구분해야 한다.

읽기 전용 원본에는 SQLite 백업 API를 사용한다. 데이터베이스 파일만 복사하면 쓰기 앞서 기록하는 로그가 빠질 수 있다. 이름 조회에는 네이티브 CNContactStore를 사용하고, 조회가 실패해도 이미 확인한 일치 정보는 보존한다. 예약 실행 앱에서 권한을 시험해야 한다. 앱을 다시 빌드하면 권한이 초기화될 수 있다.

전송 전에는 각 배치의 정확한 내용을 비공개 발신 대기함에 저장한다. 서버가 반영했지만 맥이 확인 응답을 저장하기 전에 실패하면 다음 실행이 같은 바이트를 다시 보낸다. 서버는 이를 알아보고 중복 적용을 막는다. 나는 이 실패 상황을 시험했고, 예약 업로더가 항목 수를 바꾸지 않고 복구하는 것을 확인했다.

매일 대조 작업은 별도의 새 세대를 만든다. 이때 읽기 작업은 이전 보관소를 계속 사용한다. 새 세대의 유효성을 확인한 뒤 원자적으로 전환하면, 재구성이 중단돼도 불완전한 기록이 노출되지 않는다.

배치 크기는 레코드 수가 아니라 직렬화된 바이트 수로 제한해야 한다. 메시지 길이는 제각각이기 때문이다. 레코드 식별자로는 (conversation_id, message_guid)를 사용한다. 원본 메시지 하나가 여러 대화에 속할 수 있다.

도착 순서도 감시해야 한다. 문자 메시지는 기록된 시각보다 늦게 동기화될 수 있다. 고정된 행 수만큼 읽고, 다음 단계로 넘어가기 전에 세대와 최신성을 다시 확인한다. 설정 시점의 기준 시각을 두면 과거 메시지가 새 할 일로 바뀌는 일을 막을 수 있다. 메시지 수정은 별도로 대조해야 한다. 발신 방향은 is_from_me로 판별한다.

겹쳐 실행되는 작업을 잠그고 상태를 원자적으로 저장해야 한다. 목적지에 항목을 표시해 두면, 쓰기 결과가 불확실할 때 재시도 전에 기존 항목인지 확인할 수 있다. 메시지 내용은 도구 사용을 승인하는 근거가 아니라 정보로만 다뤄야 한다.

직접 구성할 때는 전체 디스크 접근 권한, 예약 실행 앱의 권한 유지, 복구·복원 테스트, 백그라운드 작업 승인 조건을 확인할 만하다. 원문은 실제 호스팅 비용과 지원되는 Muse·macOS 버전 범위를 밝히지 않는다.

부록: 원문 예시

text
. Decode with a native Foundation helper when