Skip to content

Releases: RobLe3/iicp-client-python

v0.7.37

05 Jun 19:28

Choose a tag to compare

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)

29 May 05:55

Choose a tag to compare

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

28 May 21:55

Choose a tag to compare

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

28 May 21:05

Choose a tag to compare

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 host to 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_endpoint automatically — 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

28 May 20:50

Choose a tag to compare

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

28 May 20:39

Choose a tag to compare

v0.7.1 — Patch: CLI node ID fix + custom name support

Bug fixes

  • CLI: auto-generated node_id now uses uuid4() (was sdk-model-hex8 format which failed directory heartbeat validation — directory requires UUID format for node_id)
  • CLI: custom --node-id names 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

28 May 19:29

Choose a tag to compare

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_ACK IICP frames; RelaySessionRegistry + RelayAcceptServer on port 9485
  • R2 — Worker lifecycle: RelayWorkerClient connects outbound, handles on_bind to 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

28 May 17:24

Choose a tag to compare

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

27 May 16:52

Choose a tag to compare

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-endpoint needed.
  • 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_URL env 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)

27 May 14:14

Choose a tag to compare

Coordinated 0.5.6 release across all three SDKs (Python, TypeScript, Rust).

Highlights since v0.5.4:

  • iicp-node init wizard + persistent ~/.iicp/ operator and node identity (stable node_id survives 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::AddPinhole on IGDv2 routers. Inbound TCP is opened automatically when v6 fallback is selected.
  • Graceful shutdown on SIGINT/SIGTERM: deregister the 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 to pip install iicp-client[<extras>] in a single keystroke.
  • sdk_language=python + sdk_version now surfaced to the directory register payload, 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.