All notable changes to the IICP Python SDK (iicp-client).
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning
within the scope of the IICP Software axis (see VERSIONING.md
in the main repo).
- Accountless Cloudflare Quick Tunnel startup now opens a process-local cooldown when
cloudflaredreports rate limiting (429/1015) so retries do not hammer Cloudflare; operators should use a named tunnel orIICP_PUBLIC_ENDPOINTfor persistent relay infrastructure.
- Quick Tunnel verification now avoids destructive tunnel rotation when local
DNS has not resolved a freshly-created
trycloudflare.comhostname yet but Cloudflare DoH already publishes the A/AAAA record. - Startup errors now include the last Cloudflare output line, making rate-limit responses such as HTTP 429 visible in operator logs.
- Added
IICP_TUNNEL_DEAD_POLICY=auto|retry|exit|log-onlyso operators can choose whether confirmed Quick Tunnel Dead state retries, exits, or only logs. - Generated launchd/systemd units and Docker images now set
IICP_SUPERVISED=1; defaultautoexits non-zero under a supervisor so launchd/systemd/Docker can restart instead of leaving a publicly unreachable process alive. - Foreground/manual runs keep retrying with backoff by default, preserving a low-friction local development experience.
- Dockerfiles default to
IICP_SUPERVISED=1and theautodead policy, matching generated launchd/systemd service units. - README and contributing docs now describe Docker restart-policy expectations, current issue trackers, and the one-time manual-upgrade caveat for nodes older than 0.7.67.
- Quick Tunnel providers now mark themselves unavailable while the public tunnel URL is in twilight/recovery and only re-register a rotated URL after public
/iicp/healthverifies. - Added tunnel state reporting (
ready,twilight,recovering,dead) so service heartbeats reflect real public reachability instead of local process liveness.
- Consumers now skip keyless discovered nodes by default and refuse plaintext when no keyed node remains.
- Transitional plaintext requires explicit
IICP_CX_ALLOW_PLAINTEXT=1for debugging only.
- Normal
iicp-node servenow starts the background self-updater, matching the service-managed and cross-SDK unattended behavior. - Auto-update checks default to hourly and report update evidence in heartbeats.
- Added regression coverage for transitional directory responses that contain both canonical
cx_public_keyand deprecatedpublic_key; Python already preferscx_public_keyand encrypts to the canonical key. - Retains browser/routing signal parsing for directory v1.10.50+.
- Consumers prefer canonical
cx_public_keyand treat a directorypublic_keyfield as a deprecated alias, so keyed live nodes are encrypted instead of receiving the transitional plaintext fallback warning. - Added regression coverage for the alias path.
- Provider nodes now persist an X25519 CX key locally and advertise the public half as
cx_public_keyduring registration. POST /v1/taskdecrypts incomingiicp_confenvelopes before invoking the task handler, closing the missing provider-side half of mandatory payload confidentiality.- Added regression coverage for CX key advertisement and encrypted task handling.
- Tier-3+ reachability now tries the node's own Quick Tunnel before electing a third-party relay;
--no-tunnelretains relay-first behavior. - The background updater performs its first check within five minutes of startup, then returns to the configured cadence.
--tunnelhelp now describes the actual tunnel-first reachability order.- Reachability order is produced by the same pure planner covered by unit tests.
- Added a targeted test for the updater's initial-delay rule.
- #10 (serve goes offline):
cli.pycallednode.node_hmac_key()but it's a@property→'str' object is not callableon 0.7.61, failing serve registration across all retries so the node never reached a registered state (heartbeats stopped, directory marked it offline) though the server-side register succeeded. Now read as the property attribute. Regression-tested.
- IICP-CX payload encryption is now on by default with no opt-out: the client always encrypts
to a node advertising a
cx_public_key(theuse_confidentialityflag is a deprecated no-op). The directory, relays, and network see only ciphertext. A node not yet advertising a key gets a transitional plaintext warning during rollout. The executing node still decrypts to run the model (run locally for full privacy). - Added Tier-2 response-encryption primitives (
encrypt_response/decrypt_response) — not yet wired into the task flow.
- The
--tunnelwatchdog now actively health-checks the tunnel's OWN public URL (GET/iicp/healththrough the Cloudflare edge) every 30s, not just watch for the cloudflared process to exit. A Quick Tunnel can keep its process alive while its edge connection drops, leaving a dead public endpoint the directory still serves. After 3 consecutive unreachable probes the watchdog restarts cloudflared → new URL → re-register; the respawn cap resets when a fresh tunnel passes health, so a long-running relay self-heals indefinitely. Parity with Rust/TS.
- A node running
servenow keeps itself current automatically: it periodically checks the registry and, when a newer release is published,pip install --upgrades and re-execs onto the new version in covered service paths — no operator intervention. Early Docker/normal-serve coverage was hardened in 0.7.67, so older nodes may need one manual upgrade/restart first. Default-on; opt out withIICP_AUTO_UPDATE=0. Check cadence viaIICP_AUTO_UPDATE_INTERVAL_S(default 1h, min 5m). Loop-safe (post-upgrade the running version equals latest) and failure-isolated (a failed upgrade never restarts or crashes the node).
- Expand the
mcp-gatewaydangerous-tool denylist backstop (red-team pass 3) — broaden the set of shell/exec/interpreter tool names refused by default when exposing an MCP server as a mesh node, reducing the chance a permissive MCP server leaks an arbitrary-execution tool.
- Per-Origin
/v1/taskrate limit (F4, #524) — caps browser-origin task dispatch (the CORS confused-deputy vector); non-browser callers (the operator's own authed traffic) are never throttled. 429 IICP-E023; default 120/60s,IICP_TASK_RATE_LIMIToverrides (0 disables).
- The node now sends
current_node_tokenon re-registration when it holds a cached token, so an endpoint change after a tunnel/CGNAT rotation is accepted via the directory's IICP-E050 ownership path. Additive + backwards-compatible (directory accepts-but-does-not-require it).
- The relay caps concurrent worker sessions (default 256); new binds past the
cap are rejected (HTTP 503
IICP-E039/ TCPRELAY_ACKerror), closing a bind-flood memory-exhaustion DoS. A rebind of an existing worker_id is exempt.
- Read-only check for a newer published release (numeric version compare) with the exact upgrade command. Exit 10 when a newer release exists, 0 otherwise.
- When every NAT path fails (no direct endpoint, no UPnP pinhole, no IPv6
GUA, no relay-capable peer in the directory), the node now exposes itself
via a zero-account Cloudflare Quick Tunnel automatically: detect
cloudflaredon PATH (never auto-installed — one actionable install hint when missing), spawn it, register the issuedhttps://*.trycloudflare.comURL as the endpoint (transport_method=external_tunnel), supervise the child (bounded respawn ×3), and tear it down with the node on every exit path. --tunnelforces the rung regardless of NAT tier (e.g. to get an https endpoint for browser consumers without touching the router);--no-tunnel/IICP_TUNNEL=0disables the automatic escalation.
(Also includes the never-published 0.7.55 changes: MCP gateway as a built-in
iicp-node mcp-gateway feature.)
- Relay-capable nodes accept browser-compatible workers over plain HTTP:
POST /v1/relay/bind(bearer session token; 409 on alive-rebind, #510 interim-C),GET /v1/relay/pull(long-poll ≤25 s),POST /v1/relay/result,POST /v1/relay/unbind— same session registry as TCP RELAY_BIND workers. - Path-scoped worker endpoints
{relay}/v1/relay-for/<worker_id>/v1/task+/iicp/health: published consumers route through the relay with no client changes. RELAY_ACK gains additive field 4 (the relay's HTTP task port).
- Relay workers previously advertised the bare relay endpoint, so consumer dispatches executed on the relay itself instead of forwarding (and used the non-HTTP accept port). Workers now register the path-scoped endpoint.
- All node responses carry
Access-Control-Allow-Origin: *and every path answersOPTIONSpreflights. Web pages (e.g. iicp.network/browser-node) are first-class consumers: an https-exposed node now serves browser dispatches directly. No new capability — CORS only ever gated browsers; curl was never restricted.
- Transient failures (network error, 5xx, undecodable body) are retried once after
a 2s pause — deploy windows / shared-hosting blips no longer surface as one-shot
CLI errors (
HTTP 500/bad response: error decoding response body). - All-nodes listing (bare
iicp-node creditswith multiple saved nodes): one node's failure no longer aborts the whole listing — every node is shown and the command exits non-zero with anN/M node(s) failedsummary.
- Each heartbeat tick compares the backend's live model list against the registered set and automatically re-registers when they diverge — directory registration no longer goes stale when Ollama loads/unloads models.
- #496 Phase-2 consumer token support.
models[]array on the/iicp/healthendpoint (#494).- #503 loud CLI notice when serving without an operator identity.
backend_url/backend_api_keyinNodeConfig— when set, each heartbeat probes the backend's live model list (/api/tagsfor Ollama,/v1/modelsfor OpenAI-compatible backends) and sendshealth_models=[...]in the heartbeat payload.- The directory (≥ v1.10.28) uses
health_modelsto filter?model=discover queries to nodes whose backend actually has that model loaded, eliminating stale-model routing. - Probe failures are soft — heartbeat still fires without
health_models(backward compat). - 3 behavior tests added (
test_serve.py).
proxynow listed iniicp-node --help+ all serve flags documented.- Every subcommand
--help/-hprints usage instead of crashing. - Friendly parse errors — unknown flags print
ERROR: …(exit 2) instead of tracebacks. iicp-node serve --model Xworks without--backend-url—localhost:11434default applied unconditionally.--no-auto-detect-natoff-switch;iicp-node helpprints usage;creditsauto-resolves single node. Cross-flavour CLI parity (3-C).
iicp-node proxy— a local compat gateway on127.0.0.1:9483. Speaks OpenAI (/v1/chat/completions,/v1/models), Ollama (/api/chat,/api/generate,/api/tags), and Anthropic (/v1/messages) and routes each request across the IICP mesh.iicp-node serve --with-proxy— co-host the proxy next to a provider node in one process.- CIP consumer gating in the proxy path —
IICP-E036→ 402,IICP-E022→ 503. - One client now does node + query + proxy; the standalone
iicp-proxypackage is retired.
- Maintenance + lockstep version alignment across Python/TS/Rust SDKs (3-C). No API changes.
backend_type="anthropic"— speaks the Anthropic Messages API directly; defaultsbackend_urltohttps://api.anthropic.com.- Audio modality detection — model names containing
audio,voxtral, oromniadvertiseinput_modalities: ["audio"].
- The heartbeat loop answers the directory's liveness challenge.
- The node signs an ed25519 operator delegation on
register.
build_capabilitiesadvertisesinput_modalities(text + image for vision models).
- A node advertises every intent its backend serves (chat + embedding).
--backend-api-key/IICP_BACKEND_API_KEY.
- Per-node pidfile;
--force/IICP_FORCEto take over.
- Registration retries with backoff; heartbeat loop re-registers on 401.
cip_gaterejects tool-execution-domain intents unless the operator opted in.
Node.transport: list[str]— protocols each node speaks.
- Heartbeat loop re-registers on node-unknown rejection (404/401/410).
iicp-node initdistinguishes optional capabilities from real problems.
- 0.5.3: CBOR wire-compat fix (integer keys, RFC 8949); 3×3 cross-SDK matrix verified.
- 0.5.2: ConcurrencyGate parity port (Tier 2 Item 5).
- 0.5.1: CONF self-conformance probes (Tier 2 Item 4).
- 0.5.0: ADR-019 declarative pricing + HMAC signing (Tier 2 Item 3).
See git log — the Tier 1 ports (transport_endpoint, IICP TCP, UPnP, openai_compat, NAT observability) and Tier 2 items (CIP policy, pricing, conformance, ConcurrencyGate) shipped across iter-1409..1440 of the main repo's FORGE loop.