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).
iicp-node operator dsr export --output FILErequests a short-lived directory challenge, signs it locally with the existing operator identity, and writes a redacted, owner-only receipt (mode0600on Unix).iicp-node operator dsr restrict --yesandiicp-node operator dsr anonymize --yesrequire explicit confirmation and report the directory retention boundary; they never transmit the operator secret or node tokens.- Docker release coverage now verifies the signed, redacted DSR flow against a fake directory for Python, TypeScript, and Rust alongside existing registration and recovery checks.
iicp-node serve --policy-manifest FILEsigns a local public policy document with the existing operator identity and advertises it on every registration and recovery re-registration.- Canonical JSON and Ed25519 output share a fixed known-answer vector with the TypeScript, Rust, and directory verifier tests.
- Relay operator guidance now documents the available strict directory-signed bind-ticket cutover instead of describing bind authentication as pending.
- Directory-signed relay bind tickets now require a signed 128-bit
jti; relay accept paths atomically consume it and reject replay after disconnect until ticket expiry. - Replay protection covers native RELAY_BIND and browser-compatible HTTP-poll binds without changing the opt-in strict-bind cutover.
- The packaged MCP gateway now denies all named non-benign risk classes unless explicit operator opt-in, caller authorization policy, an accepted sandbox profile, and redacted audit are configured together.
- Gateway registration now uses the current directory
capabilities/limitscontract and advertises only tools that passed the local risk gate. - Added a canonical operator-key signing helper for challenge-based acceptance and DSR self-service requests.
- Added a packaged canonical MCP risk taxonomy and redacted policy receipts that exclude tool arguments.
- Added
required_manifest_identity_levelto routing policies so strict callers can requiresigned_valid,operator_bound, orknown_operatornode policy manifests before any prompt is dispatched. - Rejected or rotated/revoked manifests fail closed with redacted routing-policy reasons and no task POST to the rejected node.
- Expanded the packaged
mcp-gatewaydangerous-tool denylist for public-unknown callers and redacted upstream MCP error details to avoid echoing tool-input or secret text. - Prefer short-lived ticketed route discovery and fall back only when an older directory explicitly lacks the endpoint.
- Refuse prohibited and high-risk declared intents locally from the packaged canonical risk taxonomy.
- Mark model responses as AI-generated and add the compatibility-proxy response header without changing standard response bodies.
- Successful provider re-registration now persists refreshed
node_tokenandnode_hmac_keyback to the saved node identity, so read-only commands such asiicp-node creditsdo not drift behind a healthy running node. iicp-node doctornow reports stale saved credentials separately from serving health and explainsbackend_coldas normal idle/warmup rather than an automatic restart condition.
- The background provider self-updater now bootstraps
pipwith stdlibensurepipwhen the active interpreter has nopip(common for pipx app venvs andpython -m venv --without-pip), so hourly upgrades no longer silently no-op forever. - Updater failures now preserve a heartbeat-visible
sdk_update_error_classsuch asensurepip_failed,pip_missing, orpip_upgrade_failed, making downlevel drift diagnosable from the directory.
- Added client-side routing policies that filter discovered nodes before any prompt is dispatched:
standard,sensitive,eu_restricted,strict_policy, anddebug_override. iicp-node querynow exposes--routing-profile,--region-allowlist, and--allow-remote-executor, and prints a plain privacy reminder that remote executors can read prompts they run.
- Provider heartbeat recovery now treats
http_ipv6+ self-attested + browser-unusable registry evidence as limited reach, so supervised nodes restart and retry Quick Tunnel or relay fallback instead of staying indefinitely in “Direct IPv6 — unverified”.
- Relay-capable nodes no longer fall back through another relay when their own public tunnel is cooling down or unavailable. They preserve relay capacity and, under supervision, fail visibly so launchd/systemd/Docker can retry the public route.
- Automatic relay election now excludes the node's own
node_id, avoiding self-election loops during transient Quick Tunnel rate-limit recovery. - Ordinary provider nodes can still use relay fallback as the last-resort path; this guard only applies to nodes explicitly advertising relay service.
- Added
iicp-node doctorso operators can check local health, directory presence, and the deterministic recovery action without reading raw logs. - Provider heartbeat loops now classify recovery state, re-register when directory evidence disappears, and only exit for supervisor restart after configured grace checks.
- Docker images default to
IICP_PORT=8020, matching the exposed port and healthcheck used by low-friction container runs. - Release validation now includes the cross-SDK Docker gate for CLI help, no-network update checks, fake-directory registration,
/iicp/health,/v1/task, and heartbeat 401 recovery.
iicp-node creditsnow shows the operator wallet summary before per-node ledgers when the directory providesoperator_wallet, making earned and spendable credits easier to understand across multiple nodes.- JSON output preserves per-node ledgers while exposing the pooled wallet fields for tooling and future give-and-get accounting.
- Accountless Cloudflare Quick Tunnel creation now uses host-wide create spacing, a short create lease, and persistent provider-rate-limit cooldown so Dockerized or launchd-managed nodes do not retry-storm while Cloudflare limits recover.
- When tunnel creation is paced, cooling down, or held by another local node, providers fall back to the next safe reachability path instead of advertising an unverified public route.
- 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.