Releases: RobLe3/iicp-client-python
v0.7.37
Release 0.7.37 — operator identity:
- Signed display_name rename:
iicp-node operator rename <name>(#460) — change your public, mutable display name (shown on node detail + the recognition leaderboard) over the immutable operator_id, authenticated by your own operator key. - Password-encrypt the operator secret at rest:
iicp-node operator encrypt/decrypt(#460) — AES-256-GCM + PBKDF2-HMAC-SHA256, headless via $IICP_OPERATOR_PASSPHRASE. - Operator delegation + display_name sent at register (#463/#464).
Cross-impl verified against the PHP/Rust directory.
v0.7.7 — relay connect timeout (#359)
Adds a 10s application-level connect timeout to RelayWorkerClient so a TCP-reachable-but-not-accepting relay fails fast (instead of blocking ~75s+ on the OS default) and the reconnect loop retries. Also bundles the iter-1504 mypy type-annotation fixes (CI green).
v0.7.5 — port 9484 default + auto-increment
Default listen port is now the official IICP port 9484. The iicp-node CLI auto-increments to the next free port when 9484 is in use, so N nodes on one host bind 9484/9485/9486 → N pinholes (multiple models share one port). Skipped when --public-endpoint is set.
v0.7.3 — Container-aware NAT + auto relay election
v0.7.3 — Container-aware NAT + automatic relay election
Full automatic NAT choreography
This release completes the fully automatic NAT traversal flow described in the IICP ADR-041:
| Tier | Path | Environment |
|---|---|---|
| 0 | Direct IPv4 on NIC | VPS/cloud (Hetzner, DO, Vultr) |
| 1a | UPnP IPv4 port mapping | Home router, non-CGNAT |
| 1b | IPv6 GUA + UPnP AddPinhole | FRITZ!Box IGDv2, post-2010 routers |
| 1c | IPv6 GUA (no pinhole, warn) | FRITZ!Box error 606 workaround |
| 3 | Relay-as-last-resort (auto-elected) | CGNAT + no IPv6 |
New in v0.7.3
Container detection:
- Docker bridge containers skip UPnP (was querying Docker NAT, not home router)
- Detection:
/.dockerenv+ bridge IP range (172.16-31.x.x) +KUBERNETES_SERVICE_HOST - Set
IICP_PUBLIC_ENDPOINT=in Docker Compose or--network hostto enable UPnP
Automatic relay election (tier≥3):
- When CGNAT is detected with no IPv6 path, queries
/v1/discover?relay_capable=true - Elects relay deterministically: lowest load + SHA-256(nodeId:relayId) tiebreak
- Configures
relay_worker_endpointautomatically — no manual flag needed - TypeScript and Rust CLIs now have parity with Python
IPv6 without pinhole:
- When AddPinhole fails (FRITZ!Box error 606), IPv6 GUA is still advertised
- Clear warning with manual instructions (FRITZ!Box → Network → Firewall → IPv6)
Research sources
- RFC 8445 ICE candidate ordering
- Tailscale DERP relay-first pattern
- miniupnp #600 (AddPinhole 606 = temporary address selected)
- FRITZ!Box IGDv2 documentation
v0.7.2 — NAT auto-detection on by default
v0.7.2 — NAT auto-detection on by default
What changed
For operators: you no longer need to set --auto-detect-nat or IICP_AUTO_DETECT_NAT=true. The SDK now runs NAT detection on every startup automatically. To opt out: IICP_AUTO_DETECT_NAT=false.
External IP probe: defaults to https://api.ipify.org so FRITZ!Box/CGNAT detection works out of the box (FRITZ!Box sometimes returns 0.0.0.0 from UPnP GetExternalIPAddress; the probe URL provides the real WAN IP).
IPv6 pinhole warning: when the router rejects AddPinhole (e.g. FRITZ!Box error 606), the SDK now logs a clear warning explaining what to do — either open the firewall port manually or use relay-as-last-resort (IICP_RELAY_WORKER_ENDPOINT).
Tier ≥3 auto-mesh: when NAT detection returns unreachable (CGNAT + no IPv6 path) and no relay endpoint is configured, mesh is automatically enabled so relay-capable peers can be discovered via gossip.
NAT detection tier summary
| Tier | Method | When |
|---|---|---|
| 0 | Direct (public IPv4 or operator-configured endpoint) | VPS/cloud or manual --public-endpoint |
| 1 | UPnP/IGD port mapping → CGNAT check → IPv6 fallback | Home router with UPnP |
| 2 | IPv6 GUA + UPnP IGDv2 AddPinhole | FRITZ!Box with IGDv2 + IPv6 |
| 3 | Relay-as-last-resort | CGNAT + no IPv6 |
| 4 | Unreachable | No path available |
v0.7.1 — CLI node ID fix
v0.7.1 — Patch: CLI node ID fix + custom name support
Bug fixes
- CLI: auto-generated node_id now uses
uuid4()(wassdk-model-hex8format which failed directory heartbeat validation — directory requires UUID format fornode_id) - CLI: custom
--node-idnames are now truncated to 36 chars to fit the directory's CHAR(36) column
Directory (v1.9.26–v1.9.28, deployed)
- Heartbeat now accepts alphanumeric custom node names (not just UUID), matching registration validation
- Registry tier display fixed: nodes with <100 completed tasks show Bronze (not Silver) — consistent with node detail view
- Registry detail page now accessible for custom-named nodes (
/v1/registry/nodes/my-home-node)
v0.7.0 — Relay-as-last-resort NAT traversal
What's new in v0.7.0
Relay-as-last-resort (ADR-041 tier-3, #341)
Enables nodes behind CGNAT or symmetric NAT to remain reachable via an outbound relay tunnel:
- R1 — Tunnel substrate: new
RELAY_BIND/RELAY_ACKIICP frames;RelaySessionRegistry+RelayAcceptServeron port 9485 - R2 — Worker lifecycle:
RelayWorkerClientconnects outbound, handleson_bindto re-register with relay endpoint (turn_relay) and restore mesh connectivity - R3 — Deterministic election:
PeerManager.elect_relay()selects lowest-load relay-capable peer, with SHA-256 tiebreak for stable distribution
Bug fixes
- BUG-3: Python node shutdown no longer raises RuntimeError from closed event loop
- BUG-6: Port probe before directory registration prevents bind failures
- GAP-6: Ollama model auto-detect (tries /api/tags then /v1/models)
v0.6.0 — adapter parity
Adapter feature parity (#340/#356): dedicated vLLM + llama.cpp backends + selector, QoS-aware admission, availability scheduling, policy trio (TokenValidator, IdempotencyGuard opt-in, trust auditor), mesh gossip + /v1/peers (HMAC) + /v1/relay. register() stashes node token (BUG-5). 185 tests; local E2E verified.
v0.5.7 — NAT tier-0 auto-detect + external tunnel detection
What's new in 0.5.7
NAT traversal improvements (full parity across Python/TypeScript/Rust)
- Tier-0 v4 auto-detect: Bare-metal VPS nodes (Hetzner, DigitalOcean, Vultr) automatically detect a public IPv4 on their local interface — no
--public-endpointneeded. - Tier-4 external tunnel auto-detect: If ngrok, tailscale funnel, or cloudflared is running, the SDK detects the public HTTPS URL automatically. Also supports
IICP_TUNNEL_URLenv var override. - Tier-0 IPv6 pinhole (from 0.5.6): Ranked GUA retry loop for FRITZ!Box compatibility.
- AddAnyPortMapping fallback (from 0.5.6): Dynamic port assignment when the IGD rejects the default port.
Bug fixes (from 0.5.6)
- Heartbeat URL corrected:
/v1/heartbeat(was double-prefixed to/api/api/v1/heartbeat) - Heartbeat
Authorization: Bearer <token>header added (was missing → 401)
v0.5.6 — Coordinated SDK release (init wizard, IPv6 pinhole, graceful shutdown, dep checker)
Coordinated 0.5.6 release across all three SDKs (Python, TypeScript, Rust).
Highlights since v0.5.4:
iicp-node initwizard + persistent~/.iicp/operator and node identity (stablenode_idsurvives restarts — #215).- IPv6 detection (#342) and ADR-043 §10 IPv6 fallback path — when IPv4 is CGNAT'd or has no UPnP, advertise the IPv6 GUA instead.
- UPnP IPv6 firewall pinhole (#343, ADR-043 §5) via
WANIPv6FirewallControl::AddPinholeon IGDv2 routers. Inbound TCP is opened automatically when v6 fallback is selected. - Graceful shutdown on SIGINT/SIGTERM:
deregisterthe node + revoke the UPnP pinhole, so the directory promptly demotes the node and the router firewall isn't left with a stale entry. - Dependency checker + auto-install in
iicp-node init(#346) — probes the backend, reports optional deps, offers topip install iicp-client[<extras>]in a single keystroke. sdk_language=python+sdk_versionnow surfaced to the directoryregisterpayload, so dashboards render a language badge on discover.
All three SDKs now expose the same operator UX. Switching from Python to TypeScript or Rust no longer requires a config rewrite — the ~/.iicp/operator.json and ~/.iicp/nodes/<name>.json schemas are byte-identical.