All notable changes to foc-cli are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning. Pre-1.0, minor versions may contain breaking changes; they are always called out explicitly.
Nothing yet.
0.3.0 — 2026-08-05
External key custody. foc-cli can now hold a reference to a key kept in a secret manager instead of the key itself, closing the gap that left MCP and CI with no option but a key in the config file. Contains one behaviour change to wallet init (hence the minor bump).
wallet init --keyRef <provider>:<reference>— a third custody mode alongside a raw key and a Foundry keystore. The config stores only the pointer; the key is fetched into memory per command and never written to disk. Nothing prompts, so unlike keystore mode this works under the MCP server and CI.--keyProjectscopes the reference; omitted, the provider picks its own default.clawdiis the first provider. (#33)wallet balancenow reportskeySource(keyRef/keystore/privateKey/none), so a vault-backed setup is verifiable at a glance — the address proves which key signed, this proves where it came from. Never the key. (#33)- Preflight checks on every wallet-touching command, covering all three custody modes:
WALLET_NOT_CONFIGURED,MALFORMED_KEY_REF,KEY_REF_PROVIDER_MISSING,KEYSTORE_INTERACTIVE_ONLYandKEYSTORE_TOOL_MISSINGnow arrive as typed errors with actionable CTAs, instead of escaping as an untyped throw from inside key resolution or fromcast. The preflight does not resolve the key — that needs an authenticated provider and a round trip, and belongs at use time. A missing provider or missing Foundry is reported asretryableand deliberately carries no command: the wallet is intact and the fix lies outside foc-cli, so the only thing worth suggesting would have been one that discards a working configuration. (#33) wallet init --force. (#33)
wallet initno longer silently replaces a configured wallet. An explicit method used to overwrite whatever was configured, discarding a key that may have been the only copy. It now names what is at stake — the derived address for a private key, the path for a keystore, the reference for a key reference, never the key itself — and asks on a terminal, or fails withWALLET_ALREADY_CONFIGUREDand a--forceCTA in agent mode. The consequence stated is the one that actually applies: only a stored private key is destroyed by the swap, while a keystore file stays on disk and a vault key stays in the vault. Re-running the same method with the same value replaces nothing, and neither does adding or changing--keyProjecton a configured reference — both are never blocked. That CTA replays the caller's own options minus the secret: a--privateKeypassed on the refused invocation comes back as0x..., never the key. Automation that re-runswallet init --autoexpecting a fresh key must now pass--force. (#33)- Call-to-action guidance only offers a key-reference method when that provider's CLI is actually installed on the machine — suggesting a tool the caller does not have is a dead end. The reference docs still describe every provider. (#33)
- Key resolution accepts a
0x+ 64 hex value only when it stands on its own, and refuses output holding more than one. A loose match was the dangerous case: every 32-byte value is a valid secp256k1 key, so the leading 64 hex digits of a longer blob would have been accepted and signed with — as a different address — rather than failing. (#33) - Windows: an npm-installed provider helper is a
.cmd, which is a script rather than an executable and cannot be launched directly (Node has refused to since the fix for CVE-2024-27980). Those are now run throughcmd.exe, with references restricted to a character set the shell treats literally so a tampered config still cannot become command execution. Previously the PATH probe found the helper, the preflight passed, and the launch failed withEINVAL— reported as "not logged in / key missing / wrong project", none of which was true. (#33) - A reference that is itself a private key (
clawdi:0x…— the--privateKeymix-up with the provider prefix included) is refused at init and by the preflight rather than stored, and every message or result that quotes a reference redacts key-like runs first. A key is pure hex, so the character allowlist alone waved it through — stored under a field documented as safe to display, sent to the provider as a lookup name, and echoed verbatim into the use-time error envelope bound for the MCP result and the logs. (#33)
references/key-injection.md— identification table first (most of the time the answer is "already set up, run normally"), then setup, providers, what the mode does and does not protect, and the error catalog. (#33)references/integrations/clawdi-vault.md— the Clawdi recipe, including the per-project scoping that most often bites. (#33)references/keystore-setup.mdnow points at the key-reference mode as the automation-safe alternative it previously had no answer for. (#33)
0.2.0 — 2026-07-23
Agent-hardening release (#30), driven by a 609-invocation live smoke campaign on Calibration and a keystore field test. Contains one breaking change (hence the minor bump).
download <pieceCid>— retrieval as verification: the SDK validates received bytes against the piece CID, so a successful download is itself the proof of storage. Distinct error codes separate what retrying can fix (DOWNLOAD_FAILED) from what it cannot (INTEGRITY_MISMATCH,PROVIDER_NOT_FOUND,WRITE_FAILED,FILE_EXISTS). (#7)download --force— downloads no longer overwrite an existing output file: without the flag, an existing path fails withFILE_EXISTSand a ready-to-run overwrite CTA, and the MCP annotation declaresdestructiveHint: true. (#30)wallet costs --copies/--withCDN— the estimate prices the copies the next upload will create (default 2, likeupload); an empty wallet is priced as new datasets without touching provider selection. (#30)wallet initoutput schema — it was the only executable command without one, hiding its result contract from--schemaand MCPget_tool_details; a discovery test now walkssrc/commandsso the next schema-less command fails CI. (#30)docs --deep— searches the full ~1,800-page site sitemap (SDK API reference, changelogs), automatically invoked when the curated index has no matches. (#25)- MCP tool annotations on every command — human titles,
readOnlyHinton reads,destructiveHintonwallet init/dataset terminate/piece remove— plus descriptions that state consequences (uploads commit USDFC onchain, terminate is irreversible). (#28) - Fetch-all CTAs on paginated lists (
piece list,dataset details) alongside next-page. ADDRESS_NOT_ON_CHAIN—wallet balanceon a brand-new address now explains the address has no onchain history and suggestswallet fund, instead of dumping raw RPC internals. (#27)- Skill references: keystore setup (with creation recipe), mainnet funding, and a troubleshooting catalog of every error code with retry semantics. (#2, #4)
- Skills-consistency test: skill frontmatter version/license and every
foc-cli@x.y.zdoc pin are CI-pinned tocli/package.json.
- Uploads stream to providers (
upload,multi-upload) — peak memory stays flat at any file size; onlystatsizes are read up front. (#24) wallet costsno longer depends on endorsed-provider selection and estimates the upload it is quoting —--copiesstorage contexts (default 2), reusing active datasets before pricing new ones — instead of every active dataset, which made the quote scale with historical dataset count. (#26, #30)wallet costsis documented as an approximate estimate, not the "source of truth": upload's own provider selection (reachable unique providers, source/CDN metadata) may choose different datasets, changing creation fees, CDN lockups, anddepositNeeded— the upload re-quotes via its ownprepare()before spending. Full alignment is tracked as a follow-up. (#30)- Every chain-aware follow-up CTA now carries the active
chain, so a command run with--chain 314never suggests a follow-up that silently defaults back to Calibration — this reached destructive and fund-moving workflows (terminate, remove, deposit, pagination). (#30) wallet initexplicit methods (--auto,--keystore,--privateKey) now replace the configured wallet and clear the alternate credential;--autopreviously reportedalready_configuredbehind an existing key, and a configured keystore silently outranked a newly set key. (#30)--schemanow tells the truth: declared output schemas include theprocessLogstep trail andctablock that real agent-mode responses carry. (#28)- Both agent skills open with a self-discovery rule (run
<cmd> -hand<cmd> --schema --format jsonbefore first use) and document flag syntax truthfully: camelCase and kebab-case spellings both parse, and boolean flags are presence-only switches (--flag=falseis the explicit form). (#1, #3, #5) - Keystore mode documented as interactive-CLI-only — and now enforced: agent/MCP mode rejects
--keystore(KEYSTORE_INTERACTIVE_ONLY) and its init guidance no longer offers it, since the password prompt reads the terminal at use time. (#30) - README rewritten against the verified current surface — one-table command map, quick start ending in a download round-trip. (#29)
- Dependencies:
@filoz/synapse-sdk1.1.0,incur0.4.19 (fixes the doubled group prefix in--llmsoutput). (#23)
upload --withCDNalways failed against the pinned SDK —contextsandwithCDNare mutually exclusive upload options — and only after the funding transaction had run. CDN preference now rides in via context creation alone. (#30)- Both upload paths accepted directories, FIFOs, and devices at preflight (readability and size only), so the funding transaction could execute before streaming failed or blocked; non-regular files are now rejected (
NOT_A_FILE/FILE_READ_FAILED) before provider selection. (#30) wallet balanceactor-not-found guidance suggested the Calibration-only faucet on every chain; thewallet fundCTA is now testnet-only and mainnet gets prose directing funds to the address. (#30)wallet balancefresh-address humanization also recognizes the current Glif RPC wording (failed to apply on state with gas) — live-observed 2026-07-23, the olderactor not foundmatch alone had let the raw multicall dump return. (#27, #30)dataset create's success CTA recommendedpiece upload, a command that no longer exists; it now suggestsuploadanddataset details. (#30)wallet summaryrendered the funding runway as the same duration in five concatenated units (17468h 727d 103w 25m 2y); it now picks one unit (~2y). (#30)wallet costsfailed withNo endorsed provider availableand undercounted datasets sharing a provider (live check: 0.1058 → correct 0.1322 USDFC/month for 1 GiB). (#26)wallet init --keystoreaccepted a directory or arbitrary JSON and reported success; it now validates the path is a regular file (a FIFO could block the process at the synchronous read) containing an encrypted keystore with acryptoobject (KEYSTORE_INVALID). (#27, #30)- Keystore failures decode themselves: missing
cast(install Foundry),Mac Mismatch(wrong password), no terminal (keystore mode cannot run under MCP/CI). (#27) docsauto-fetch could return raw HTML for pages without a markdown mirror; both fetch paths now share the HTML backstop.- Interactive spinner no longer blanks step labels or leaks orphan glyphs when info/success messages interleave with steps.
- Breaking:
dataset upload—uploadalready creates a dataset automatically; the low-level duplicate had a worse interface. Usefoc-cli upload <path>(auto provider/dataset) ordataset create+uploadfor explicit control. (#24)
docs --urlis restricted todocs.filecoin.cloud(full URL or bare docs path), rejects traversal, and refuses redirects so the host allowlist holds end-to-end. (#25)- The same allowlist now also gates URLs parsed out of fetched content —
llms.txtindex entries, sitemap shards, and sitemap pages — so a planted external link can never be auto-fetched. (#30) - Keystore decryption invokes
castwith an argument array — the keystore path is never interpolated into a shell command. (#27)
0.1.1 — 2026-06-16
Synapse SDK v1 migration plus a CLI-hardening pass (#17, #19, #20, #21, #22).
- Provider health checks before upload context selection — unreachable providers are skipped instead of failing the upload.
- Piece pagination with next-page CTAs on
piece listanddataset details. wallet init --source <name>— attribution tag reported to Synapse/Warm Storage.
- Migrated all commands to Synapse SDK v1 APIs.
- Centralized Synapse client construction (
synapseClient). incur0.4.8; Node.js >= 22 required; dropped@remix-run/fs.- CLI version is read from
package.json(was hardcoded).
- Failure envelopes render the real error code and message (previously
code: null, message: null). wallet costsno longer reports a duplicate monthly rate and surfaces whether a one-time operator approval is still needed.
0.1.0 — 2026-05-13
- CI workflow (test + lint on every push).
- Test coverage for the Synapse-backed commands.
- Upgraded Synapse SDK and synapse-core.
dataset createrequires aproviderId(schema-enforced).
0.0.4 — 2026-03-19
Initial public release.
- CLI refactored out of the original
foc-skill: upload, wallet, dataset, piece, provider, and docs commands for Filecoin Onchain Cloud. - MCP server mode and the two agent skills (
foc-cli,foc-docs). - MCP client compatibility fixes.