Direkt zum Inhalt
ColliePWA

17/Documentation

Bereitstellungsvarianten B–E

Andere Eingänge als der Standard: ein Identity-Aware-Proxy, ein Reverse-Proxy ohne Tailscale, ein Off-Host-Ingress, mehrere Collies auf einem Host (einer pro Benutzer oder mehrere Instanzen für einen Benutzer) und der Standby-Eingang einer Crew

Die Bridge bindet 127.0.0.1. Bereitstellungen unterscheiden sich durch Ingress und Identitätsprüfung. Variante A (einfaches tailscale serve) befindet sich in der README. Die Sicherheitsanforderungen in docs/security.md gelten für alle Varianten.

Einzelne Geräte können auch ohne Proxy über Koppeln autorisiert werden.

---

Variante B: Identity-Aware-Proxy + gerätespezifische Autorisierung

Collie liest eine opake Geräte-ID aus COLLIE_DEVICE_HEADER und prüft COLLIE_DEVICE_ALLOWLIST. IDs auf der Positivliste erhalten Schreibzugriff; fehlende oder nicht gelistete IDs erhalten schreibgeschützten Zugriff (Terminal-Eingaben, Uploads und Bereichsaktionen sind blockiert). Die Kopplung ist eine zweite Hürde und immer aktiv: Jedes Gerät benötigt außerdem ein Kopplungstoken für jeden Aufruf von /api, Lesezugriffe eingeschlossen.

Collie-Konfiguration (.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

Proxy-Anforderungen:

  1. Gerät authentifizieren (mTLS, SSO, Forward-Auth).
  2. Geräte-Header überschreiben bei jeder Upstream-Anfrage, um clientseitiges Spoofing zu verhindern.
  3. Proxy auf Loopback (127.0.0.1:$COLLIE_PORT).
  4. Öffentlichen Host unverändert weiterleiten oder tragen Sie den öffentlichen Origin in COLLIE_ALLOWED_ORIGINS ein, um die Same-Origin-Prüfung zu bestehen.

Beispielhafte Nginx-Konfiguration:

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

Überprüfen Sie Header-Injection und -Überschreibung von einem externen Gerät aus. Jeder Aufruf von /api außer /api/health und /api/pair benötigt ein Kopplungstoken; koppeln Sie daher zuerst ein Terminal. Führen Sie auf dem Host collie pair aus. Dies gibt einen 8-stelligen Code aus. Fordern Sie den Code von dem Gerät an, mit dem Sie testen. Der Header Origin muss mit der öffentlichen URL übereinstimmen. Die Antwort enthält das Token. Speichern Sie es in einer Shell-Variable und fügen Sie es niemals in eine Datei ein:

$ 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)

Führen Sie dann die Header-Prüfung mit dem Token aus:

$ 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}

Mit einem Token benennt device die gekoppelte Bezeichnung (header-check), nicht den Header-Wert. Kein Feld gibt mehr den rohen Header-Wert zurück; prüfen Sie daher authorized: Es ist nur dann true, wenn der von der Bridge empfangene Header in COLLIE_DEVICE_ALLOWLIST enthalten ist und das Token gültig ist. Wenn die zweite Prüfung "authorized":false zurückgibt, hängt der Proxy den Header des Clients an, anstatt ihn zu überschreiben. Wenn Sie fertig sind, entfernen Sie das Testgerät mit collie devices revoke header-check.

Betriebshinweise:

  • Direkter Loopback-Zugriff (http://127.0.0.1:$COLLIE_PORT) sendet keinen Header, ist schreibgeschützt und benötigt dennoch ein Kopplungstoken.
  • Um Befehle lokal über curl auszuführen, übergeben Sie das Token und den Header explizit an Loopback: curl -H "Authorization: Bearer $TOKEN" -H 'X-Device-Id: my-laptop' http://127.0.0.1:$COLLIE_PORT/api/...
  • Entziehen Sie den Zugriff, indem Sie die ID aus COLLIE_DEVICE_ALLOWLIST entfernen und neu starten (Standalone: bin/collie restart; Herdr: herdr plugin action invoke restart --plugin herdr.collie).
  • Aktivieren Sie COLLIE_DEVICE_HEADER nicht bei einfachem tailscale serve; Header werden dabei nicht überschrieben.
  • Wenn der Proxy auf einem anderen Host läuft, siehe Variante D.

---

Variante C: Reverse-Proxy als einziger Eingang (kein Tailscale)

Verwenden Sie dies beim Betrieb außerhalb von Tailscale oder hinter einem dedizierten TLS/SSO-Proxy. COLLIE_SKIP_SERVE=1 verhindert, dass Collie tailscale serve verwaltet.

Die vier Proxy-Anforderungen aus Variante B gelten.

Beispielkonfiguration für Caddy:

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

Collie-Konfiguration (.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

COLLIE_TRUSTED_USER hat ohne Tailscale keine Wirkung. Header pro Gerät oder die eigene Authentifizierung des Proxys dienen als Zugriffskontrolle.

Routing für /auth/ und Caching

  • Leiten Sie /sw.js und index.html mit dem Origin Cache-Control (no-cache) weiter. Blockieren Sie statische Assets für nicht authentifizierte Clients nicht, da Service-Worker sonst nicht aktualisiert werden können.
  • Leiten Sie Authentifizierungsabläufe unter /auth/* weiter (oder /cdn-cgi/access/ für Cloudflare Access). Service-Worker umgehen das Caching für /auth/*.
  • Forward-Auth-Weiterleitungen bei API-Anfragen werden als 401-Fehler behandelt, um die Login-Benutzeroberfläche anzuzeigen. Authentik-Setups sollten /auth/ an /outpost.goauthentik.io/start weiterleiten.
  • Ohne Proxy auf /auth/ antwortet Collie dort mit 404. Die Seite weist ein nicht gekoppeltes Gerät nun an, collie pair auszuführen und den Code in den Einstellungen einzugeben. Sie leitet nie weiter, da der Pfad zu Ihrem Proxy gehört.
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 { ... }
    }
}

Überprüfen Sie den Endpunkt und die Caching-Regeln. $TOKEN ist ein Kopplungstoken, angefordert wie in Variante B; authorized ist true, wenn Ihr Header auf der Positivliste steht:

$ 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

---

Variante D: Externer Identity-Proxy über das Tailnet

Verwenden Sie dies, wenn Sie Datenverkehr über einen zentralen Tailscale-Ingress-Knoten leiten.

Ein Ingress-Knoten authentifiziert das Telefon am Rand des Tailnet, und Collie selbst antwortet nur auf dem Loopback-Interface.phoneingress nodeagent hostCollie127.0.0.1:8787httpsTLS + forward-auth,sets the device headerhttp, tailnet only —WireGuard already encrypts ittailscale serve, tailnet-onlyloopback
Ein Ingress-Knoten authentifiziert das Telefon am Rand des Tailnet, und Collie selbst antwortet nur auf dem Loopback-Interface.

Die Anforderungen aus Variante B gelten, mit der Ausnahme, dass der Proxy auf den Tailscale-HTTP-Endpunkt des Hosts statt auf Loopback zielt.

Tailscale-ACLs (erforderlich)

Da tailscale serve Client-Header unverändert weiterleitet, müssen Tailscale-ACLs den direkten Portzugriff ausschließlich auf den Ingress-Knoten beschränken.

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-Konfiguration (.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 entspricht der Identität des Ingress-Knotens, nicht der des Endbenutzers dahinter.

Überprüfung:

Von einem Tailnet-Peer ohne Ingress. $TOKEN ist ein Kopplungstoken, angefordert wie in Variante 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

Auf dem Agenten-Host sendet eine Anfrage keinen Header, daher ist authorized selbst mit einem gültigen Token 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}

---

Variante E: Jedes andere Mesh oder Tunnel (NetBird, ZeroTier, Cloudflare Tunnel)

Richten Sie den Tunnel/Proxy auf 127.0.0.1:$COLLIE_PORT aus.

Collie-Konfiguration (.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

Regeln:

  1. Wenden Sie die Proxy-Regeln für Variante B an.
  2. COLLIE_TRUSTED_USER ist ohne Tailscale inaktiv. Verwenden Sie COLLIE_DEVICE_HEADER oder die Authentifizierung des Tunnels.
  3. Verwenden Sie einen statischen Hostnamen, damit das PWA-Caching und COLLIE_PUBLIC_HOSTS gültig bleiben.

---

Eingangstüren, Produkt für Produkt

Grobe Konfigurationen für die Eingangstüren, nach denen häufig gefragt wird. Jede endet an derselben Stelle: HTTPS auf einem festen Hostnamen, weitergeleitet an 127.0.0.1:$COLLIE_PORT auf diesem Host.

Hinweis. Web Push, das Mikrofon und die Installation auf dem Startbildschirm erfordern HTTPS mit einem Zertifikat, dem das Telefon vertraut. Über einfaches HTTP liest und antwortet Collie weiterhin in einem Browser-Tab, und diese drei Funktionen bleiben deaktiviert (Spracheingabe und Web-Push).

Jede Eingangstür außer Tailscale verwendet dieselben drei Zeilen in Collies .env, gefolgt von einem Neustart:

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

Führen Sie den Proxy oder Tunnel auf demselben Host wie Collie aus. Collie weist Verbindungen ab, die nicht vom Loopback stammen. Ein Proxy auf einem anderen Rechner erfordert daher stattdessen Variante D.

Koppeln Sie das Telefon anschließend mit collie pair. Keine dieser Türen prüft, welches Gerät tippen darf; das übernimmt die Kopplung (Ein Gerät koppeln).

TürStandardmäßig privatHTTPS-ZertifikatZusätzlicher Schritt
Tailscaleja, nur TailnetTailscale stellt es ausHTTPS-Zertifikate einmalig aktivieren
Cloudflare Tunnelnein, öffentlichCloudflare stellt es ausCloudflare Access-Anmeldung vorgeschaltet
Nginx Proxy Managerhängt vom Host abLet's EncryptHost-Netzwerk für den Container
NetBirdja, nur Meshkein eigenesein Proxy auf dem Host, DNS-Challenge
Caddyhängt vom Host abLet's Encrypt, automatischein DNS-Plugin für einen privaten Namen

Tailscale

Die Standardeinstellung und die einzige Tür, die Collie selbst verwaltet: collie start führt tailscale serve auf Ihrem MagicDNS-Namen aus.

  1. Öffnen Sie in der Tailscale-Admin-Konsole DNS, aktivieren Sie MagicDNS und dann HTTPS aktivieren.
  2. Starten Sie Collie, das sich selbst im Tailnet veröffentlicht.
    collie start
  1. Setzen Sie COLLIE_TRUSTED_USER in .env auf Ihr Tailnet-Login und starten Sie dann neu.
    collie restart

Variante A enthält den Rest. Setzen Sie COLLIE_SKIP_SERVE für diese Tür nicht.

Verwenden Sie Niemals tailscale funnel. Funnel stellt den Port ins öffentliche Internet.

Cloudflare Tunnel

cloudflared stellt eine ausgehende Verbindung zu Cloudflare her, daher wird auf Ihrem Router kein Port geöffnet. Der Hostname ist öffentlich, daher muss ein Login davorstehen.

  1. Melden Sie sich an, erstellen Sie einen Tunnel und verweisen Sie einen Hostnamen darauf.
    cloudflared tunnel login
    cloudflared tunnel create collie
    cloudflared tunnel route dns collie collie.example.com
  1. Schreiben Sie ~/.cloudflared/config.yml mit der von create ausgegebenen Tunnel-ID.
    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. Installieren Sie ihn mit dieser Konfiguration als Dienst, damit er einen Neustart übersteht.
    sudo cloudflared --config ~/.cloudflared/config.yml service install
  1. Fügen Sie in Cloudflare Zero Trust eine selbst gehostete Anwendung für collie.example.com hinzu, mit einer Richtlinie, die nur Ihre E-Mail-Adresse zulässt.

Ein im Zero-Trust-Dashboard erstellter Tunnel funktioniert ebenfalls: Setzen Sie den Dienst seines öffentlichen Hostnamens auf http://127.0.0.1:8787. cloudflared leitet den öffentlichen Hostnamen als Host weiter, was COLLIE_PUBLIC_HOSTS erwartet.

Warnung. Ohne Schritt 4 erhält jeder, der den Hostnamen findet, eine Shell auf Ihrem Rechner. Cloudflare Access ist das Schloss der Tür; die Kopplung entscheidet anschließend, welches Gerät tippen darf.
  1. Lassen Sie Collie die Sperre prüfen. Kopieren Sie in der Access-Anwendung das Application Audience (AUD)-Tag. Fügen Sie es sowie Ihren Teamnamen in .env ein und starten Sie dann neu.
    COLLIE_ACCESS_TEAM=myteam          # from myteam.cloudflareaccess.com
    COLLIE_ACCESS_AUD=<the AUD tag>

    Jede Anfrage über den Tunnel muss nun ein Token enthalten, das Cloudflare für diese Anwendung signiert hat. Wenn die Access-Anwendung gelöscht wird, eine Bypass-Regel erhält oder noch nicht alle Edge-Knoten erreicht hat, lehnt Collie die Anfrage ab, anstatt die Bereiche anzuzeigen. Die Prüfung der Audience ist wesentlich: Jede Access-Anwendung innerhalb eines Teams signiert mit demselben Schlüssel, daher wird auch ein Token für eine andere Anwendung abgelehnt.

    Collie ruft die öffentlichen Schlüssel des Teams beim Start sowie stündlich ab. Bis der erste Abruf gelingt, erhält jede Remote-Anfrage 503. Schlägt ein späterer Abruf fehl, behält Collie die vorhandenen Schlüssel bei. Definieren Sie beide Zeilen oder keine: Eine Zeile allein führt zur Ablehnung jeder Remote-Anfrage. In jedem Fall nennt das Bridge-Protokoll das Problem in einer einzelnen Zeile; prüfen Sie daher das Protokoll nach dem Neustart.

    Ausschließlich ein Prozess auf diesem Rechner umgeht das Token: eine Anfrage an 127.0.0.1 oder localhost, die weder Cf-* noch einen Forwarding-Header (X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto, Forwarded, X-Real-Ip) enthält. Zum Schreiben ist weiterhin eine Kopplung erforderlich. /api/health und die Crew-Links benötigen ebenfalls kein Token. collie doctor ist ein solcher lokaler Aufrufer; ein grüner Status bei doctor belegt daher weder das Laden der Schlüssel noch die Gültigkeit eines Tokens. Rufen Sie den Hostnamen vom Smartphone aus auf, um dies zu überprüfen. ADR 0081 erläutert die Hintergründe.

    Beachten Sie diese Regeln bei aktivem Gate:

    - Nur ein Eingang gleichzeitig. Wenn Access aktiviert ist, benötigt eine Anfrage über tailscale serve, Caddy oder Traefik ebenfalls ein Token, da diese standardmäßig einen Weiterleitungs-Header hinzufügen. Ein Rechner, der sowohl einen Tunnel als auch einen Tailnet-Namen (tailscale serve) bereitstellt, verliert den Tailnet-Zugang für Browser: Diese Anfragen enthalten kein Access-Token und erhalten 401. - Kein zweiter Proxy zwischen cloudflared und Collie, der Anfragen lokal erscheinen lässt. Einer, der Host in 127.0.0.1 oder localhost umschreibt und die Cf-*- sowie Weiterleitungs-Header entfernt, tut dies absichtlich. Ein reines nginx proxy_pass http://127.0.0.1:8787 tut dies standardmäßig: Es sendet Host: 127.0.0.1:8787 und fügt keinen Weiterleitungs-Header hinzu, sofern Sie keinen konfigurieren. Diese Anfragen überspringen das Token. Richten Sie cloudflared direkt auf Collie aus. - Fügen Sie keine Access-Bypass-Regeln für Collie-Pfade hinzu, nicht für das Manifest, den Service-Worker oder die Icons. Access fügt das Token jeder Anfrage hinzu, die es durchlässt. Ein umgangener Pfad enthält kein Token, daher antwortet Collie mit 401.

Access leitet zum Anmelden auf /cdn-cgi/access/ um. Halten Sie sw.js und index.html ungecacht, wie in Routing für /auth/ und Caching beschrieben.

Nginx Proxy Manager

Ein Reverse-Proxy mit Web-UI und integriertem Let's Encrypt. Er läuft in Docker, und innerhalb eines Containers ist 127.0.0.1 der Container, nicht Ihr Host.

  1. Weisen Sie dem Container das Host-Netzwerk zu, damit er Collie über Loopback erreichen kann.
    services:
      npm:
        image: jc21/nginx-proxy-manager:latest
        network_mode: host
        volumes:
          - ./data:/data
          - ./letsencrypt:/etc/letsencrypt
  1. Öffnen Sie die Benutzeroberfläche auf Port 81 und fügen Sie einen Proxy Host hinzu: Domain collie.example.com, Schema http, Weiterleitung an 127.0.0.1, Port 8787.
  2. Fordern Sie auf dem Reiter SSL ein Let's-Encrypt-Zertifikat an und aktivieren Sie SSL erzwingen.

Lassen Sie Assets zwischenspeichern deaktiviert, damit der Service-Worker aktualisiert werden kann. Collie pollt über einfaches HTTP, daher spielt der Schalter Websockets-Unterstützung keine Rolle.

Für einen Namen, der nur innerhalb Ihres Netzwerks aufgelöst wird, wählen Sie Eine DNS-Challenge verwenden auf dem SSL-Reiter. Let's Encrypt kann einen privaten Host nicht über HTTP erreichen.

Warnung. host.docker.internal mit host-gateway funktioniert hier nicht. Es erreicht die Docker-Bridge-Adresse, und Collie lauscht ausschließlich auf 127.0.0.1.

Wenn der Proxy für das Internet offen ist, fügen Sie dem Proxy-Host eine Zugriffsliste hinzu, oder lassen Sie die Ports 80 und 443 auf dem Router geschlossen und erreichen Sie den Host über ein VPN.

NetBird

NetBird ist ein WireGuard-Mesh, ähnlich wie ein Tailnet. Es überträgt den Datenverkehr, und ein Reverse-Proxy auf dem Host stellt HTTPS bereit.

  1. Fügen Sie den Host und das Telefon demselben NetBird-Netzwerk hinzu.
    netbird up
  1. Leiten Sie einen DNS-Eintrag für collie.example.com auf die NetBird-Adresse des Hosts (100.x.y.z).
  2. Führen Sie Caddy auf dem Host aus, gebunden an diese Adresse, mit einem DNS-Challenge-Zertifikat.
    collie.example.com {
        bind 100.x.y.z
        tls {
            dns cloudflare {env.CF_API_TOKEN}
        }
        reverse_proxy 127.0.0.1:8787
    }

Die DNS-Challenge weist nach, dass Sie die Domäne besitzen, ohne dass Let's Encrypt den Host erreichen muss; der Name kann also auf eine private Adresse verweisen. Caddy benötigt das DNS-Plugin für Ihren Anbieter, beispielsweise xcaddy build --with github.com/caddy-dns/cloudflare.

Ein NetBird-Peer-Name wie host.netbird.cloud besitzt kein öffentlich vertrauenswürdiges Zertifikat; verwenden Sie daher eine Domäne, die Sie besitzen. Das gleiche Vorgehen funktioniert für ZeroTier oder jedes andere Mesh.

Caddy

Caddy bezieht und erneuert sein Zertifikat selbst. Auf einem Host, der auf den Ports 80 und 443 erreichbar ist, genügt diese Konfiguration:

collie.example.com {
    reverse_proxy 127.0.0.1:8787
}

Fügen Sie für einen privaten Namen den Block tls { dns … } aus NetBird oben ein. Ein öffentlicher Host benötigt zudem eine vorgeschaltete Anmeldung, wie in Variante C.

---

Collie unter einem Pfad bereitstellen

Ein einziger Release-Build bedient jeden Einhängepfad. Setzen Sie den Pfad in der Instanz-.env (oder [serve] base_path in config.toml), starten Sie neu, und die Bridge stellt die Anwendung, ihre Assets und /api/* unter diesem Pfad bereit:

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

Akzeptiert: /collie, collie/, /apps/collie/. Abgelehnt, mit einer Zeile im Protokoll und Verwendung des Wurzelverzeichnisses stattdessen: ein .- oder ..-Segment, Leerraumzeichen, ?, #. collie doctor gibt den Einhängepfad in der Zeile front-door aus.

Mit dem standardmäßigen Haupteingang veröffentlicht collie serve (und jedes collie start) tailscale serve --set-path=/collie/ statt des Wurzelverzeichnisses, sodass eine andere Anwendung / auf demselben Knoten behalten kann. Tailscale entfernt den Einhängepfad vor dem Weiterleiten, und die Bridge akzeptiert auch Anfragen, die ihn noch enthalten; ein Reverse-Proxy funktioniert daher auf beide Weisen. Um Ihre Konfiguration zu prüfen:

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

Hängen Sie die Bridge hinter Ihrem eigenen Proxy (Variante C) unter demselben Pfad ein. Caddy: handle_path /collie/* { reverse_proxy 127.0.0.1:8787 }. Nginx: location /collie/ { proxy_pass http://127.0.0.1:8787/; }. Wenn Sie COLLIE_PUBLIC_URL setzen, binden Sie den Pfad ein: https://collie.example.com/collie/.

Eine Änderung des Einhängepfads ist eine neue Anwendung auf dem Telefon. Gültigkeitsbereich und Identität der installierten PWA entsprechen dem Pfad, von dem aus sie installiert wurde. Entfernen Sie nach dem Ändern von COLLIE_BASE_PATH oder dem Zurückwechseln auf das Wurzelverzeichnis das Startbildschirm-Symbol und fügen Sie es von der neuen Adresse aus erneut hinzu; das alte verweist weiterhin auf einen Pfad, den die Bridge nicht mehr bedient. Die Bridge selbst benötigt lediglich einen Neustart, und collie serve verschiebt den Eingang selbstständig: Sie hebt die Zuordnung auf, die ihr Eintrag benennt, bevor sie die neue veröffentlicht.

---

Mehrere Collies auf einem Host

Um unabhängige Instanzen pro Benutzer auf einem gemeinsam genutzten System zu hosten (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-Prüfung für eigenständige Binärdatei: bin/collie url. URL-Prüfung für Herdr-Plugin: herdr plugin action invoke url --plugin herdr.collie.

Voraussetzungen:

  • Verwenden Sie getrennte Unix-Benutzer und separate Instanzen von herdr/Collie.
  • Setzen Sie COLLIE_TRUSTED_USER immer, um den Zugriff pro Port einzuschränken.
  • Initiale Tailscale-Bindungen erfordern eine Konfiguration durch den Operator: Führen Sie bin/collie serve (oder das Herdr-Äquivalent) mit Operator-Rechten aus.

COLLIE_SERVE_PORT legt den Eingangsport für eine Instanz fest. COLLIE_INSTANCE erstellt eine isolierte Instanz mit eigener Service-Unit und Konfiguration auf demselben Host (Mehrere Collie-Instanzen auf einem Host).

---

Mehrere Collie-Instanzen auf einem Host

Verwenden Sie dieses Setup, um ein zweites, separates Collie auf derselben Maschine auszuführen. Beispiele sind der Betrieb einer stabilen Version neben einer Arbeitskopie oder die Ausführung eines zweiten Multiplexers (tmux/zellij) neben dem von Herdr verwalteten Standard. Jede Instanz erhält einen dedizierten Port, eine Konfiguration, ein Zustandsverzeichnis und eine Service-Unit. Dies unterscheidet sich von Mehrere Collies auf einem Host, wo eine Instanz pro Unix-Benutzer zugewiesen wird. Hier führt ein Benutzer mehrere Instanzen aus.

Die zweite Instanz erstellen

Setzen Sie COLLIE_INSTANCE=<name>, um die Instanz zu benennen. Der Name muss [a-z0-9-]{1,16} entsprechen. Eine benannte Instanz erfordert zudem einen expliziten COLLIE_PORT. Das CLI bricht mit einem Fehler ab, wenn dieser Port fehlt; es leitet keinen Standardwert ab.

Erstellen Sie ~/.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 und COLLIE_STATE_DIR können sich alle in dieser .env-Datei befinden. Die zusammengeführte Umgebung löst die Instanz auf. HERDR_PLUGIN_CONFIG_DIR überschreibt das Verzeichnis, in dem das CLI nach der Konfiguration sucht.

Eine Service-Unit pro Instanz ausführen

collie start schreibt die Unit, wenn die Umgebung die Instanz festlegt. Der Operator schreibt sie nicht von Hand. Unter macOS schreibt es stattdessen eine launchd-plist unter ~/Library/LaunchAgents/. Die Unit setzt COLLIE_PORT, COLLIE_INSTANCE, COLLIE_PLUGIN_ROOT, HERDR_PLUGIN_CONFIG_DIR und EnvironmentFile=-<config dir>/.env. --instance <name> auf ExecStart existiert einzig, damit zwei Instanzen, die sich ein Binary teilen, ihre Bridges unterscheiden können (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

Das CLI verwaltet eine instanzspezifische tailscale serve-Handler-Datei, sodass die Ausführung von unserve auf einer Instanz das Mapping für eine andere nicht löscht.

Eine benannte Instanz über das CLI ansteuern

Jedes CLI-Verb (pair, devices, url, qr, crew …, logs, push-test, …) ermittelt seine Zielinstanz aus der Prozessumgebung. Setzen Sie COLLIE_INSTANCE, bevor Sie das Verb aufrufen:

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

Ohne COLLIE_INSTANCE fragt das CLI Herdr ab. Herdr erfasst nur das Plugin ohne Suffix, daher wird der Befehl für die erste Instanz ausgeführt. Setzen Sie COLLIE_INSTANCE immer explizit, wenn Sie mehrere Instanzen auf einem Host ausführen, um Änderungen an der falschen Instanz zu vermeiden.

Die Verweigerungsregel

Wenn COLLIE_INSTANCE gesetzt ist, aber herdr.collie-<name>/.env fehlt, bricht das CLI mit einem Fehler ab. Es greift nicht auf die Konfiguration einer anderen Instanz zurück.

Kopplung erfolgt pro Instanz

collie pair schreibt einen Kopplungscode in das Zustandsverzeichnis dieser Instanz. Der ausgegebene QR-Code kodiert die eigene URL dieser Instanz, da jede Instanz ihr eigenes Zustandsverzeichnis und über COLLIE_PUBLIC_URL oder ihren eigenen Port einen eigenen Zugang hat. Öffnen Sie die spezifische URL dieser Instanz auf dem Smartphone und geben Sie den Code unter Einstellungen → System → Gekoppelte Geräte ein. Ein für eine Instanz erstellter Code kann kein Gerät mit einer anderen Instanz koppeln (Ein Gerät koppeln).

---

Der Standby-Eingang: der Failover-Pfad einer Crew

Für Crew-Bereitstellungen. Konfiguriert einen vorab autorisierten deputy zur Übernahme, falls das lead unerreichbar wird (ADR 0027, ADR 0028, CREW_PROTOCOL.md §18).

Einstellungen für deputy und lead:

SchlüsselStandardFunktion
COLLIE_STANDBY_PORT(nicht gesetzt)Port für den Standby-Listener. Nicht gesetzt deaktiviert die Standby-Tür. Muss auf Lead und deputy übereinstimmen.
COLLIE_STANDBY_HOST127.0.0.1Bind-Adresse (127.0.0.1 für lokale Proxys, Overlay-IP für Remote).
COLLIE_STANDBY_ARM_MSmax(30000, 2.5 × COLLIE_POLL_IDLE_MS)Erforderliche Dauer der Lead-Inaktivität vor dem Scharfschalten.

Setzen Sie COLLIE_STANDBY_PORT sowohl auf dem Lead als auch auf dem deputy auf einen identischen, ungenutzten Port.

Die Voraussetzung: ein Hostname, zwei Backends

Lead und deputy müssen vom selben Ursprung bereitgestellt werden, um die PWA-Registrierung und die Geräteanmeldedaten zu teilen. Eine eigenständige Crew ohne einheitlichen Ingress stellt sich über bin/collie promote (oder Herdr: herdr plugin action invoke promote --plugin herdr.collie; CREW_PROTOCOL §14.4) wieder her.

Beispielkonfiguration für 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

Antworten der Zustandsprüfung: Lead gibt 200 zurück (ungleich 200, wenn abgesetzt); deputy gibt 503 zurück, bis er scharfgeschaltet ist, danach 200.

Einmalig einrichten, solange das System fehlerfrei läuft

Standalone-Einrichtung auf dem Lead:

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

Herdr-Einrichtung auf dem Lead:

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

Konfigurieren Sie auf dem deputy COLLIE_STANDBY_PORT=8788 und starten Sie neu. Stellen Sie sicher, dass alle Peers neu starten, um den deputy warrant zu laden. Überprüfen Sie dies unter https://collie.example.com/standby.

⚠️ Der deputy muss überwacht werden

Bei einer Übernahme schreibt der deputy den Status und beendet sich mit dem Statuscode 75 (EX_TEMPFAIL), um einen Prozessneustart (bridge/index.ts) auszulösen.

Stellen Sie sicher, dass der Supervisor bei Beendigungscodes ungleich null neu startet (systemd: Restart=always oder Restart=on-failure).

Der Ernstfall: das Betriebshandbuch

  1. Öffnen Sie https://collie.example.com.
  2. Wenn der Lead fehlerhaft ist, leitet der Proxy zu /standby weiter.
  3. Prüfen Sie den Standby-Status des deputy.
  4. Wählen Sie Übernehmen aus.
  5. Der deputy überprüft den Peer-Konsens, wendet den warrant an und beendet sich mit 75.
  6. Der Supervisor startet den Prozess neu; die PWA lädt sich als neuer Lead neu.

Nach der Wiederherstellung:

  • Der vorherige Lead setzt sich beim Wiederverbinden selbst ab (CREW_PROTOCOL.md §8.4).
  • Aktualisieren Sie die Peer-Adresse des abgesetzten Knotens: Standalone bin/collie crew set-address <member> <host:port> oder Herdr herdr plugin action invoke crew --plugin herdr.collie set-address <member> <host:port>.
  • Weisen Sie einen neuen deputy zu: Standalone bin/collie crew deputy <member> oder Herdr herdr plugin action invoke crew --plugin herdr.collie deputy <member>.
  • Führen Sie crew rotate erst aus, wenn alle Mitglieder die Verbindung wiederhergestellt haben.

Diese Seite auf GitHub bearbeiten

The rest of the documentation