Skip to content
stacks
Docs menu

Registries and trust

Know where stacks finds each stack and fragment, which sources it trusts, how the signed index is verified, and what changes for an untrusted entry.

View as MarkdownOpen in ClaudeOpen in ChatGPT

stacks reads entries from four places, called origins, and trusts entries from some origins and not others. Trust decides whether stacks hands an entry to an agent without asking you first.

How stacks decides trust

stacks trusts an entry by its origin and doesn't inspect what the entry says. Entries from places you or the stacks maintainers control are trusted, and any other entry needs a person to review it first. A stack's guidance becomes your agent's instructions and its verify steps run as shell commands on your machine, so an entry shipped inside a repository you just cloned could tell the agent to do anything.

Origins and lookup order

When stacks looks up an id, it checks the origins in this order and uses the first one that has it:

Origin Where Trusted
project .stacks/stacks/ and .stacks/fragments/ in the current directory No. A cloned repository can ship these
user ~/.config/stacks/ (or $XDG_CONFIG_HOME/stacks/), beside config.json Yes
index The signed registry index from stacks.steamfield.io, verified and cached locally Yes, for the default index. No, for a custom STACKS_INDEX_URL
bundled The registry shipped inside the CLI package Yes, unless STACKS_REGISTRY_DIR replaced it

stacks show prints an entry's origin and trust on its first line:

stacks show go-cli
Terminal
Go CLI (go-cli), stack, origin bundled, trusted
A starter for a terminal program written in the Go language, with settings and clear error messages.
...
Shadows: none
...
Output

A higher origin's copy hides the lower ones. list --json marks the winning copy's shadows with the origins it hides, and show prints a Shadows: line. An entry that fails to load is listed under rejected with a one-line error, and stacks never silently falls back to a lower copy of it. An entry written for a newer schema_version than your CLI reads is the exception: it's skipped with a warning, and the next origin's copy is used.

Your own stacks and fragments belong in the user origin. stacks create stack my-api starts one there, and stacks create stack go-cli --from bundled copies a bundled entry there to edit, after which your copy shadows the original.

The signed index

The index is how new and updated entries reach you without a CLI release. list, show, new and add read it from the hosted API at https://stacks.steamfield.io, refreshing it when the cached copy is stale. search ranks the cached copy and never refreshes it. The directory is built from the same index.

Before using anything from it, the CLI checks:

  1. The index envelope's Ed25519 signature against the public keys compiled into the CLI. A signature that doesn't verify rejects the whole index.
  2. The index format version and the minimum CLI version it declares. An index your CLI is too old to read is set aside, and the last verified index or the bundled registry is used instead.
  3. Each entry bundle's sha256 digest against the digest the signed index advertises. A bundle that doesn't match is rejected on its own, without hiding unrelated entries.

Fetched content is only read and written to disk, never executed by the CLI. An index entry whose digest matches a bundled one keeps its bundled identity. An index stack names the exact fragment digests it was proven with; when that composition isn't available, the bundled stack and fragments are used instead.

Caching and offline use

The verified index lives under ~/.cache/stacks/index/ ($XDG_CACHE_HOME/stacks/index/): the signed envelope in index.json, request state in state.json, and extracted bundles under entries/<digest>/. Cached files are verified again before every use, offline too.

Situation What happens
Cached index younger than 24 hours Used without a request
Refresh needed One request with a 1,500 ms deadline
Request fails The last verified index is kept, or the bundled registry is used. The next attempt waits ten minutes
Server answers 404 The next attempt waits 24 hours
--offline or STACKS_OFFLINE=1 No request. Verified cached entries stay available

stacks verify never touches the index. stacks doctor --json reports its state under remoteIndex without contacting the server: the URL, isDefaultUrl (whether that URL is the default, signature-verified index), the offline flag, when it was cached, the last attempt and its outcome, and any backoff deadline.

Using a custom index

STACKS_INDEX_URL points the CLI at a different index, such as one your team hosts. It needs HTTPS, except for plain HTTP on localhost or 127.0.0.1. An invalid URL is never fetched.

Entries from a custom index are untrusted, because the CLI's keys only cover the default index. Digests are still checked, so a bundle can't change underneath its index, but nothing vouches for the index itself. Setting STACKS_INDEX_URL also turns off the published stats in list and show, since those numbers describe the default registry. See Configuration.

What an untrusted entry changes

You can still show, compose and build from an untrusted entry, but these steps need a person:

Where Trusted entry Untrusted entry
stacks new / stacks add handoff Can launch an agent Never launches an agent. Under an agent, the printed next step says STOP: show the user the entries and the brief's first section, and continue only if they confirm
stacks add under an agent or without a terminal Records the new steps' digest Writes the new steps but keeps the previous digest, so verify reports changed until a person runs stacks verify --accept-changes
stacks create --from Copies into your user registry Needs a person at a terminal to read the full trust report. Agents are refused with PermissionDenied (exit 6)
stacks search Ranked normally Never ranked strong, and carries an untrusted penalty
The manifest "trusted": true "trusted": false, so the record stays with the project

Warning

Promoting a project entry into your user registry makes it trusted everywhere on your machine. Read the full trust report before you confirm, including when you pass --yes.