# Hokono — Unabhängige Knoten betreiben (Operator-Onboarding)

> **Hinweis (21.09.2026):** Für einen Mix gilt die kürzere [ANLEITUNG.md](ANLEITUNG.md): Binary und `authorities.txt` aus der Registry, Peers aus dem Consensus, Meldung als JSON-Block mit den Werten aus `pubkeys.txt`. `nodetool` ist dafür nicht nötig. Dieses Dokument beschreibt die Betriebsanforderungen und das Deskriptor-Format im Detail; einige verlinkte Dateien liegen im (noch privaten) Backend-Repository.


Dieses Dokument richtet sich an **unabhängige Betreiber**, die einen Hokono-Knoten (Mix, Gateway,
Entry-Relay oder Notifier) beisteuern wollen. Es deckt die Schritte E-6 (unabhängiges Entry-Relay)
und E-7 (Multi-Operator) ab und verbindet sie mit der signierten Directory (E-9).

## Warum „unabhängig" zählt

Hokonos Unverkettbarkeits-Aussage trägt nur, wenn **kein einzelner Principal** den gesamten Pfad eines
Nutzers kontrolliert. Dafür braucht es Knoten unter **wirklich unabhängiger** Kontrolle (eigene
Organisation, eigener Rechtsraum, eigene Infrastruktur). Selbst betriebene „Zweit-Identitäten" zählen
**nicht** und werden auch nicht als unabhängig vermarktet.

Modell: **ein gemeinsames Netz, viele unabhängige Betreiber** (eine signierte Directory, große
Anonymitätsmenge / gemeinsame Cover-Kohorte). Wer ein eigenes kleines Netz will, darf das (AGPL) —
mit dem ehrlichen Hinweis „kleines Netz = kleine Anonymitätsmenge".

## Lizenz & Reziprozität

- Server-Software ist **AGPL-3.0**. Kommerzieller Betrieb ist erlaubt (er vergrößert das Netz).
- Reziprozität: Wer Knoten beisteuert, dem stellen wir im Gegenzug unsere Knoten für Pfade zur Verfügung.

## Betriebsanforderungen (verbindlich)

- **No-Log**: keine Klartext-/Metadaten-Logs (die Daemons schreiben im Release nichts auf stdout;
  systemd `StandardError=null`). Kein Mitschneiden von Verkehr, Quell-/Ziel-IPs, Timing.
- **Schlüssel geheim**: der private Knoten-Schlüssel entsteht **auf dem Knoten** (der Daemon erzeugt ihn beim
  ersten Start selbst, siehe Schritt 2) und verlässt ihn nie. Er wird **nie übergeben** (auch nicht an uns) —
  Aufnahme läuft ausschließlich über die öffentliche Deskriptor-Zeile (DECISIONS #100/#102).
- **Build-Integrität**: veröffentliche den `build_hash` deiner Knoten-Binary (siehe unten), damit Clients
  Manipulation erkennen können (NODE_INTEGRITY.md).
- **Rechtsraum/Transparenz**: gib `jurisdiction` (z. B. `IS`, `DE`) und eine `operator`-Kennung an.

## Stand 2026-09-18: Was ein Betreiber heute konkret bekommt

Seit dem Mehr-Anbieter-Netz (DECISIONS #144) ist der Regelweg **das statische Binary aus der Registry**,
nicht der eigene Build. Beides ist gleichwertig — der Build ist reproduzierbar (`tools/build_static.sh`,
Alpine/musl, `linux/amd64`), und die Registry veröffentlicht genau die Hashes, die wir selbst ausrollen:

| Was | Wo |
|---|---|
| Binaries `mixnode`, `gateway`, `relay` (Entry), `gwclient` (Probe) | Registry `hokono.app/registry/` (SHA256SUMS + Build-Commit), identisch mit unserem Rollout-Bucket |
| Referenz-Betriebssystem | Debian 12 (bookworm), amd64, 1 vCPU / 1 GB reichen für Mix/Entry; Gateways 2 vCPU / 4 GB |
| Referenz-Härtung | `journald Storage=none`, kein SSH nach der Einrichtung, nftables nur mit den Rollen-Ports (Vorlage: `infra/terraform-mp/cloud-init.sh.tftpl`) |
| Unit-Vorlage | `tools/deploy_mp.sh` (`exec_for`/`prestart_for`): `ProtectSystem=strict`, `NoNewPrivileges`, `StandardOutput=null` |
| Schlüssel | entsteht beim ersten Start (`--keyfile`), optional versiegelt (`--keywrap`, DECISIONS #145); **rotiert alle 7 Tage mit 72 h Karenz** (`--rotate-days 7 --rotate-grace-hours 72`, J-13) |
| Replay-Schutz | persistent (`--replay-file`), nur Hash-Präfixe, kein Inhalt |

Ports und Protokolle: Mix `1789/tcp` (feste 4096-B-Frames), Gateway `7443/tcp` (Clients) + `1789/tcp`,
Entry `7443/tcp`. Die App wählt Pfade aus dem signierten Consensus (`dynamicInject`); dein Knoten
wird genutzt, sobald er im Consensus steht — ohne App-Update.

**Was du NICHT bekommst:** Zugriff auf unsere Steuerungsebene (SSM, Parameter Store). Ein fremder
Knoten braucht seinen eigenen Weg für Rollout und Wickelschlüssel — das ist Absicht, nicht Lücke.

## Schritte

### 1. Knoten-Software aus der Quelle bauen
Siehe `BUILDING.md`/`DEPENDENCIES.md`. Du erhältst die Binary deiner Rolle, z. B. `mixnode`, `gateway`,
`ohttp-relay` oder `notifier`.

### 1b. Oder: Binary aus der Registry nehmen und prüfen
```sh
curl -fsSLO https://hokono.app/registry/SHA256SUMS && curl -fsSLO https://hokono.app/registry/mixnode
sha256sum -c --ignore-missing SHA256SUMS          # muss "mixnode: OK" liefern
od -An -tx1 -j18 -N1 mixnode                       # 3e = x86-64 (Architektur-Gate wie im Rollout)
```

### 2. Knoten starten — der Daemon erzeugt seinen Schlüssel SELBST (kanonisch)
Zeige den Daemon einfach auf ein **noch nicht existierendes** Keyfile: fehlt es, erzeugt er das
Schlüsselpaar (X25519 ‖ ML-KEM-768) beim ersten Start, schreibt es **0600** und legt daneben
`pubkeys.txt` mit dem **öffentlichen** Material ab. Der private Teil verlässt den Knoten nie.
```sh
mixnode --port 1789 --keyfile /var/lib/hokono/node.key --peers <ip1,ip2,…> \
        --rotate-days 7 --rotate-grace-hours 72 --replay-file /var/lib/hokono/replay.bin
# schreibt /var/lib/hokono/node.key (0600, GEHEIM) und /var/lib/hokono/pubkeys.txt:
#   X_PUB=<hex>
#   K_PUB=<hex>
```
> Das ist der „als wärst du ein Fremder"-Weg (DECISIONS #100): kein Schlüssel wird jemals herumgereicht.
> `kemtool keygen` gibt es weiterhin als **offline**-Alternative (Air-Gap), ist aber nicht der Regelweg.

### 3. Deskriptor erzeugen (aus `pubkeys.txt`)
`nodetool` baut die kanonische Deskriptor-Zeile aus deinen **öffentlichen** Schlüsseln. Der
`build_hash` wird aus deiner Binary berechnet (oder per `--build-hash <hex>` gesetzt):
```sh
. <(sed 's/^/export /' /var/lib/hokono/pubkeys.txt)   # X_PUB / K_PUB laden
nodetool descriptor \
  --ip <öffentliche.ip> --port <port> \
  --role mix|gateway|relay|notifier \
  --xpub "$X_PUB" --kpub "$K_PUB" \
  --operator <deine-kennung> --jurisdiction <CC> \
  --binary <pfad/zur/binary>
# -> "<role> <addr_hex16> <xpub_hex> <kpub_hex> <operator> <jurisdiction> <build_hash_hex>"
```
Optional (Directory v2, CRYPTO_SPEC §6.4): `--region <pop-tag>` (z. B. `hz-nbg`), `--bandwidth-mbit <n>` (nominale
Betreiberangabe) und `--flags <hex>` (Bit 1 = `stable`). `published_ts` und das `running`-Bit setzt die Authority beim
Aufnehmen selbst — Angaben eines Betreibers dazu werden ignoriert.
Ports: Mix `1789/tcp`, Gateway-Onion-Exit `1789/tcp` (der Inter-Knoten-Onion-Transport ist seit
DECISIONS #124 **TCP**, nicht UDP; der TCP-Ingress 7443 gehört NICHT in den Deskriptor),
Entry-Relay `7443/tcp`. Prüfe nach dem Start, dass der Knoten wirklich auf **TCP** 1789 lauscht:
`ss -tlnp | grep 1789` (nicht `-ulnp`).

### 4. Deskriptor einreichen + Vetting
Schicke die Deskriptor-Zeile zusammen mit deinem Vetting-Nachweis (Unabhängigkeit, No-Log-Setup,
Rechtsraum) an die Directory-Authority. Vetting-Kriterien (Sybil-Grenzen, Unabhängigkeit, Jurisdiktions-
Diversität) legt die Authority fest.

### 5. Aufnahme in die Directory
Nach erfolgreichem Vetting nimmt die Authority die Zeile in die nächste Directory-Epoche auf und signiert
sie (m-von-n, siehe `infra/dirauth`). Clients laden die aktualisierte, signierte Directory — **ohne
App-Update** — und routen über deinen Knoten. Bei der m-von-n-Schwellenwert-Directory bestätigen mehrere
**unabhängige** Authorities dieselbe Liste; erst das trägt den Claim „kein einzelner Principal kontrolliert
das Netz".

## Authority-Seite (zur Einordnung)
Die Fremd-Deskriptor-Zeile wird **ohne jeden Zugriff auf den Betreiber-Knoten** aufgenommen — nur die
eine öffentliche Zeile wandert herein (`tools/add_operator.sh` validiert Format, dedupt, warnt bei
xpub-Kollision), dann signiert die Authority neu:
```sh
tools/add_operator.sh nodes.txt '<deskriptor-zeile>'   # fremde Zeile aufnehmen (kein Schlüssel!)
tools/publish_stufe0.sh nodes.txt authkeys.txt         # dirauth init/sign (m-von-n) + verteilen
# darunter, zur Einordnung:
#   dirauth init <epoch> <valid_until> nodes.txt   ·   dirauth sign <priv_b64> <index> <dir>
#   dirauth verify <m> <dir> authkeys.txt          (Client-Sicht: ≥ m distinkte Authorities → gültig)
```

## Pfadwahl & Diversität (umgesetzt)
Die clientseitige Pfadauswahl (`HokonoCrypto/PathSelection`) erzwingt bereits Operator-Disjunktheit
(paarweise verschiedene `operator_id`), Entry-Betreiber ≠ Gateway-Betreiber und gültigen Consensus
(hart, fail-closed), mit Jurisdiktions-/Ausfalldomänen-Streuung als gezählter Präferenz (DECISIONS #103).
Die Deskriptor-Felder `operator`/`jurisdiction` liefern die Grundlage. **Grenze:** `operator_id` ist
Selbstauskunft — die Regeln sind exakt so viel wert wie das Vetting (Schritt 4).
