Warrant Manifest Specification

An open standard for declaring CLI tool capabilities.

Schema: warrant.manifest.v1 Public Beta CC0 1.0 — No rights reserved

The Warrant Manifest Specification enables policy engines to make fine-grained access control decisions over any command-line tool — without cooperation from the tool author. Anyone can write a manifest for any tool. No helper libraries, no upstream changes, no permission needed.

Why a Specification?

Most CLI security tools are closed systems — their rules are hardcoded and only cover tools the authors anticipated. The Warrant Manifest Specification takes a different approach: a published, open standard that anyone can implement.

  • Tool authors can publish manifests alongside their CLIs
  • Security teams can write manifests for internal tools
  • Community contributors can cover the long tail of CLI tools
  • Other projects can build their own policy engines on top of the same manifest format

The specification is released under CC0 1.0 — no rights reserved. Implement freely.

Design Principles

  • Declarative, not imperative. Manifests describe capabilities — they don't enforce policy. Policy is defined separately in warrants.
  • No cooperation required. Anyone can write a manifest for any tool. No helper libraries, no upstream changes.
  • Human-readable. TOML format, reviewable by security teams, versionable in git.
  • Composable. Manifests are independent units. Bundles compose them into setup profiles.

The CLI Control Surface

The schema is derived from a systematic analysis of how CLI tools are controlled. Every mechanism through which a user or agent influences a tool's behaviour is a potential vector that a policy engine must understand.

CategoryMechanismsManifest Coverage
Invocation syntaxCommands, subcommands, positional args, options/flags, option bundling, -- terminatormatch, when_*_flags, args, options, flags, respect_option_terminator
External configurationEnvironment variables, config filesstrip_env, env
Data streamsstdin, stdout, stderr, pipes, redirectionOut of scope (shell-layer; handled by policy engine)
Interactive/runtimeTTY prompts, REPL, signalsOut of scope (runtime; not statically declarable)
Process integrationExit codes, IPC, daemon socketsOut of scope (process-level; addressed by warrant-box)

The manifest focuses on invocation syntax and external configuration — the two categories where a static document can fully describe a tool's interface. The core contribution: scope extraction turns opaque CLI invocations into structured, auditable capability checks — bridging syntax to semantics.

See the full design rationale in the canonical specification.

Document Structure

A manifest has four sections:

[manifest]        # Metadata — tool identity, version, licence
[transforms]      # Scope transform functions available in this manifest
[tool_policy]     # Tool-level security policy (env stripping, path restrictions)
[[commands]]      # Command capability declarations (one or more)

[manifest] — Metadata

FieldTypeRequiredDescription
schemastringMust be "warrant.manifest.v1"
idstringNamespaced identifier (e.g. "warrant-sh/git")
toolstringBinary name as invoked on the command line
tool_versionstringSemver constraint (e.g. ">=2.30", "*")
manifest_versionstringSemver version of this manifest
summarystringShort description
licensestringSPDX licence identifier
sourcestringURL to the upstream tool

Namespaces

Manifest IDs use a namespace/name convention:

  • warrant-sh/* — maintained by the Warrant project
  • <org>/* — maintained by a third-party organisation
  • <user>/* — maintained by an individual

[[commands]] — Capability Declarations

Each entry maps a subcommand pattern to a named capability:

[[commands]]
match = ["push"]
when_no_flags = ["--force", "-f", "--force-with-lease"]
capability = "git.push"
description = "Push local commits to a remote repository"
risk = "moderate"
default = "allow"
args = { remote = 1, branch = 2 }
scope_examples = { remote = ["origin", "upstream"], branch = ["main", "release/*"] }
scope_descriptions = { remote = "Which remote to push to", branch = "Which branches" }

Matching

FieldTypeDescription
matcharraySubcommand tokens to match (e.g. ["push"], ["remote", "add"])
when_any_flagsarrayMatch only when at least one flag is present
when_all_flagsarrayMatch only when all flags are present
when_no_flagsarrayMatch only when none of these flags are present

Flag-based specialisation enables capability splitting. The same subcommand can map to different capabilities depending on flags — e.g. git push (moderate risk) vs git push --force (high risk, separate capability).

Capability Metadata

FieldTypeDescription
capabilitystringDotted capability name (e.g. "git.push")
descriptionstringWhat this capability allows
riskstring"low", "moderate", "high", or "critical"
defaultstringSuggested default: "allow" or "deny"

Scope Extraction

Scopes let policy engines control not just what command runs, but what it targets. Scopes are extracted from arguments, flags, options, or environment variables and passed through transform functions:

TransformDescriptionExample
literalUse value unchanged"origin""origin"
pathCanonicalise file path"./src""/home/user/project/src"
hostnameExtract hostname from URL"https://api.example.com""api.example.com"
email_domainExtract domain from an email address"ops@example.com""example.com"
globNormalize value for glob pattern matching"src/**""src/**"
git_remoteNormalise git remote URL"git@github.com:org/repo.git""github.com:org/repo"

[tool_policy] — Tool-Level Security

FieldTypeDescription
strip_envarrayEnvironment variables to remove before execution (supports * glob)
pathsarrayAllowed resolved binary paths (glob patterns)
package_policystringPackage security: "open", "denylist", or "allowlist"

Registry Protocol

Manifests are distributed via a registry — a Git repository containing manifest files and a discovery index (registry.toml). The default registry is warrant-sh/registry on GitHub.

# Fetch protocol
1. Client fetches <registry_url>/registry.toml
2. Client compares sha256 hashes against local cache
3. Client downloads changed manifests
4. Client validates against this specification
5. Client writes to local cache

The registry URL is configurable via WSH_REGISTRY_URL.

Bundle Format

Bundles compose multiple manifests into named setup profiles:

[bundle]
name = "codex"
description = "Coding agents (Codex, Claude Code, OpenCode)"
version = "1.0.0"

[setup]
shell_guard = true
prompt_lock = true

[[agents]]
name = "codex"
alias = "codex"

[manifests]
include = [
  "warrant-sh/coreutils",
  "warrant-sh/git",
  "warrant-sh/cargo",
  "warrant-sh/npm",
  "warrant-sh/dangerous-patterns",
  "warrant-sh/sanitize-env",
]

From Manifest to Policy

The manifest is a factual capability map. Policy is defined separately:

manifest (factual)     →  draft (opinion)      →  warrant (signed policy)
"git push exists"         "allow push to origin"   cryptographically signed
  1. wsh pull git — fetch manifest from registry
  2. wsh add git — generate draft (every capability starts as review)
  3. wsh edit git — set each capability to allow or deny
  4. wsh lock — compile drafts into a signed warrant

This separation ensures that capability discovery is decoupled from capability authorisation. The same manifest can produce vastly different policies depending on the operator's requirements.

Manifest Authoring Guide

To write a custom manifest for a new CLI, follow this workflow:

  1. Identify the capability surface. List distinct actions and group by risk (read, write, destructive).
  2. Map invocation patterns. Use match and conditional flags to split capabilities when intent changes.
  3. Define scopes. Extract target context (host, path, package, branch, remote) so policy can be precise.
  4. Choose transforms. Use literal, hostname, path, git_remote, and other transforms as needed.
  5. Validate and iterate. Run test invocations through wsh check ... and confirm expected capability matches.

Registry Coverage (Reference)

The default registry currently includes manifests for:

  • git — clone/fetch/push flows, including separate force-push capability
  • cargo — build/test/run/publish/install and related workflows
  • npm — install/run/publish/login/audit with package and script scopes
  • curl — request/upload/write split by flags and output behavior
  • docker — build/pull/push/run/exec/compose operations
  • ssh — connect/scp/sftp behaviors
  • pip — install/uninstall/freeze/download operations

Reference Implementation