Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Start with MCP

Verify another agent's output, sign an audit record, preserve an agreement transcript, or verify text and images. Start with the use cases to choose the appropriate MCP profile, then connect your client.

JACS signs structured data and verifies it using explicit public evidence. MCP is the primary integration. Install the portable CLI and start its stdio server:

cargo install jacs-cli --locked
jacs mcp

The default profile exposes jacs_verify_document. Verification checks integrity under the supplied public key; identity, consent, trust and permission remain application decisions. Diagnostics go to stderr and stdout carries only MCP.

Explicit local signing provides create, sign, verify, rotate, re-encrypt and encrypted import/export operations for one selected vault. New identities use pq2025 (ML-DSA-87), without automatic classical fallback. Passwords belong in the host's prompt or designated environment variable, never in MCP parameters.

See the complete MCP contract and configuration guide and CLI commands. The extended MCP profiles provide separate agreement and media capabilities for existing integrations.

Rust, Node.js, Python and Go remain supported direct libraries, alongside browser and mobile bindings. See supported packages or choose your language in the navigation. Native APIs include capabilities beyond the portable MCP tool set; they remain in their own dependency workspace.

For persistent native identity across DID documents and signed Agent Cards, see DID and A2A identity, including protocol and release boundaries.

For native protocol integration, follow the A2A how-to or W3C DID how-to. Both distinguish source exports from published packages and explain the verification and application responsibilities.

JACS use cases

Start with MCP when an agent or tool host needs local signing and verification. Rust, Node.js, Python, Go, browser and mobile libraries remain available for embedding the same primitives in an application. Choose the integration by its actual capabilities and the authority the host is prepared to grant.

Verify an artifact before using it

Use the default jacs mcp server to check signed JSON received from another agent, tool or service. It needs no signing identity or private key.

  1. The host selects the expected signer's public key and algorithm from its trusted state, independently of the received document.
  2. Call jacs_verify_document with the signed JSON and that public evidence.
  3. Parse the JSON text in the MCP result and require valid: true. A successful JSON-RPC response alone is insufficient.
  4. Apply the application's identity, freshness, revocation and authorization policy before acting on the verified content.

This is useful for agent handoffs, signed tool outputs, stored memories, configuration and audit records. It verifies integrity under the chosen key; the application decides whether to trust and use the result.

Sign agent output with one selected identity

Use portable local-sign to sign JSON before it leaves an agent or service. The host selects one encrypted vault and supplies its password outside the tool request. The process can create, sign, verify, rotate, re-encrypt and import/export encrypted material within that vault.

The grant applies to the process. Applications that need human approval for each signature must enforce it in their own host flow. New identities use pq2025 (ML-DSA-87), and an error never triggers an algorithm downgrade.

See the MCP configuration guide and portable tool contract.

Record agreements and review text or images

The separate jacs-compat mcp server's extended local-sign profile provides document signing and Agreement v2 operations. An explicit content directory also enables text/image signing, verification and signature extraction.

Use these for signed agreement transcripts, reviewed documents and media provenance. The host selects the agent and content directory; a tool cannot choose arbitrary key storage or filesystem roots. The exact 1/9/14-tool profiles are documented in the extended MCP guide.

Embed JACS in a service or application

Direct libraries retain capabilities beyond the portable MCP tool set:

Use caseStarting point
JSON signing, verification and encrypted identity lifecyclePortable Rust core, native Rust, Node.js, Python, Go, browser or mobile; select the relevant library guide
Request-bound HTTP authentication and signed response/event envelopesNative Rust and Node/Python/Go instance APIs
A2A discovery and signed artifact exchangeA2A quickstart; language wrappers and extended CLI have their own feature rules
MIME email signatures and PNG logo embeddingNative Rust email guide and HAI SDK email integration
Attestations, trust policies, W3C interoperability and framework adaptersNative integration reference; availability depends on the package and enabled features
Durable signed-document storagejacs-duckdb, jacs-redb, jacs-postgresql, jacs-surrealdb

The compiled native MCP inventory has 42 names. Only the selected runtime profile's authorized subset is callable; installing a library does not expose all of its APIs as MCP tools. See the example index for retained examples and their validation limits.

Connect an MCP client

Install the portable CLI:

cargo install jacs-cli --locked
jacs mcp

For clients that accept an mcpServers configuration, the verification-only connection is:

{
  "mcpServers": {
    "jacs": { "command": "jacs", "args": ["mcp"] }
  }
}

The host starts a local stdio process. There is no HTTP listener; diagnostics go to stderr. The only default tool is jacs_verify_document. Select the signer's public evidence independently, parse the returned JSON text, and check its valid field. See the verification use case.

Verify a sample document

To create a local test fixture, run in a private directory (skip this when you already have a signed document and independently selected public identity):

mkdir -m 700 .jacs
jacs create --output .jacs/agent.json > public-identity.json
printf '%s' '{"message":"hello"}' | jacs sign --agent .jacs/agent.json --input - > signed.json
jacs verify --public-identity public-identity.json --input signed.json

The CLI prompts for the key password. Its successful verification reports valid: true. To prepare the equivalent MCP request with Python 3:

import json
from pathlib import Path

trusted = json.loads(Path("public-identity.json").read_text())
request = {
    "jsonrpc": "2.0", "id": 1, "method": "tools/call",
    "params": {"name": "jacs_verify_document", "arguments": {
        "document": Path("signed.json").read_text(),
        "public_key": trusted["public_key"],
        "algorithm": trusted["algorithm"],
    }},
}
print(json.dumps(request))

Send this through your MCP client after its normal connection/initialization. document is an exact JSON string; public_key is canonical standard Base64 of the raw public-key bytes, not PEM. Parse the response's text content as JSON and require valid: true. For identity attribution, also compare signer_id to the independently selected public identity's agent_id. Malformed requests can return protocol/tool errors. Do not accept a key merely because it arrived beside an untrusted document.

Enable local signing explicitly

Start the signer with the encrypted fixture vault created above:

jacs mcp --profile local-sign --agent .jacs/agent.json

The terminal prompts for the password. A noninteractive MCP host must supply JACS_PRIVATE_KEY_PASSWORD through its secret-handling mechanism; do not put passwords in tool arguments, command arguments or committed client config. Configure the client to launch this profile with an absolute vault path:

{
  "mcpServers": {
    "jacs": {
      "command": "jacs",
      "args": ["mcp", "--profile", "local-sign", "--agent", "/absolute/path/.jacs/agent.json"]
    }
  }
}

Local signing enables seven portable tools for the selected vault. Creation and encrypted import require an empty vault and refuse to overwrite an identity. Rotation retains the current password unless the host supplies JACS_NEW_PRIVATE_KEY_PASSWORD; re-encryption requires that new password at startup. The host owns approval of that process-level grant. Unix vault permissions are enforced; platforms without the required filesystem custody adapter can still verify public data.

For agreements and text/images, choose the separate extended MCP profiles. For service integration, use the direct libraries.

Call jacs_sign_document with {"document":"{\"message\":\"hello\"}"}. Its parsed text response contains a document object and algorithm; serialize that object to a JSON string for subsequent verification. Check the active tools/list inventory before calling lifecycle tools.

Portable and extended MCP schemas differ: the extended signer accepts content and returns signed_document; its verifier takes raw key bytes as a JSON array. Use the selected server's contract rather than reusing arguments across them. For other protocols, follow the A2A how-to or W3C DID how-to.

Extended CLI and MCP profiles

The primary entry point is jacs mcp, from cargo install jacs-cli. The extended jacs-compat binary adds native integration commands for agreements, A2A, text/media, attestations and trust:

cargo install jacs-cli-compat --locked
jacs-compat mcp
Rust packageExecutableCLI scope
jacs-clijacsPortable document/key operations and focused MCP
jacs-cli-compatjacs-compatExtended integration commands and its separately scoped MCP server

Email signing is Rust-library only; there is no dedicated email CLI command.

From this checkout, make build-jacs-compat builds that binary and make mcp-compat starts it without writing Make output to protocol stdout. These commands select the separate native workspace.

Server/profileRuntime toolsRequired host configuration
Portable jacs mcp, default1: document verificationExplicit public evidence in each request
Portable jacs mcp --profile local-sign7: portable vault lifecycle and document operationsOne selected encrypted vault and host-held passwords
Extended jacs-compat mcp, default1: document verificationExplicit public evidence in each request
Extended local-sign9: document sign/verify plus seven agreement-v2 operationsExisting signed local config, matching unlocked key, offline process
Extended local-sign with a content directory14: the nine above plus five text/image toolsExplicit JACS_MCP_BASE_DIR, confined separately from config/key storage

The extended CLI compiles the full tool families, but runtime authorization restricts the advertised and callable subset. To select an existing local agent:

jacs-compat mcp --profile local-sign --config /path/to/jacs.config.json

For local text/image signing, verification and extraction:

JACS_MCP_BASE_DIR=/path/to/content \
  jacs-compat mcp --profile local-sign --config /path/to/jacs.config.json

Supply the host password through the configured environment, protected password file or keychain. The MCP signing process does not prompt interactively. Keep network capability flags unset. File arguments are relative to the selected content directory; config and key directories cannot be used as content roots. The selected process can sign as this agent; it does not provide per-call human approval. Ordinary HAI photo policy remains an application decision.

Only the selected profile's tools are callable. The 42-name compiled contract includes unavailable tools; trust-admin and legacy-core refuse startup.

See the extended MCP protocol and authorization details. make test-jacs-mcp-compat exercises the native profiles and contract with full-tools; make test-jacs-mcp tests the portable server.

Supported libraries and packages

Start with MCP for agent/tool integration, or choose a direct library below. Native Rust, Node.js, Python and Go remain supported in their separate workspace. The release inventory records 0.16.1 packages, platforms and provenance. 0.16.0 is the first release in which the identity-standard exports reuse the persistent ML-DSA-87 identity. Published Python wheels support CPython 3.11–3.14.

IntegrationPackage / installationReference
Portable CLI and MCPcargo install jacs-cli --version 0.16.1 --lockedCLI, MCP
Portable Rust primitivesjacs-core = "=0.16.1"Core API
Rust integration libraryjacs = "=0.16.1", optional a2a, agreements, attestation featuresRust library
Native Node.jsnpm install --save-exact @hai.ai/jacs@0.16.1Node guide
Pythonpython -m pip install jacs==0.16.1Python guide
Go with CGogithub.com/HumanAssisted/JACS/jacsgo@v0.16.1, plus its matching native libraryGo installation
Browsernpm install --save-exact @hai.ai/jacs-wasm@0.16.1Browser guide
Android / iOSjacs-mobile plus its platform host librariesMobile guide
Extended Rust CLI and MCPcargo install jacs-cli-compat --version 0.16.1 --locked; executable jacs-compatExtended MCP profiles
Storagejacs-duckdb, jacs-redb and jacs-postgresql 0.16.1; jacs-surrealdb 0.15.0, for use with jacs 0.15 onlyStorage guide

Choose by capability

The portable core, browser and mobile libraries cover cryptography and encrypted identity lifecycle. They do not include native filesystem, email, database or network integrations. Native libraries retain JSON/files, Agreement v2, A2A, trust, text/media, attestation and transport helpers, with package-specific feature gates. Check the relevant language API before assuming a native Rust method is exposed by every wrapper.

Node and Python ship native binaries for the platforms in the release inventory. Go requires CGo, a C toolchain and the matching shared library at build and runtime. Mobile bundle assembly and physical-device acceptance are separate from publishing the jacs-mobile Rust crate.

Build and validate from source

The root commands remain supported:

make build-jacsnpm
make build-jacspy
make build-jacsgo
make test-bindings

They select the separate native workspace and test installed consumers. See the developer commands for broader native test suites and the example index for integration examples. Historical reference chapters can contain earlier package names or CLI commands; current extended CLI examples use jacs-compat.

Identity across MCP, A2A and DID

A persistent JACS identity signs documents with the same key across sessions. New identities use ML-DSA-87 (pq2025); verification errors never trigger a classical fallback. Choose the integration boundary you need:

GoalStart hereWhat JACS supplies
Connect an agent to local signing/verification toolsMCP client setupA stdio server; verification-only by default
Publish an Agent Card or exchange A2A artifactsA2A how-toNative discovery and signing APIs; explicit A2A 1.0 profile
Export a DID document or test a DID-bound request proofW3C DID how-todid:wba export and JACS-specific request proofs

MCP is the primary integration. The portable jacs server does not host A2A messaging or resolve DIDs. The separate native jacs-compat CLI and language libraries provide those documented exports; an application supplies its own messaging handler and identity/authorization policy.

The release inventory records the published packages; 0.16.0 is the first release whose source includes the direct-PQ DID/JWK and Agent Card behavior. The A2A and DID how-tos build from source so that they track this checkout. A public key fetched alongside a signature is not an independent trust anchor, and a valid signature is not permission to act.

See the standards evidence for RFC 9964, DID and A2A references. An AKP JWK represents an ML-DSA key; it does not imply a finalized ML-DSA Data Integrity cryptosuite or universal peer support.

The key discovery guide covers opt-in native DNS/HTTPS lookup and exact signed identity/version checks. Discovered keys do not enroll trusted identities. Portable CLI/MCP verification continues to use caller-supplied public identity or key material.

Use JACS with A2A

Use native JACS to publish signed Agent Cards and sign artifacts. Discovery export does not implement a messaging endpoint: your application must serve the skills and URL it advertises. The portable MCP server has no A2A transport.

Build this checkout

The binding build uses Python 3.11+, Rust 1.97 through rustup, a C compiler/linker, Make and Perl. On Linux, the default keychain build also needs pkg-config and D-Bus development headers (libdbus-1-dev on Debian/Ubuntu). The official JavaScript SDK smoke requires Node.js 24.7+ and npm.

These instructions use current source, including direct ML-DSA-87 card signing, which first shipped in the published 0.16.0 packages. From the repository root, with Rust 1.97+ and native build prerequisites:

cargo install --locked --path archive/native/jacs-cli

Provide the native key password through your host secret source or configured keychain; keep it out of command arguments and committed files. Use a private working directory for persistent keys and jacs.config.json.

Serve local discovery

The default direct-PQ walkthrough requires an ML-DSA-87 root. Loading an older Ed25519 identity does not migrate it; explicitly upgrade it before using this profile.

jacs-compat a2a quickstart --name example-agent --domain agent.example.com

This creates a persistent PQ identity, or loads the existing identity as-is and serves discovery on http://127.0.0.1:8080. In another terminal:

curl --fail http://127.0.0.1:8080/.well-known/agent-card.json
curl --fail http://127.0.0.1:8080/.well-known/jwks.json

The CLI uses the legacy 0.4.0 card model with compact signatures[].jws. Default source discovery publishes five documents and signs with the persisted PQ root; its JWK has kty: "AKP" and alg: "ML-DSA-87". Fetching these documents only checks that the server responds—it does not verify a trusted identity.

For an existing config, restart with jacs-compat a2a serve. Behind a TLS reverse proxy, pass --origin https://agent.example.com; the signed public origin must match the service clients use. A classical peer requires explicit --a2a-algorithm ES256 and the scoped compatibility binding (a sixth document). Selecting an Ed25519 root alone does not enable ES256 card signing.

Export an A2A 1.0 card

The public Node/Python APIs accept an explicit protocolVersion: "1.0", actual messaging URL, nonempty skills and content modes. Omitting these options keeps legacy discovery. Build the Node binding with make build-jacsnpm, then install its source package in your consuming project:

npm install /absolute/path/to/JACS/archive/native/jacsnpm

Save this as discovery.cjs and run node discovery.cjs:

const { JacsClient } = require('@hai.ai/jacs/client');
const { buildWellKnownDocuments } = require('@hai.ai/jacs/a2a-server');

async function main() {
  const client = await JacsClient.quickstart({
    name: 'report-agent', domain: 'agent.example.com',
  });
  const documents = buildWellKnownDocuments(client, {
    protocolVersion: '1.0',
    messagingUrl: 'https://agent.example.com/a2a',
    skills: [{ id: 'report', name: 'Report', description: 'Return a report', tags: ['report'] }],
    defaultInputModes: ['text/plain'],
    defaultOutputModes: ['application/json'],
  });
  console.log(JSON.stringify(documents['/.well-known/agent-card.json'], null, 2));
}
main().catch(error => { console.error(error.message); process.exitCode = 1; });

The resulting card uses supportedInterfaces with protocol version 1.0 and detached signatures[].protected / signature fields. This example exports a card; it does not start the advertised /a2a handler. Preserve the encrypted identity and its host-managed password between runs.

Python exposes the same option names through client.get_a2a().generate_well_known_documents(discovery_options=options); see the binding examples.

Verify and exchange a message

Before choosing a transport, verify the card with an independently trusted key, expected identity/version and algorithm. For direct PQ v1 cards, native Rust provides jacs::a2a::v1::verify_root_card; official SDK verification also needs trusted key selection. Legacy verified/strict policy helpers are not v1 parsers.

The official SDK examples start real SDK servers/clients, verify cards and exchange one signed report. For the JavaScript example, use Node 24.7+ with ML-DSA WebCrypto, then run from the repository root after building the binding:

npm ci --ignore-scripts --prefix archive/native/examples/a2a_v1_sdk
npm test --prefix archive/native/examples/a2a_v1_sdk

The pinned JavaScript SDK 1.3.0 supports direct ML-DSA-87 through jose 6.2.13. The Python SDK 1.3.0 example uses explicit ES256 because its PyJWT backend lacks ML-DSA verification; the native root remains PQ. The tests include modified cards/signatures and untrusted-root rejection, not every A2A capability.

Next: signed artifacts, discovery trust policy and W3C DID export. Apply application authorization after verification; card discovery, cached freshness and signature validity do not grant it.

Use JACS with W3C DIDs

JACS exports a DID Core document for its implemented did:wba method, using the persistent native signing key. It does not provide a general DID resolver or did:web/did:key exports. The commands below describe current source; the published 0.16.0 packages are the first whose did:wba export uses the persistent ML-DSA-87 key.

Build and create an identity

Use a C compiler/linker and Rust 1.97. On Linux the default keychain build also requires pkg-config and D-Bus development headers (libdbus-1-dev on Debian/Ubuntu).

From the repository root, with Rust 1.97+ and native build prerequisites:

cargo install --locked --path archive/native/jacs-cli

In a private working directory, supply JACS_PRIVATE_KEY_PASSWORD through your host secret mechanism or use the configured native keychain, then run:

jacs-compat quickstart --name example-agent --domain agent.example.com
jacs-compat w3c did --origin https://agent.example.com
jacs-compat w3c did-document --origin https://agent.example.com > did.json
jacs-compat w3c well-known --origin https://agent.example.com --out discovery

The identifier has the form did:wba:agent.example.com:agent:<jacsId>. The DID document uses DID v1 / Controlled Identifiers v1 contexts and a JsonWebKey verification method. For a pq2025 root, its public JWK uses kty: "AKP", alg: "ML-DSA-87" and base64url public-key bytes in pub. Repeated exports retain that root key and its thumbprint-derived JWK kid.

well-known writes three public documents and prints their paths:

  • /.well-known/agent-descriptions
  • /agent/<jacsId>/description.json
  • /agent/<jacsId>/did.json

Publish them at those paths under the controlling HTTPS origin. Exporting files does not deploy them or prove domain control. For a local inspection server, use jacs-compat w3c serve --origin https://agent.example.com --host 127.0.0.1 --port 8081; this is a plaintext demo listener, not deployment of the claimed HTTPS origin.

Test a request-bound proof

JACS has a request-proof JSON format, JacsW3cRequestProof with scheme DIDWba. It is not ANP HTTP Message Signatures. For a local round trip, sign the actual method, target URL and body, then verify the same values:

printf '%s' '{"task":"review"}' > request.json
jacs-compat w3c sign-request --origin https://agent.example.com \
  --method POST --url 'https://api.example.com/tasks?priority=high' \
  --body-file request.json > proof.json
jacs-compat w3c verify-request --proof proof.json --did-document did.json \
  --method POST --url 'https://api.example.com/tasks?priority=high' \
  --body-file request.json

Success prints JSON with valid: true and expectedRequestChecked: true. A substituted body or URL fails verification. The default maximum proof age is 300 seconds. In this local test, did.json is your own exported document; a receiver must obtain the DID document under an independently trusted policy, check its verification relationship and enforce current authorization/revocation.

Production hosts must also enforce replay protection across their deployment. A fresh CLI process does not retain previous processes' nonce history. See replay-store deployment and the request-proof reference.

Compatibility and limits

Explicit ES256 compatibility uses a persisted key and a scoped binding signed by the native root. A peer without ML-DSA support needs that deliberate integration; an error never triggers a downgrade. Current VC/Data Integrity exports retain the ES256 path. An AKP JWK is not a claim of a finalized ML-DSA-87 Data Integrity cryptosuite.

Next: A2A discovery, native DID reference and standards evidence. DID document structure, method interoperability and real-world identity trust are separate checks.

Rust libraries

Use jacs-core = "=0.16.1" for the no-I/O portable protocol and crypto layer. Use jacs = "=0.16.1" for native filesystem, email, media, trust and integration APIs; enable a2a, agreements or attestation as needed. Both packages are published and supported, with separate dependency graphs.

Start with the native Rust API, document operations, agreements, email signing or storage backends. The portable core guide describes the smaller embedding surface.

The installed portable CLI is jacs; extended command examples use jacs-compat. See packages for the complete inventory and release evidence.

Node.js library

npm install --save-exact @hai.ai/jacs@0.16.1

The package includes native bindings for the released platforms. It supports direct application integration as well as MCP adapters. It is separate from the browser package, @hai.ai/jacs-wasm.

Start with the simple API, instance API or API reference. Framework guides cover Express, Koa, LangChain.js and Vercel AI SDK.

The current package README explains CLI download behavior, public approval verification and request/event authentication. Older reference chapters can contain pre-0.15 registry status; the release inventory records current verified artifacts.

Python library

python -m venv .venv
.venv/bin/python -m pip install jacs==0.16.1

Use the native Python library directly in services, tools and framework adapters. Prebuilt wheels are available for the platforms in the release inventory; custom source builds use maturin and Rust.

Start with the simple API, instance API, framework adapters or API reference.

The current package README covers public approval verification, request/event authentication and the optional CLI shim. Current extended CLI examples use jacs-compat; the shim's downloaded jacs executable is the portable CLI.

Go library

The published module is github.com/HumanAssisted/JACS/jacsgo@v0.16.1. It requires CGo, a C toolchain and the version-matched shared library. In a module that imports github.com/HumanAssisted/JACS/jacsgo:

go get github.com/HumanAssisted/JACS/jacsgo@v0.16.1
go mod vendor
go run github.com/HumanAssisted/JACS/jacsgo/cmd/jacsgo-install@v0.16.1 -version v0.16.1

The installer verifies the native library's checksum and places it in the vendored module. Build with CGO_ENABLED=1 and deploy the matching shared library alongside the executable, as described in the current Go guide. macOS arm64/x86_64 and glibc Linux arm64/x86_64 libraries are released; Windows and musl prebuilt libraries are unavailable.

See the Go API and quickstart, using the current version and installation contract above. go get alone does not install the native library needed for linking and execution.

Browser and mobile

Browser

npm install --save-exact @hai.ai/jacs-wasm@0.16.1

Use the browser guide for WASM initialization, workers, signing/verification and encrypted storage. The npm scope is @hai.ai; older @jacs/wasm examples predate this release. The browser package shares the portable core and does not load the native Node module or expose native email/filesystem integrations.

Android and iOS

jacs-mobile provides UniFFI bindings and platform host libraries. See the mobile guide, Android lifecycle and custody guide and iOS guide.

The hardware-backed platform store protects access to software PQ key material; it does not perform native hardware ML-DSA signing. Publishing the Rust crate is separate from assembling host bundles and accepting behavior on physical devices.

Library and integration reference

The library and integration documentation covers supported Node, Python, Go and Rust APIs, email, media, storage, A2A, attestations and framework adapters. The documentation source remains with the native workspace. Some chapters retain historical installation/version text: use current package guidance and the jacs-compat executable for extended CLI commands.

Start with DID and A2A identity for current source behavior. Jump to A2A, Agreement v2, attestations, email, text, media, trust, storage or observability.

Use extended MCP profiles to determine which tools are actually available at runtime. A compiled tool inventory is not an authorization grant.

Development and releases

From a checkout, make help lists the main build and test entry points. Use make check for dependency boundaries, source version alignment, licensing, notices, release policy and script tests. make test-all exercises the portable workspace; native compatibility suites remain explicit.

make build-wasm creates the browser package; make test-wasm runs headless Chrome tests. make build-jacsbook builds this guide and the preserved native reference. See the developer command reference for prerequisites, native tests and maintenance commands.

All 17 Rust crates and all language packages use one source version. make release-everything triggers six publishing workflows. Run make verify-shipped-release after recording actual published artifacts. Homebrew formula preparation verifies the CLI assets' checksums and source-bound attestations; tap synchronization is a separate workflow.

See the release guide.