IC Host Tooling
Host-side Rust libraries for artifact inspection, local filesystem safety, admitted process execution and IC-specific format handling.
| Crate | Implemented source ownership |
|---|---|
| ic-host-artifacts | Bounded streams and hashing writers, exact byte comparison, SHA-256, gzip encoding/decoding, verified tar members and generic Wasm facts |
| ic-host-fs | Regular/no-follow reads, missing-suffix path resolution, streamed durable publication, private files and observed descriptor locks |
| ic-host-process | Executable resolution, digest/version admission, bounded capture, explicit child/group cleanup and Git observations |
| ic-host-tools | Candid extraction/normalization, ICP CLI response decoding and optional IC resource reports |
These run locally outside canisters. The ic-host prefix identifies their ecosystem;
the generic crates have no IC runtime dependency. Compression, archive and Wasm
support are optional features of ic-host-artifacts. Filesystem/process consumers
select the small default artifact dependency.
Response-only consumers can disable ic-host-tools' default features to exclude
the artifact/filesystem/process extraction dependencies. The default
candid-extraction feature preserves the existing Unix Candid API. Response
decoding is always available and needs only serde/serde_json.
The optional ic-limits feature compares Wasm facts with explicitly selected,
revision-bound IC resource limits, without filesystem/process dependencies when
defaults are disabled. See the 0.5 contract changes.
The compiled IC resource-report example inspects once and compares code-body bytes, defined functions and all globals against the explicitly selected reference revision. Run it with:
The numeric arguments select the hard raw-byte, section, export and custom-section bounds, followed by a soft raw-byte warning threshold. Text and JSON share the same failure exit decision; warnings alone do not reject. This is a composition recipe, not complete Wasm validation or evidence of deployed subnet limits.
tool::capture_command accepts a caller-configured std::process::Command when
the consumer owns executable admission. It shares bounded capture and direct-child
cleanup with AdmittedTool, whose exact digest/version checks remain in place.
ToolError::evidence and execution_error borrow retained diagnostics without
copying output; callers own presentation and recovery.
Capture and communication return CompletedExecution with a required exit
status, including nonzero and signalled exits. Use require_success() when zero
exit is required. Interrupted execution retains optional status in ExecutionEvidence.
child::OwnedChild::spawn starts a caller-configured command in a new owned
process group. It preserves command IO and supports polling, waiting, explicit
termination and cleanup during unwinding. Callers retain admission, readiness,
cancellation and application lifecycle policy. For deliberate background startup,
poll_exit retains cleanup ownership through result admission; explicit handoff
then reaps a successful leader without stopping the background group. See the
handoff contract and the
child cleanup contract
for exclusive ownership and descendant limitations.
spawn_with_cleanup selects CleanupPolicy::TermThenKill for caller-owned TERM
grace and bounded reaping. Communication failures and Drop use that same policy;
the default remains immediate KILL with synchronous reaping. Reap timeout retains
the owner for explicit recovery and does not start a background reaper. See the
bounded cleanup contract.
tool::capture_group_command combines bounded stdout/stderr capture with that
group cleanup for commands whose descendants must be signalled on exit or
failure. Ordinary capture_command retains direct-child cleanup. Both share one
capture engine; callers retain budgets, admission and external-effect recovery.
OwnedChild::spawn_direct preserves inherited or caller-selected process groups
for foreground communication and cleans up only the direct child. It uses the
same I/O engine; consumers retain terminal control, interruption handling and
descendant lifetime, including any descendant-held output pipes.
tool::communicate_child_with_observer adds live stdout/stderr observation to
communicate_child for an existing owner. Each stream chooses
OutputLimit::Terminate(bytes) to fail on overflow or OutputLimit::Truncate(bytes)
to forward all drained output while retaining only a bounded prefix.
The cancellation callback can also emit caller-scheduled heartbeats while silent.
Callback unwinding attempts cleanup even when the borrowed owner survives the
caller's panic handler.
Output observers return io::Result<()>; a sink error retains its original cause
and triggers the same child cleanup without a caller-side cancellation workaround.
All capture, communication and admission functions accept one OutputLimits
type with stdout, stderr and timeout: Option<Duration>. Select None for
long builds without an elapsed-time deadline, or Some(duration) for a positive
deadline. Retention and cleanup still apply; communication also polls cancellation.
Version, Git and Candid admission reject truncated captures. Other consumers can
use CompletedExecution::require_complete before parsing retained output.
Examples live with their owners: ic-host-artifacts has hash_serialized_json;
ic-host-fs has inspect_regular_file and
the artifact/file compositions inspect_archive, inspect_artifact, inspect_gzip;
ic-host-process has inspect_git and resolve_tool. ic-host-tools retains
extract_candid, inspect_install_limits and inspect_response; the response
example runs with --no-default-features. Example-only dependencies do not widen
the tools crate's production feature graph.
AdmittedTool::admit_version accepts a VersionSpec for caller-trusted installed
tools without a published binary digest. It records the installed identity,
checks the exact version and rejects byte drift before later execution. That
observed identity is not authentication; installation provenance stays with the
caller. admit with ToolSpec continues to require an exact digest pin.
Filesystem reads live under ic_host_fs::read, including optional bounded
no-follow reads and typed private-file admission. Durable publication and locks
remain under durable. Upgrading from 0.3 requires the
0.4 consumer changes.
On Unix, read::hash_file_no_follow(path, max_bytes) hashes regular files with
constant working memory, rejecting final symlinks and opened special files.
It shares the bounded stream hasher and preserves typed errors. Callers retain
trusted ancestors, concurrent-writer custody and any exact expected-length check.
durable::try_lock_regular_file_with_parents shares regular-file admission and
durable creation with the blocking lock API, but returns a native WouldBlock
I/O cause on contention. It preserves existing bytes and holds the lock through
the returned close-on-exec descriptor. Callers retain retry and waiting policy.
durable::open_regular_lock_file_with_parents exposes that same admitted file
without acquiring a lock. Consumers can select shared locking or compose it with
lock_exclusive_with_wait, keeping wait timing and explicit unlock policy local.
Existing lock files need no staging or parent write permission; durable creation
runs only when the entry is missing.
The 0.13 migration replaces the fixed-cadence pathname
progress helper with this composition; callers keep polling and reporting policy.
durable::open_private_regular_lock_file instead creates a missing sidecar with
owner-only permissions in an existing parent. It preserves existing bytes, inode
and permissions, returns an unlocked close-on-exec descriptor, and performs no
durability syncs. Callers retain locking, trusted ancestors and descriptor custody.
durable::write_named_with lets an external tool write to an owned absolute
staging path, then shares the normal durable publication engine. Callers validate
bounded output before success and retain typed producer/cleanup errors. See the
named-output contract before adopting it.
durable::write_validated_with closes the staging writer before a caller's
prepublication admission callback, enabling executable probes while preserving
the working destination on rejection. See the
closed-writer contract.
durable::write_with(path, options, producer) retains serializer errors while streaming through
the same engine, with explicit replace/create-only options and file permissions.
Byte writers also return NamedWriteError<io::Error>, preserving publication
state and separate cleanup failures. See the 0.10 migration.
durable::write_at_with borrows an admitted directory descriptor so publication
stays anchored to it even when its original path moves. Consumers retain path
admission, budgets and recovery; see the
publication contract.
With the artifact gzip feature, hash_gzip and hash_gzip_or_raw identify
decoded payloads without a full decoded allocation. gzip_matches compares
decoded bytes exactly with an expected slice. All retain explicit input/payload
bounds and strict single-member integrity checks; see the
gzip contract.
The four crates support Linux x86-64 and macOS 15 on Intel and Apple Silicon, with Rust 1.88.0 as their MSRV. See host qualification for validation commands and native evidence requirements.
For development, prepare the toolchain selected in rust-toolchain.toml and the bootstrap prerequisites and shell PATH, then run:
Setup is explicit; tools-check verifies prepared tools offline. Hook installation
separately activates the repository-local formatter. Shared Tooling owns these
common commands and executable installation; this
workspace owns the four libraries, package checks and publication policy.
Use make check, make clippy, make docs-check or make test with
PACKAGE=<crate> for focused work. Before delivering code, run make ci and
make msrv PACKAGE=<crate> for each affected package. Documentation-only changes
need link, consistency and diff checks. The host guide describes
feature-specific and hook checks; make help lists the available commands.
Git release and crates.io publication are separate operations; a tag does not establish registry availability. Read the release and publication guide for prerequisites, explicit command effects and retained-evidence recovery. Consumers adopt and qualify registry selections independently.
Read the extraction contract, the handoff and agent rules. Human and agent contributions follow the PR contribution rules.