Skip to content

Release, pin & license-review a CLI tool

Admin

In the desktop edition, the CLI command tool runs external command-line programs. This how-to is for operators: it covers the approval flow in operation, version pinning (the binary allowlist), and the license/redistribution review gate every bundled CLI must pass before it ships. For the end-user overview and the customer-readable guarantee statement, see → Command-line tools (CLI) in the assistant — overview & limits. For where configurable tools fit, see Configurable tools — overview.

A state-changing or network-reaching command never starts automatically:

  1. The assistant builds a manifest — the program plus its arguments as a fixed list (argv), never a free shell line.
  2. Before execution, the HITL approval dialog appears: it shows the exact argv and a dry run (what would happen) before anything runs.
  3. Execution happens only after human approval. Reading calls are exempt; anything writing or network-reaching needs approval.
  4. Every execution produces an audit record — the run is logged and traceable.

This human approval, together with credential-scoping, is the load-bearing security boundary (see the guarantee boundary below), not the OS sandbox.

Raw CLI output is version-dependent. To ensure only a vetted, exact version launches, there is a binary allowlist with checksum verification.

  • Default without a registry = unpinned. With no registry configured, the binary launches as-is (still under sandbox, default-deny egress, credential-scoping and HITL). This is logged once. It preserves the behaviour of existing deployments.
  • Turn pinning on: point Cli:BinaryRegistryPath at a cli-binaries.json. The strict registry then loads at startup.
  • Fail-loud: a configured-but-missing or malformed registry file makes the application fail hard at startup — an allowlist must not silently degrade to “allow all”.

Per binary: name → version → per platform path + SHA-256.

{
"binaries": [
{
"name": "git",
"version": "2.45.1",
"platforms": {
"osx-arm64": { "path": "/Applications/CompanyWizard.app/Contents/Resources/bin/git", "sha256": "ab12…" },
"win-x64": { "path": "C:\\Program Files\\CompanyWizard\\bin\\git.exe", "sha256": "ef56…" }
}
}
]
}
  • name matches manifest.binary exactly (case-sensitive).
  • Platform keys are .NET RID short forms: osx-arm64, osx-x64, win-x64, win-arm64, linux-x64, …
  • Generate sha256 as hex: macOS/Linux shasum -a 256 <file>, Windows Get-FileHash <file> -Algorithm SHA256.

Before any launch the executor resolves manifest.binary + manifest.version against the registry and verifies the on-disk file for the current OS/arch. Only a binary that is registered, pinned to the version, present for this platform and hash-matching launches — via the registry-resolved absolute path, never the raw manifest value. Every drift (not registered, missing/mismatched version, no platform entry, file missing, hash mismatch) is a fail-closed reject — before the sandbox, the approval dialog or secret resolution are reached.

The pinned binaries ship inside the Photino package so paths and hashes are known at build time; the registry file is generated as a build step that hashes the bundled binaries.

  • macOS (.pkg/.app): binaries under CompanyWizard.app/Contents/Resources/bin/. Generate cli-binaries.json in macos/build-macos.sh after staging Resources/bin/ and before signing.
  • Windows (Velopack/vpk): binaries under bin\ in the package root; generate in the vpk pack step.
  • Cross-OS availability: not every CLI ships for every platform. A registration lacking the current platform is a fail-closed reject (no fallback) — decide per binary which platforms to bundle and document the gaps.

Bundling a third-party CLI inside the package is redistribution, so each one needs license and patent clearance before it ships. Currently bundled: none. The unpinned default ships zero binaries; concrete CLI selection is a product/sales decision. Before a binary is added to the registry and to the inventory table, this sign-off applies:

  1. Identify the license from the upstream release (not guessed); record the SPDX id.
  2. Redistribution cleared for our bundling form (binary, inside a signed installer):
    • Permissive (MIT, BSD-2/3, Apache-2.0, ISC) → low risk; Apache-2.0 also grants a patent license → preferred where there is a choice.
    • Weak copyleft (MPL-2.0, LGPL) → allowed, but file-/library-scoped obligations; keep the binary unmodified and carry the upstream notices.
    • Strong copyleft (GPL-2.0/3.0, AGPL) → flag. Bundling triggers source-offer obligations; AGPL adds network-use obligations. Requires explicit legal sign-off or a different tool — do not bundle on a maintainer’s say-so.
  3. Check patent exposure — prefer permissive/Apache-2.0 with an explicit patent grant; note known encumbrances (codecs, crypto-export).
  4. Carry notices — bundle upstream LICENSE/NOTICE in the package.
  5. Record the sign-off (✅ + who/when) before the registry pin lands.

A binary that reaches cli-binaries.json without a ✅ row is a release blocker.

Containment is best-effort plus egress control, not VM isolation; the core boundary is credential-scoping + HITL approval. The full, customer-readable statement lives in the end-user overview → Command-line tools (CLI) in the assistant — overview & limits.