본문 바로가기
ColliePWA

17/Documentation

배포 변형 B–E

기본 구성 이외의 진입점: 신원 인식 프록시, Tailscale이 없는 역방향 프록시, 외부 호스트 인그레스, 단일 호스트의 여러 Collie(사용자당 하나 또는 단일 사용자를 위한 여러 인스턴스), crew 대기 진입점

브릿지는 127.0.0.1에 바인딩됩니다. 배포 방식은 인그레스 및 신원 확인 방식에 따라 다릅니다. 변형 A(일반 tailscale serve)는 README에 있습니다. docs/security.md의 보안 요구사항은 모든 형태에 적용됩니다.

프록시 없이 페어링을 통해 개별 기기를 인가할 수도 있습니다.

---

변형 B: 신원 인식 프록시 + 기기별 인가

Collie는 COLLIE_DEVICE_HEADER에서 불투명 디바이스 ID를 읽고 COLLIE_DEVICE_ALLOWLIST을 확인합니다. 허용 목록에 있는 ID는 쓰기 권한을 받습니다. 누락되었거나 목록에 없는 ID는 읽기 전용 권한을 받습니다(터미널 입력, 업로드 및 창 작업이 차단됩니다). 페어링은 두 번째 관문이며 항상 켜져 있습니다. 모든 디바이스는 읽기를 포함한 모든 /api 호출에 페어링 토큰도 필요합니다.

Collie 설정(.env):

COLLIE_HOST=127.0.0.1                       # keep loopback (default)
COLLIE_DEVICE_HEADER=X-Device-Id            # the header your proxy injects
# ids allowed to drive agents; others → read-only
COLLIE_DEVICE_ALLOWLIST=my-phone,my-laptop
# only if the proxy does NOT forward the public Host
# COLLIE_ALLOWED_ORIGINS=https://collie.example.com
# REQUIRED unless the proxy forwards a Host Collie already knows: loopback, a
# discovered Tailscale host, or an allowed origin's host
# COLLIE_PUBLIC_HOSTS=collie.example.com
# opt out of Host validation entirely (re-opens DNS rebinding)
# COLLIE_ALLOW_ANY_HOST=1
# COLLIE_TRUSTED_USER still composes on top if your ingress also injects
# Tailscale-User-Login
# accept a request carrying no Tailscale-User-Login at all
# COLLIE_TRUSTED_USER_OPTIONAL=1

프록시 요구사항:

  1. 기기 인증(mTLS, SSO, forward-auth).
  2. 클라이언트 스푸핑을 방지하기 위해 모든 업스트림 요청에 기기 헤더 재정의을 적용합니다.
  3. 루프백으로 프록시 전달(127.0.0.1:$COLLIE_PORT).
  4. 동일 출처 확인을 통과하려면 공개 Host을 변경 없이 전달을 전달하거나 COLLIE_ALLOWED_ORIGINS에 공개 오리진을 등록합니다.

Nginx 설정 예시:

location / {
    proxy_set_header X-Device-Id $device_id;
    proxy_set_header Host        $host;
    proxy_pass http://127.0.0.1:8787;
}

외부 디바이스에서 헤더 삽입 및 재정의를 확인하십시오. /api/health 및 /api/pair를 제외한 모든 /api 호출에는 페어링 토큰이 필요하므로 먼저 터미널을 페어링하십시오. 호스트에서 collie pair을 실행하십시오. 8자리 코드가 출력됩니다. 테스트에 사용할 디바이스에서 해당 코드를 등록하십시오. Origin 헤더는 공개 URL과 일치해야 합니다. 응답에 토큰이 포함됩니다. 셸 변수에 저장하고 절대로 파일에 붙여넣지 마십시오.

$ collie pair                                   # on the host; note the code it prints
$ TOKEN=$(curl -s -X POST https://collie.example.com/api/pair \
    -H 'Content-Type: application/json' -H 'Origin: https://collie.example.com' \
    -d '{"code":"ABCD1234","label":"header-check"}' | jq -r .token)

그런 다음 토큰으로 헤더 확인을 실행하십시오.

$ curl -s -H "Authorization: Bearer $TOKEN" https://collie.example.com/api/snapshot | jq -c .device
{"enforced":true,"device":"header-check","authorized":true}

$ curl -s -H "Authorization: Bearer $TOKEN" -H 'X-Device-Id: not-listed' https://collie.example.com/api/snapshot | jq -c .device
{"enforced":true,"device":"header-check","authorized":true}

토큰을 사용하면 device은 헤더 값이 아닌 페어링된 레이블(header-check)을 지정합니다. 더 이상 원시 헤더 값을 반환하는 필드가 없으므로 authorized를 확인하십시오. 브리지가 확인한 헤더가 COLLIE_DEVICE_ALLOWLIST에 있고 토큰이 유효할 때만 true가 됩니다. 두 번째 확인에서 "authorized":false가 반환되면 프록시가 클라이언트 헤더를 재정의하지 않고 추가하고 있는 것입니다. 완료되면 collie devices revoke header-check 명령으로 테스트 디바이스를 제거하십시오.

운영 참고 사항:

  • 직접 루프백 접근(http://127.0.0.1:$COLLIE_PORT)은 헤더를 보내지 않으며 읽기 전용이고, 여전히 페어링 토큰이 필요합니다.
  • curl을 통해 로컬에서 명령을 실행하려면 루프백에 토큰과 헤더를 명시적으로 전달하십시오: curl -H "Authorization: Bearer $TOKEN" -H 'X-Device-Id: my-laptop' http://127.0.0.1:$COLLIE_PORT/api/...
  • COLLIE_DEVICE_ALLOWLIST에서 ID를 제거하고 다시 시작하여 접근 권한을 취소합니다(Standalone: bin/collie restart, Herdr: herdr plugin action invoke restart --plugin herdr.collie).
  • 일반 tailscale serve에서는 COLLIE_DEVICE_HEADER을 활성화하지 마십시오. 헤더를 재정의하지 않습니다.
  • 프록시가 다른 호스트에서 실행 중인 경우 변형 D을(를) 참조하십시오.

---

변형 C: 유일한 프론트 도어 역할을 하는 리버스 프록시(Tailscale 미사용)

Tailscale 외부에서 실행하거나 전용 TLS/SSO 프록시 뒤에서 실행할 때 사용합니다. COLLIE_SKIP_SERVE=1은(는) Collie가 tailscale serve을(를) 관리하지 못하도록 합니다.

변형 B의 네 가지 프록시 요구사항이 적용됩니다.

Caddy 구성 예시:

collie.example.com {
    reverse_proxy 127.0.0.1:8787 {
        header_up X-Device-Id {your_device_id}
        header_up Host {host}
    }
}

Collie 설정(.env):

# proxy is ingress; never run tailscale serve
COLLIE_SKIP_SERVE=1
# REQUIRED — Host validation fails closed, and `collie start` discovers no
# tailnet name here
COLLIE_PUBLIC_HOSTS=collie.example.com
# opt out of Host validation (re-opens DNS rebinding)
# COLLIE_ALLOW_ANY_HOST=1
# exact public origin for the same-origin gate
COLLIE_ALLOWED_ORIGINS=https://collie.example.com
COLLIE_DEVICE_HEADER=X-Device-Id                    # the header your proxy injects…
# …and the ids allowed to drive; others → read-only
COLLIE_DEVICE_ALLOWLIST=my-phone,my-laptop
# optional — status banner, `collie qr`, and the address a lead hands
# joining machines (crew)
# COLLIE_PUBLIC_URL=https://collie.example.com

Tailscale이 없으면 COLLIE_TRUSTED_USER은(는) 적용되지 않습니다. 기기별 헤더 또는 프록시 자체 인증이 접근 게이트 역할을 합니다.

/auth/ 라우팅 및 캐싱

  • 오리진 Cache-Control(no-cache)과(와) 함께 /sw.js 및 index.html을(를) 전달하십시오. 인증되지 않은 클라이언트에 대해 정적 에셋을 차단하지 마십시오. 그렇지 않으면 서비스 워커가 업데이트되지 않습니다.
  • /auth/*(또는 Cloudflare Access의 경우 /cdn-cgi/access/) 아래로 인증 흐름을 라우팅하십시오. 서비스 워커는 /auth/*에 대한 캐싱을 우회합니다.
  • API 요청의 포워드 인증 리디렉션은 로그인 UI를 노출하기 위해 401로 처리됩니다. Authentik 설정에서는 /auth/을(를) /outpost.goauthentik.io/start(으)로 라우팅해야 합니다.
  • /auth/에 프록시가 없으면 Collie가 해당 위치에서 404에 응답합니다. 이제 페이지는 페어링되지 않은 기기에 collie pair 명령을 실행하고 설정에서 코드를 입력하도록 안내합니다. 경로가 프록시에 속하므로 리디렉션하지 않습니다.
collie.example.com {
    handle /auth/* {
        reverse_proxy 127.0.0.1:9091
    }
    handle {
        forward_auth 127.0.0.1:9091 { ... }
        reverse_proxy 127.0.0.1:8787 { ... }
    }
}

엔드포인트 및 캐싱 규칙을 확인하십시오. $TOKEN은 변형 B에서 등록한 페어링 토큰입니다. authorized는 헤더가 허용 목록에 있을 때 true입니다.

$ curl -s -H "Authorization: Bearer $TOKEN" https://collie.example.com/api/snapshot | jq -c .device
{"enforced":true,"device":"header-check","authorized":true}

$ curl -sI https://collie.example.com/sw.js | grep -i '^cache-control'
cache-control: no-cache

---

변형 D: tailnet 기반의 외부 호스트 신원 프록시

중앙 집중식 Tailscale 인그레스 노드를 통해 트래픽을 라우팅할 때 사용합니다.

인그레스 노드가 tailnet 에지에서 휴대폰을 인증하며, Collie 자체는 루프백에서만 응답합니다.phoneingress nodeagent hostCollie127.0.0.1:8787httpsTLS + forward-auth,sets the device headerhttp, tailnet only —WireGuard already encrypts ittailscale serve, tailnet-onlyloopback
인그레스 노드가 tailnet 에지에서 휴대폰을 인증하며, Collie 자체는 루프백에서만 응답합니다.

프록시가 루프백 대신 호스트의 Tailscale HTTP 엔드포인트를 대상으로 한다는 점을 제외하고 변형 B의 요구사항이 적용됩니다.

Tailscale ACL(필수)

tailscale serve은(는) 클라이언트 헤더를 수정 없이 전달하므로, Tailscale ACL은 직접 포트 접근을 인그레스 노드로만 제한해야 합니다.

Tailscale / Headscale ≥ 0.29:

"grants": [
  { "src": ["tag:ingress"], "dst": ["tag:agent-host"], "ip": ["tcp:8787"] },
]

Headscale ≤ 0.28:

acls:
  - action: accept
    src: ["ingress-node"]
    dst: ["agent-host:8787"]
  - action: accept
    src: ["my-phone", "my-laptop"]
    dst: ["agent-host:1-8786", "agent-host:8788-65535"]

Collie 설정(.env):

# proxy terminates TLS; this hop is tailnet-internal
COLLIE_SERVE_MODE=http
COLLIE_HOST=127.0.0.1                                 # keep loopback (default)
# header your forward-auth injects — REQUIRED here
COLLIE_DEVICE_HEADER=X-Tailnet-Device
# ids allowed to drive; others + header-less → read-only
COLLIE_DEVICE_ALLOWLIST=my-phone,my-laptop
# REQUIRED — the Host the proxy forwards. COLLIE_TAILSCALE_HOSTS carries the bare
# tailnet name `collie start` found; a rewritten Host is yours to list.
# COLLIE_ALLOW_ANY_HOST=1 opts out.
COLLIE_PUBLIC_HOSTS=host:8787,host.your-tailnet.ts.net:8787
# the public origin the browser actually uses
COLLIE_ALLOWED_ORIGINS=https://collie.example.com

COLLIE_TRUSTED_USER은(는) 인그레스 노드 뒤의 최종 사용자가 아닌 인그레스 노드의 ID와 일치합니다.

검증:

수신이 아닌 tailnet 피어에서 실행하는 경우입니다. $TOKEN은 변형 B에서 등록한 페어링 토큰입니다.

$ curl -s -H "Authorization: Bearer $TOKEN" https://collie.example.com/api/snapshot | jq -c .device
{"enforced":true,"device":"header-check","authorized":true}

$ curl -s --max-time 10 -H 'X-Tailnet-Device: my-phone' http://host.your-tailnet.ts.net:8787/api/snapshot
curl: (28) Connection timed out

에이전트 호스트에서는 요청이 헤더를 보내지 않으므로 유효한 토큰이 있더라도 authorized은 false입니다.

$ curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8787/api/snapshot | jq -c .device
{"enforced":true,"device":"header-check","authorized":false}

---

변형 E: 기타 모든 메시 또는 터널(NetBird, ZeroTier, Cloudflare Tunnel)

터널/프록시가 127.0.0.1:$COLLIE_PORT을(를) 가리키도록 설정하십시오.

Collie 설정(.env):

COLLIE_SKIP_SERVE=1                                 # never run tailscale serve
# REQUIRED — exact public host; Host validation fails closed and finds no
# tailnet name here
COLLIE_PUBLIC_HOSTS=collie.example.com
# exact public origin for the same-origin gate
COLLIE_ALLOWED_ORIGINS=https://collie.example.com

규칙:

  1. 변형 B 프록시 규칙을 적용하십시오.
  2. Tailscale이 없으면 COLLIE_TRUSTED_USER은(는) 비활성화됩니다. COLLIE_DEVICE_HEADER 또는 터널 인증을 사용하십시오.
  3. PWA 캐싱 및 COLLIE_PUBLIC_HOSTS이(가) 유효하게 유지되도록 고정 호스트 이름을 사용하십시오.

---

프런트 도어 제품별 설정

자주 묻는 프런트 도어의 대략적인 설정입니다. 각 설정은 동일한 지점으로 귀결됩니다. 하나의 고정 호스트 이름에서 HTTPS를 수신하여 이 호스트의 127.0.0.1:$COLLIE_PORT로 전달합니다.

참고. 웹 푸시, 마이크, 홈 화면 설치 기능을 사용하려면 휴대전화가 신뢰하는 인증서가 적용된 HTTPS가 필요합니다. 일반 HTTP 환경에서도 Collie는 브라우저 탭에서 읽고 응답할 수 있지만, 위 세 가지 기능은 꺼진 상태로 유지됩니다(음성 입력 및 Web Push).

Tailscale을 제외한 모든 프런트 도어는 Collie의 .env에 동일한 세 줄을 추가한 다음 재시작합니다.

COLLIE_SKIP_SERVE=1                                 # you own the door, not Collie
COLLIE_PUBLIC_HOSTS=collie.example.com              # the hostname the phone opens
COLLIE_ALLOWED_ORIGINS=https://collie.example.com   # the same, as an origin

Collie와 동일한 호스트에서 프록시나 터널을 실행하십시오. Collie는 루프백에서 오지 않는 연결을 거부하므로, 다른 머신에 있는 프록시는 대신 변형 D이 필요합니다.

그런 다음 collie pair으로 휴대전화를 페어링하십시오. 이러한 프런트 도어는 어떤 기기가 입력을 수행할 수 있는지 알지 못하며, 페어링을 통해 이를 제어합니다(기기 페어링).

도어기본적으로 비공개HTTPS 인증서추가 단계
Tailscale예, tailnet 전용Tailscale에서 발급HTTPS 인증서 한 번 활성화
Cloudflare Tunnel아니요, 공개Cloudflare에서 발급앞단에 Cloudflare Access 로그인 구성
Nginx Proxy Manager호스트에 따라 다름Let's Encrypt컨테이너에 호스트 네트워킹 사용
NetBird예, 메시 전용자체 인증서 없음호스트의 프록시, DNS 챌린지
Caddy호스트에 따라 다름Let's Encrypt, 자동비공개 이름을 위한 DNS 플러그인

Tailscale

기본값이자 Collie가 직접 관리하는 유일한 출입구입니다. collie start이 MagicDNS 이름에서 tailscale serve을 실행합니다.

  1. Tailscale 관리 콘솔에서 DNS을 열고 MagicDNS를 켠 다음 Enable HTTPS을 켭니다.
  2. Collie를 시작하면 tailnet에 자체 등록됩니다.
    collie start
  1. .env에서 COLLIE_TRUSTED_USER을 tailnet 로그인 계정으로 설정한 다음 다시 시작합니다.
    collie restart

나머지는 변형 A에 있습니다. 이 출입구에는 COLLIE_SKIP_SERVE을 설정하지 마십시오.

절대 tailscale funnel을 사용하십시오. Funnel은 포트를 공용 인터넷에 노출합니다.

Cloudflare Tunnel

cloudflared이 Cloudflare로 아웃바운드 연결을 설정하므로 라우터에서 포트가 열리지 않습니다. 호스트 이름이 공개되므로 앞에 로그인이 있어야 합니다.

  1. 로그인하고 터널을 생성한 뒤 호스트 이름을 터널로 지정합니다.
    cloudflared tunnel login
    cloudflared tunnel create collie
    cloudflared tunnel route dns collie collie.example.com
  1. 출력된 터널 ID create을 사용하여 ~/.cloudflared/config.yml을 작성합니다.
    tunnel: <tunnel-id>
    credentials-file: /home/you/.cloudflared/<tunnel-id>.json
    ingress:
      - hostname: collie.example.com
        service: http://127.0.0.1:8787
      - service: http_status:404
  1. 재부팅 후에도 유지되도록 해당 구성으로 서비스 설치합니다.
    sudo cloudflared --config ~/.cloudflared/config.yml service install
  1. Cloudflare Zero Trust에서 본인의 이메일만 허용하는 정책으로 collie.example.com에 대한 자체 호스팅 애플리케이션을 추가합니다.

Zero Trust 대시보드에서 만든 터널도 작동합니다. 공용 호스트 이름의 서비스를 http://127.0.0.1:8787으로 설정하십시오. cloudflared은 공용 호스트 이름을 COLLIE_PUBLIC_HOSTS이 요구하는 Host로 전달합니다.

경고. 4단계가 없으면 호스트 이름을 찾는 누구나 시스템의 셸에 접근할 수 있습니다. Cloudflare Access는 문의 자물쇠 역할을 하며, 페어링을 통해 입력 가능한 기기를 결정합니다.
  1. Collie가 잠금을 확인하도록 설정하십시오. Access 애플리케이션에서 Application Audience (AUD) 태그을 복사하십시오. 이를 팀 이름과 함께 .env에 추가한 다음 재시작하십시오.
    COLLIE_ACCESS_TEAM=myteam          # from myteam.cloudflareaccess.com
    COLLIE_ACCESS_AUD=<the AUD tag>

    이제 터널을 통과하는 모든 요청에는 Cloudflare가 이 애플리케이션용으로 서명한 토큰이 포함되어야 합니다. Access 앱이 삭제되었거나, 우회 규칙이 추가되었거나, 아직 모든 엣지에 전파되지 않은 경우, Collie는 창을 표시하는 대신 요청을 거부합니다. 대상(Audience) 확인은 필수적입니다. 팀 내의 모든 Access 앱은 동일한 키로 서명하므로 다른 앱용 토큰 역시 거부됩니다.

    Collie는 시작 시점과 매시간 팀의 공개 키를 가져옵니다. 첫 번째 조회가 완료될 때까지 모든 원격 요청은 503 오류를 반환합니다. 이후 조회가 실패하면 Collie는 기존에 보유한 키를 유지합니다. 두 항목을 모두 설정하거나 둘 다 설정하지 마십시오. 하나만 설정하면 모든 원격 요청이 거부됩니다. 어느 쪽이든 브리지 로그에 문제가 한 줄로 기록되므로 재시작 후 로그를 확인하십시오.

    이 머신의 프로세스만 토큰을 건너뜁니다. 즉, Cf-* 및 포워딩 헤더(X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto, Forwarded, X-Real-Ip)를 포함하지 않는 127.0.0.1 또는 localhost 대상 요청만 해당됩니다. 입력하려면 여전히 페어링이 필요합니다. /api/health 및 크루 링크에도 토큰이 필요하지 않습니다. collie doctor 역시 이러한 로컬 호출자이므로 doctor 상태가 녹색이라고 해서 키가 로드되었거나 토큰이 검증된다는 의미는 아닙니다. 휴대폰에서 호스트 이름을 열어 확인하십시오. 자세한 이유는 ADR 0081에 있습니다.

    게이트가 켜진 상태에서 다음 규칙을 유지하십시오:

    - 한 번에 하나의 진입점만 사용하십시오. Access가 켜져 있으면 tailscale serve, Caddy 또는 Traefik을 통과하는 요청에도 토큰이 필요합니다. 기본적으로 포워딩 헤더를 추가하기 때문입니다. 터널과 tailnet 이름(tailscale serve)을 둘 다 제공하는 머신은 브라우저용 tailnet 진입점을 잃게 됩니다. 해당 요청에는 Access 토큰이 없으므로 401 응답을 받습니다. - 요청을 로컬 요청처럼 보이게 만드는 보조 프록시를 cloudflared와(과) Collie 사이에 두지 마십시오. Host을(를) 127.0.0.1 또는 localhost으로 재작성하고 Cf-* 및 포워딩 헤더를 제거하는 프록시는 의도적으로 그렇게 동작하는 것입니다. 기본 설정의 순수 nginx proxy_pass http://127.0.0.1:8787 설정이 그렇습니다. Host: 127.0.0.1:8787을(를) 전송하며 직접 설정하지 않는 한 포워딩 헤더를 추가하지 않습니다. 이러한 요청은 토큰 확인을 건너뜁니다. cloudflared이(가) Collie를 직접 가리키도록 설정하십시오. - 매니페스트, 서비스 워커, 아이콘용이 아니라 Collie 경로에 대한 Access 우회 규칙을 추가하지 마십시오.용입니다. Access는 통과시키는 모든 요청에 토큰을 추가합니다. 우회된 경로에는 토큰이 전달되지 않으므로 Collie는 401 응답을 반환합니다.

Access는 로그인을 위해 /cdn-cgi/access/으로 리디렉션합니다. /auth/ 라우팅 및 캐싱의 안내대로 sw.js 및 index.html는 캐시되지 않도록 유지하십시오.

Nginx Proxy Manager

웹 UI와 Let's Encrypt가 내장된 리버스 프록시입니다. Docker에서 실행되며, 컨테이너 내부의 127.0.0.1은 호스트가 아니라 컨테이너를 가리킵니다.

  1. 루프백으로 Collie에 접근할 수 있도록 컨테이너에 호스트 네트워크를 부여합니다.
    services:
      npm:
        image: jc21/nginx-proxy-manager:latest
        network_mode: host
        volumes:
          - ./data:/data
          - ./letsencrypt:/etc/letsencrypt
  1. 81번 포트에서 UI를 열고 Proxy Host을 추가합니다. 도메인은 collie.example.com, 스킴은 http, 전달 대상은 127.0.0.1, 포트는 8787입니다.
  2. SSL 탭에서 Let's Encrypt 인증서를 요청하고 Force SSL을 켭니다.

서비스 워커가 업데이트될 수 있도록 자산 캐시을 꺼 두십시오. Collie는 일반 HTTP를 통해 폴링하므로 Websockets 지원 스위치는 영향을 주지 않습니다.

네트워크 내부에서만 확인되는 이름의 경우 SSL 탭에서 DNS 챌린지 사용을 선택하십시오. Let's Encrypt는 HTTP를 통해 비공개 호스트에 연결할 수 없습니다.

여기서는 host-gateway와 함께 경고. host.docker.internal을 사용할 수 없습니다. Docker 브리지 주소에 도달하지만, Collie는 127.0.0.1에서만 수신 대기합니다.

프록시가 인터넷에 노출되어 있다면 프록시 호스트에 액세스 목록을 추가하거나, 라우터에서 80 및 443 포트를 닫아 두고 VPN을 통해 호스트에 연결하십시오.

NetBird

NetBird는 tailnet과 같은 WireGuard 메시입니다. 트래픽을 전달하며, 호스트의 리버스 프록시가 HTTPS를 추가합니다.

  1. 호스트와 휴대폰을 동일한 NetBird 네트워크에 연결하십시오.
    netbird up
  1. collie.example.com의 DNS 레코드가 호스트의 NetBird 주소(100.x.y.z)를 가리키도록 설정하십시오.
  2. 해당 주소에 바인딩하고 DNS 챌린지 인증서를 사용하도록 호스트에서 Caddy를 실행하십시오.
    collie.example.com {
        bind 100.x.y.z
        tls {
            dns cloudflare {env.CF_API_TOKEN}
        }
        reverse_proxy 127.0.0.1:8787
    }

DNS 챌린지는 Let's Encrypt가 호스트에 도달하지 않고도 도메인 소유권을 증명하므로 비공개 주소를 가리킬 수 있습니다. Caddy에는 공급자용 DNS 플러그인(예: xcaddy build --with github.com/caddy-dns/cloudflare)이 필요합니다.

host.netbird.cloud과 같은 NetBird 피어 이름에는 공개적으로 신뢰할 수 있는 인증서가 없으므로 직접 소유한 도메인을 사용하십시오. ZeroTier나 다른 메시 네트워크에도 동일한 방식이 적용됩니다.

Caddy

Caddy는 자체 인증서를 발급받고 갱신합니다. 80 및 443 포트에 연결할 수 있는 호스트의 경우 전체 설정은 다음과 같습니다.

collie.example.com {
    reverse_proxy 127.0.0.1:8787
}

비공개 이름의 경우 위 NetBird의 tls { dns … } 블록을 추가하십시오. 공개 호스트는 변형 C와 같이 전면에 로그인도 필요합니다.

---

특정 경로에서 Collie 서비스하기

하나의 릴리스 빌드로 모든 마운트를 처리합니다. 인스턴스 .env(또는 config.toml의 [serve] base_path)에서 경로를 설정하고 재시작하면 브리지가 해당 경로 아래에서 앱, 자산 및 /api/*을 제공합니다.

# https://<your-node>/collie/ — leading and trailing slash both optional; the bridge adds them
COLLIE_BASE_PATH=/collie

허용됨: /collie, collie/, /apps/collie/. 거부됨(로그에 한 줄이 기록되고 루트가 대신 사용됨): . 또는 .. 세그먼트, 공백, ?, #. collie doctor은 front-door 줄에 마운트를 출력합니다.

기본 전면 도어를 사용하는 경우 collie serve(및 모든 collie start)은 루트 대신 tailscale serve --set-path=/collie/를 게시하므로 다른 앱이 동일한 노드에서 /을 유지할 수 있습니다. Tailscale은 프록시를 수행하기 전에 마운트를 제거하며, 브리지도 마운트가 포함된 요청을 그대로 수락하므로 리버스 프록시는 어느 방식으로든 작동합니다. 현재 설정을 확인하려면 다음을 실행하십시오.

curl -s https://<your-node>/collie/api/health   # JSON, not HTML, means the mount is right

자체 프록시(변형 C) 뒤에서는 브리지를 동일한 경로에 마운트하십시오. Caddy: handle_path /collie/* { reverse_proxy 127.0.0.1:8787 }. Nginx: location /collie/ { proxy_pass http://127.0.0.1:8787/; }. COLLIE_PUBLIC_URL을 설정한 경우 경로를 포함하십시오: https://collie.example.com/collie/.

마운트 변경은 휴대폰에서 새 앱으로 인식됩니다. 설치된 PWA의 범위와 ID는 설치된 원본 경로입니다. COLLIE_BASE_PATH을 변경하거나 루트로 다시 이동한 후에는 홈 화면 아이콘을 삭제하고 새 주소에서 다시 추가하십시오. 이전 아이콘은 브리지가 더 이상 제공하지 않는 경로를 계속 가리킵니다. 브리지 자체는 재시작만 필요하며, collie serve는 도어를 자동으로 이동합니다. 새 레코드를 게시하기 전에 해당 레코드 이름의 매핑을 해제합니다.

---

단일 호스트의 여러 Collie 인스턴스

공유 시스템(ADR 0001)에서 사용자별 독립 인스턴스를 호스팅하려면:

# ~/.config/herdr/plugins/config/herdr.collie/.env — one per Unix user
# this user's loopback bridge port — unique per user
COLLIE_PORT=8801
# this user's tailnet https listener — unique per user
COLLIE_SERVE_PORT=8443
COLLIE_TRUSTED_USER=dev-a@example.com  # only this tailnet login may drive these agents

독립형 바이너리 URL 확인: bin/collie url. Herdr 플러그인 URL 확인: herdr plugin action invoke url --plugin herdr.collie.

요구 사항:

  • 서로 다른 Unix 사용자와 분리된 herdr/Collie 인스턴스를 실행하십시오.
  • 포트별 접근을 제한하려면 항상 COLLIE_TRUSTED_USER을 설정하십시오.
  • 초기 Tailscale 바인딩에는 운영자 설정이 필요합니다. 운영자 권한으로 bin/collie serve(또는 이에 상응하는 Herdr 명령)을 실행하십시오.

COLLIE_SERVE_PORT은 인스턴스의 진입 포트를 설정합니다. COLLIE_INSTANCE은 동일한 호스트에 자체 서비스 유닛과 구성을 갖춘 격리된 인스턴스를 생성합니다(단일 호스트에서의 다중 Collie 인스턴스).

---

단일 호스트에서의 다중 Collie 인스턴스

동일한 머신에서 별도의 두 번째 Collie를 실행하려면 이 설정을 사용하십시오. 예로는 안정 릴리스와 작업 복사본을 함께 실행하거나, Herdr에서 관리하는 기본 멀티플렉서 옆에 두 번째 멀티플렉서(tmux/zellij)를 실행하는 경우가 있습니다. 각 인스턴스는 전용 포트, 구성, 상태 디렉터리 및 서비스 유닛을 가집니다. 이는 Unix 사용자당 하나의 인스턴스를 할당하는 단일 호스트의 여러 Collie 인스턴스와 다릅니다. 여기서는 한 사용자가 여러 인스턴스를 실행합니다.

두 번째 인스턴스 생성

인스턴스 이름을 지정하려면 COLLIE_INSTANCE=<name>을 설정하십시오. 이름은 [a-z0-9-]{1,16}과 일치해야 합니다. 이름을 지정한 인스턴스에는 명시적인 COLLIE_PORT도 필요합니다. 이 포트가 없으면 CLI가 오류와 함께 종료되며, 기본값을 유추하지 않습니다.

~/.config/herdr/plugins/config/herdr.collie-<name>/.env 생성:

COLLIE_INSTANCE=<name>        # required — [a-z0-9-], max 16 chars
COLLIE_PORT=8788              # required for a named instance — no default is inferred
# Unset, this defaults to ~/.local/state/collie, with no instance suffix
COLLIE_STATE_DIR=/home/you/.local/state/collie-<name>

COLLIE_INSTANCE, COLLIE_PORT, COLLIE_STATE_DIR는 모두 해당 .env 파일에 위치할 수 있습니다. 병합된 환경이 인스턴스를 확인합니다. HERDR_PLUGIN_CONFIG_DIR는 CLI가 구성을 검색하는 디렉터리를 재정의합니다.

인스턴스별 서비스 유닛 실행

환경에서 인스턴스를 설정하면 collie start이 유닛을 작성합니다. 운영자가 직접 작성하지 않습니다. macOS에서는 대신 ~/Library/LaunchAgents/ 아래에 launchd plist를 작성합니다. 유닛은 COLLIE_PORT, COLLIE_INSTANCE, COLLIE_PLUGIN_ROOT, HERDR_PLUGIN_CONFIG_DIR, EnvironmentFile=-<config dir>/.env을 설정합니다. ExecStart의 --instance <name>은 하나의 바이너리를 공유하는 두 인스턴스가 자체 브리지를 구분할 수 있도록 하기 위해서만 존재합니다(cli/unit.ts systemdUnit()).

ExecStart=<root>/bin/collie _exec-bridge --instance <name>
Environment=COLLIE_PORT=8788
Environment=COLLIE_INSTANCE=<name>
Environment=COLLIE_PLUGIN_ROOT=<root>
Environment=HERDR_PLUGIN_CONFIG_DIR=<config dir>
EnvironmentFile=-<config dir>/.env

CLI는 인스턴스별 tailscale serve 핸들러 파일을 유지하므로 한 인스턴스에서 unserve을 실행해도 다른 인스턴스의 매핑이 삭제되지 않습니다.

CLI에서 이름이 지정된 인스턴스 지정

모든 CLI 동사(pair, devices, url, qr, crew …, logs, push-test, …)는 프로세스 환경에서 대상 인스턴스를 확인합니다. 동사를 호출하기 전에 COLLIE_INSTANCE을 설정하십시오.

COLLIE_INSTANCE=next bin/collie pair
COLLIE_INSTANCE=next bin/collie devices list

COLLIE_INSTANCE이 없으면 CLI는 Herdr를 쿼리합니다. Herdr는 접미사가 없는 플러그인만 추적하므로 명령이 첫 번째 인스턴스에 대해 실행됩니다. 잘못된 인스턴스를 수정하지 않도록 단일 호스트에서 여러 인스턴스를 실행할 때는 항상 COLLIE_INSTANCE을 명시적으로 설정하십시오.

거부 규칙

COLLIE_INSTANCE이 설정되어 있지만 herdr.collie-<name>/.env이 없으면 CLI가 오류와 함께 종료됩니다. 다른 인스턴스의 구성으로 대체되지 않습니다.

페어링은 인스턴스별로 수행됩니다

collie pair 명령은 해당 인스턴스의 상태 디렉터리에 페어링 코드를 작성합니다. 각 인스턴스는 고유한 상태 디렉터리를 가지며 COLLIE_PUBLIC_URL 또는 자체 포트를 통해 고유한 프런트 도어를 가지므로, 인쇄되는 QR 코드는 해당 인스턴스의 자체 URL을 인코딩합니다. 휴대전화에서 해당 인스턴스의 특정 URL을 열고 설정 → 시스템 → 페어링된 기기에서 코드를 입력하십시오. 한 인스턴스용으로 생성된 코드는 다른 인스턴스에 기기를 페어링할 수 없습니다(기기 페어링).

---

대기 진입점: crew의 장애 조치 경로

crew 배포용. lead에 연결할 수 없게 될 때 인계받을 사전 승인된 deputy를 구성합니다(ADR 0027, ADR 0028, CREW_PROTOCOL.md §18).

deputy 및 lead 설정:

키기본값기능
COLLIE_STANDBY_PORT(설정되지 않음)대기 리스너용 포트입니다. 설정하지 않으면 대기 도어가 비활성화됩니다. lead와 deputy에서 일치해야 합니다.
COLLIE_STANDBY_HOST127.0.0.1바인드 주소입니다(로컬 프록시의 경우 127.0.0.1, 원격의 경우 오버레이 IP).
COLLIE_STANDBY_ARM_MSmax(30000, 2.5 × COLLIE_POLL_IDLE_MS)준비 태세(arming)로 전환하기 전에 필요한 lead 무응답 지속 시간입니다.

lead와 deputy 양쪽에서 COLLIE_STANDBY_PORT 값을 사용되지 않는 동일한 포트로 설정하십시오.

전제 조건: 하나의 호스트 이름, 두 개의 백엔드

PWA 등록과 기기 자격 증명을 공유하려면 lead와 deputy를 동일한 오리진에서 제공해야 합니다. 통합 인그레스가 없는 독립형 crew는 bin/collie promote(또는 Herdr: herdr plugin action invoke promote --plugin herdr.collie; CREW_PROTOCOL §14.4)를 통해 복구합니다.

Traefik 구성 예시:

http:
  routers:
    collie:
      rule: "Host(`collie.example.com`)"
      service: collie-crew
      tls: {}

  services:
    collie-crew:
      failover:
        service: collie-lead
        fallback: collie-deputy

    collie-lead:
      loadBalancer:
        servers:
          - url: "http://lead.internal:8787"
        healthCheck:
          path: /standby/health
          interval: 5s
          timeout: 2s

    collie-deputy:
      loadBalancer:
        servers:
          - url: "http://deputy.internal:8788"   # COLLIE_STANDBY_PORT
        healthCheck:
          path: /standby/health
          interval: 5s
          timeout: 2s

헬스 체크 응답: lead는 200을 반환합니다(강등 시 200이 아님). deputy는 준비 태세가 될 때까지 503를 반환하고, 그 후 200을 반환합니다.

모든 것이 정상일 때 한 번 설정하십시오

lead의 Standalone 설정:

bin/collie pair
bin/collie crew deputy nas
bin/collie crew status

lead의 Herdr 설정:

herdr plugin action invoke pair --plugin herdr.collie
herdr plugin action invoke crew --plugin herdr.collie deputy nas
herdr plugin action invoke crew --plugin herdr.collie status

deputy에서 COLLIE_STANDBY_PORT=8788을 구성하고 재시작하십시오. 모든 피어가 재시작하여 deputy warrant를 로드하도록 하십시오. https://collie.example.com/standby에서 확인하십시오.

⚠️ deputy는 감독(supervised)되어야 합니다

인계 과정에서 deputy는 상태를 기록하고 프로세스 재시작(bridge/index.ts)을 트리거하기 위해 종료 코드 75(EX_TEMPFAIL)으로 종료됩니다.

감독자(supervisor)가 0이 아닌 종료 코드에서 재시작하도록 설정하십시오(systemd: Restart=always 또는 Restart=on-failure).

장애 발생 시 런북

  1. https://collie.example.com을 엽니다.
  2. lead가 비정상 상태일 때 프록시는 /standby으로 라우팅합니다.
  3. deputy 대기 상태를 검토하십시오.
  4. 인계 실행을 선택하십시오.
  5. deputy가 피어 합의를 확인하고, warrant를 적용한 뒤 75으로 종료됩니다.
  6. 감독자가 프로세스를 재시작하고, PWA가 새 lead로 다시 로드됩니다.

복구 후 작업:

  • 이전 lead는 다시 연결될 때 스스로를 강등합니다(CREW_PROTOCOL.md §8.4).
  • 강등된 노드의 피어 주소를 업데이트하십시오: Standalone bin/collie crew set-address <member> <host:port> 또는 Herdr herdr plugin action invoke crew --plugin herdr.collie set-address <member> <host:port>.
  • 새 deputy를 지정하십시오: Standalone bin/collie crew deputy <member> 또는 Herdr herdr plugin action invoke crew --plugin herdr.collie deputy <member>.
  • 모든 구성원이 다시 연결될 때까지 crew rotate을 실행하지 마십시오.

GitHub에서 이 페이지 수정하기

The rest of the documentation