crossbell/멀티머신
최근 수정 시각: 2026-08-28 22:20:10
🪦 은퇴한 기능 — 2026-08-03 축소 개편으로 connector·깨우기·실시간 허브가 crossbell에서 제거됐다. 머신별 세션 관제는 warren이 Tailscale SSH로 tmux를 직접 읽는 방식으로 대체했다. 아래는 그 시절의 기록이다.
1. 무엇을 하나
crossbell은 브라우저, Cloudflare 허브, 각 PC의 connector를 세 층으로 나눈다. 따라서 “앱이 연결됨”, “기기가 켜짐”, “에이전트가 라이브”는 서로 다른 상태다. 멀티머신 화면과 깨우기는 이 차이를 보이게 하고 세션이 없는 PC를 원격으로 다시 붙인다.
2. 상태 읽기
메신저 목록 상단의 연결됨/재접속 중은 현재 브라우저와 Hub Durable Object 사이의 WebSocket 상태다. 개별 에이전트 행과 설정의 에이전트 목록은 다음 상태를 보여 준다.
| 상태 | 의미 | 다음 행동 |
|---|---|---|
| 라이브 | 머신의 connector와 에이전트 세션이 허브에 연결됨 | 바로 대화한다. |
| 기기 켜짐 · 세션 없음 | Tailscale 기기 생존은 확인되지만 connector 세션이 없음 | 깨우기를 누른다. |
| 기기 오프라인 | 머신 자체가 응답하지 않음 | 전원·네트워크 상태를 확인한다. |
| 오프라인 | 세션이 없고 기기 정보가 없거나 확인되지 않음 | 잠시 후 새로고침하거나 운영 상태를 확인한다. |
기기 생존은 진입 즉시, 60초 주기, 탭 복귀 때 /api/machines에서 갱신된다. 에이전트 presence는 connector heartbeat와 WebSocket 상태로 판단한다.
3. 깨우기
- 오프라인 에이전트의 대화창을 연다.
- 헤더의 깨우기를 누른다.
- 버튼이
신호 보내는 중…→깨어나는 중…으로 바뀌는지 확인한다. - 머신의 상주 세션이 붙어 상태가 라이브로 바뀌고 버튼이 사라질 때까지 기다린다.
허브는 presence와 60초 쿨다운을 원자적으로 확인한 뒤, 해당 머신의 Tailscale Funnel 뒤 Hermes webhook에 HMAC 서명된 신호를 한 번 보낸다. 머신 쪽 Hermes가 tmux 상주 세션을 되살리고 connector가 재접속한다. 상시 폴링으로 세션을 띄우는 구조는 아니다.
4. 메시지 유실 방지
에이전트가 오프라인일 때 보낸 메시지는 D1에 미배달 상태로 쌓인다. connector가 다시 attach하면 ack 기반으로 재전송하며, 브라우저는 ULID로 에코와 중복을 제거한다.
connector는 기본 30초마다 ping을 보내고 10초 안에 pong이 없으면 연결을 닫고 재접속한다. 허브도 약 75초 이상 ping이 없는 소켓을 정리해 유령 온라인 상태를 줄인다. 한 머신에서 여러 세션이 같은 에이전트 ID를 쓰면 가장 최근 세션이 자리를 차지하고 이전 connector는 대기한다.
5. 새 머신의 구성 요소
새 머신이 목록에 나타나려면 저장소의 connector 의존성, 허브 URL, 에이전트 ID, Cloudflare Access service token, Claude Code 채널 로딩 플래그가 필요하다. connector는 Bun stdio MCP 플러그인으로 동작하며 수신 메시지를 세션 컨텍스트에 주입한다.
시크릿 값은 crossbell.json 같은 gitignore 대상이나 머신 환경에만 둔다. 위키·채팅·공유 폴더에 service token이나 wake 시크릿을 남기지 않는다.
6. 팁과 주의
- 허브 배포 뒤 ping을 보내지 않는 옛 connector가 남아 있으면 약 2분 간격으로 끊겼다 붙는 현상이 생길 수 있다. 모든 머신의 connector 세션을 새 버전으로 재시작한다.
- 깨우기 요청 성공과 실제 세션 라이브 전환은 별개다. 최종 확인은 헤더 상태와 답장이다.
- 앱 설정의 에이전트 목록은 각 머신의 마지막 라이브 시각도 보여 준다. 문제를 재현하기 전에 어느 층이 끊겼는지 먼저 구분한다.