Preset catalogs

Catalogs discover presets and select releases for ID-based installation. Their priorities order catalog sources, independently of the installed preset priorities that determine artifact resolution.

Discovery and trust

The built-in stack contains:

Name Source Default permission
default First-party preset catalog Installation allowed
community Community preset catalog Discovery only

Community presets are independently maintained. Maintainers check catalog entries for completeness and format, not the safety or quality of the preset code. They do not review, audit, endorse, or support that code. Users must vet a preset's source before installation and use.

Important

A project's .specify/preset-catalogs.yml can point add and search at a catalog you did not choose. Run specify preset catalog list in unfamiliar projects and inspect install-allowed sources. Project configuration and install_allowed: true are not evidence of code review. Only permit installation from catalogs you own or have vetted; leave unfamiliar and community sources discovery-only.

After vetting a community release, an explicit specify preset add --from <archive-url> installs that archive without catalog lookup. This is a source choice, not a bypass that makes unreviewed content safe.

Catalog configuration

The first usable configuration level supplies the whole stack; project and user entries do not automatically augment the defaults:

Order Source
First SPECKIT_PRESET_CATALOG_URL environment variable
Next Project .specify/preset-catalogs.yml
Next User ~/.specify/preset-catalogs.yml
Last Built-in default and community catalogs

The environment variable selects one catalog named custom with installation allowed. Use it only with a vetted source. A config file with no valid catalog entries falls through to the next level; malformed YAML or invalid configured URLs/priorities raise validation errors rather than silently selecting another source.

Example project configuration:

catalogs:
  - name: "my-org-presets"
    url: "https://catalog.example.com/presets.json"
    priority: 5
    install_allowed: true
    description: "Our vetted presets"
Entry field Meaning
url HTTPS catalog URL; loopback HTTP is allowed for local development
name Source name displayed by the CLI and used for provenance
priority Lower numbers are checked first
install_allowed Whether ID-based installation is permitted; default false
description Optional source description

Use the CLI to manage project entries:

specify preset catalog list
specify preset catalog add https://catalog.example.com/presets.json \
  --name my-org-presets --priority 5 --install-allowed
specify preset catalog remove my-org-presets

Only use --install-allowed after vetting the source. Omitting it adds a discovery-only catalog. Adding the first project entry replaces the implicit built-in stack; explicitly include other sources if you still need them. The user-level file is configured separately, not by catalog add.

Entry schema

Catalog JSON has a schema_version field and a presets object keyed by preset ID. The following illustrates a downloadable current release for a private catalog; replace the example URLs and digest before use:

{
  "schema_version": "1.0",
  "updated_at": "2026-11-03T00:00:00Z",
  "presets": {
    "team-traceability": {
      "id": "team-traceability",
      "name": "Team Traceability",
      "description": "Adds team traceability requirements to specifications",
      "author": "Example Team",
      "version": "2.0.0",
      "download_url": "https://github.com/example/team-traceability/archive/refs/tags/v2.0.0.zip",
      "sha256": "<64 hexadecimal digits for this archive>",
      "repository": "https://github.com/example/team-traceability",
      "documentation": "https://github.com/example/team-traceability/blob/main/README.md",
      "license": "MIT",
      "requires": {
        "speckit_version": ">=0.9.0"
      },
      "provides": {
        "templates": 1,
        "commands": 0,
        "scripts": 0
      },
      "tags": ["traceability", "governance"],
      "created_at": "2026-11-03",
      "updated_at": "2026-11-03"
    }
  }
}

Catalog provides holds contribution counts, unlike the manifest's provides.templates list. Keep metadata consistent with the published manifest. bundled: true can describe a first-party preset shipped inside Spec Kit; such an entry may have no download URL and requires the matching packaged files rather than a network download.

Single-version legacy entries remain valid. Current releases may omit sha256; when present, the digest is checked before installation. Community submission automation computes and records the downloaded archive's digest even when the submitter did not provide one.

Historical releases

To advertise older releases, add a releases mapping keyed by version:

{
  "version": "2.0.0",
  "download_url": "https://example.com/team-traceability-2.0.0.zip",
  "sha256": "<64 hexadecimal digits for the current archive>",
  "releases": {
    "1.5.0": {
      "download_url": "https://example.com/team-traceability-1.5.0.zip",
      "sha256": "<64 hexadecimal digits for the older archive>",
      "requires": {"speckit_version": ">=0.8.0"},
      "provides": {"templates": 1, "commands": 0, "scripts": 0}
    }
  }
}

This is the release-related portion of one entry, not a complete catalog. Each historical record needs its own HTTPS archive URL (or loopback HTTP for development) and a 64-digit SHA-256 digest. Digests may have a case-insensitive sha256: prefix and surrounding whitespace; other algorithm prefixes are rejected.

Optional historical requires and provides describe that release; they do not inherit the current release's values when absent. Shared metadata such as name, description, and catalog permissions is inherited. Historical requires.extensions uses the manifest's string-or-mapping dependency format. Do not put id, version, nested releases, or internal _catalog_name / _install_allowed fields inside a historical record.

Version keys must be distinct, including PEP 440-equivalent spellings, and cannot repeat the current version. Duplicate JSON keys are rejected before parsing can discard a release record.

specify preset info team-traceability --versions
specify preset add team-traceability --version 1.5.0

Available versions list the current release first, then historical versions in descending order. Exact release selection checks the downloaded manifest's ID and version before modifying installed presets.

The community catalog remains discovery-only and need not publish release histories. Bundle pin resolution is a separate capability; adding preset release records alone does not make bundle pins installable.

Lookup and failure behavior

Catalogs are checked in ascending priority order. The highest-precedence entry for an ID supplies both its release metadata and install permission. A lower-priority source cannot provide a missing historical version or override a discovery-only decision.

For exact ID lookup, malformed or oversized catalogs encountered before a winning entry fail resolution. A malformed lower-priority source cannot block an already found higher-priority entry. A non-object entry fails an exact lookup for its own ID, but all-catalog searches skip that entry and retain other valid ones.

Searches across all sources still fail on malformed catalogs. Historical release validation applies to winning entries; invalid history from a shadowed entry does not block a valid higher-priority replacement. Unreachable sources can be skipped when another source is readable. If every source fails, lookup, including info --versions, reports the fetch error rather than claiming no versions exist.

Cache and private sources

Catalogs are cached for one hour in .specify/presets/.cache/. The default catalog retains legacy catalog.json and catalog-metadata.json filenames; other sources use URL-hashed cache filenames. Cache payloads undergo the same shape validation as network responses; unreadable or invalid cached content triggers a fetch.

Private catalog and archive requests use the shared opt-in authentication configuration in ~/.specify/auth.json. A token environment variable by itself does not enable authentication. Configure only the hosts authorized to receive credentials; the authentication reference covers GitHub CLI, token, enterprise, and other supported providers. Do not put credentials in catalog JSON or source URLs.

See also