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

Welcome

joy is a native C++ package and build manager with a CLI designed to feel familiar to teams that prefer cargo/uv style workflows.

Core characteristics:

  • predictable command surface and lock-aware flows
  • explicit dependency source semantics (github, registry, git, path, archive)
  • reproducible workflows (sync, --locked, --offline, --frozen)
  • machine-readable JSON output for all commands (--json / --machine)
  • CLI-first developer ergonomics with objective editor integration gates

As of March 6, 2026, joy ships:

  • scaffolding (joy new, joy init)
  • dependency lifecycle (add, remove, update, tree, why, outdated)
  • registry config/discovery (registry, search, info)
  • offline/cache workflows (fetch, vendor, cache gc)
  • supply-chain verification (verify + SBOM output)
  • package publishing/ownership/yank workflows (package, publish, owner, yank)
  • local builds and execution (build, sync, run) with workspace routing and named targets
  • metadata and diagnostics (metadata, doctor, recipe-check, version)

Who This Book Is For

  • C++ developers who want faster reproducible local builds
  • contributors tracking current joy behavior and boundaries
  • automation authors integrating against stable JSON envelopes/error codes

Documentation Strategy

  • README.md is the overview and entrypoint.
  • This mdBook is the canonical detailed usage/reference documentation.
  • Planned/deferred features are explicitly labeled so expectations remain clear.

Status Legend

This book documents both shipped and planned behavior.

Use these labels when reading chapters:

  • Shipped: available in the current codebase.
  • Experimental: present but likely to evolve quickly.
  • Deferred: intentionally postponed and not available yet.
  • Planned: roadmap intent, not implemented.

When a chapter contains deferred/planned sections, it will call that out directly near the relevant heading.

Getting Started

Start here if you want your first joy project running quickly.

Recommended path:

  1. Install
  2. Quickstart
  3. Core Commands
  4. Dependency Source Backends
  5. Reproducible Workflows

Install

Build From Source (All Platforms)

cargo build --workspace
cargo install --path .

GitHub Release Binaries

joy publishes versioned release artifacts to GitHub Releases.

Current artifact set (subject to release workflow updates):

  • joy-vX.Y.Z-x86_64-unknown-linux-gnu.tar.gz
  • joy-vX.Y.Z-aarch64-apple-darwin.tar.gz
  • joy-vX.Y.Z-x86_64-apple-darwin.tar.gz
  • joy-vX.Y.Z-x86_64-pc-windows-msvc.zip
  • joy-vX.Y.Z-x86_64-pc-windows-gnu.zip (compatibility artifact)

Homebrew (Single-Repo Tap)

joy publishes and maintains Formula/joy.rb in this repository.

Install and upgrade:

brew tap harnesslabs/joy
brew install harnesslabs/joy/joy
brew upgrade joy

Scoop Metadata

Scoop metadata is maintained at:

  • packaging/scoop/joy.json

Release workflows publish generated package-manager metadata with concrete checksums as release artifacts.

See the Releasing reference for the exact release and packaging workflow.

Required Host Tools (for joy build / joy run)

You need host tools for local C++ builds and compiled recipe-backed dependencies:

  • C++ compiler (clang++, g++, or cl.exe on Windows MSVC)
  • ninja
  • cmake (for compiled recipes)
  • git

Use joy doctor to inspect tool availability and cache/recipe health.

Quickstart

60-Second Flow

joy new hello_cpp
cd hello_cpp
joy add nlohmann/json
joy run
joy tree

What happens:

  1. joy new scaffolds joy.toml, src/main.cpp, and .gitignore.
  2. joy add records dependency intent and runs sync-lite refresh by default.
  3. joy run builds the project and executes the binary.
  4. joy tree shows resolved dependency graph state.

Reproducible Follow-Up Flow

After the first run, validate deterministic state:

joy fetch
joy --frozen sync
joy --frozen build
joy --json verify --strict --sbom sbom.json

What Gets Created

Project-local state is kept under .joy/ and lock state in joy.lock.

Typical layout:

hello_cpp/
  joy.toml
  joy.lock
  compile_commands.json
  src/
    main.cpp
  .joy/
    build/
      compile_commands.<target>.json
    bin/
    include/
    lib/
    state/
      dependency-graph.json

Next Steps

Workflows

These chapters explain how to use joy day-to-day, from dependency edits to reproducible CI builds.

Core Commands

This chapter covers the most common day-to-day joy command flow, including command families added after the original docs wave.

Project Scaffolding

Create a new project:

joy new demo

Initialize the current directory:

joy init

Dependency Management

Add dependencies from different sources:

joy add nlohmann/json
joy add registry:nlohmann/json --version ^3
joy add git:https://github.com/fmtlib/fmt.git --as fmtlib/fmt --rev 11.0.2
joy add path:vendor/localdep --as localdep
joy add archive:https://example.com/libfoo.tar.gz --as acme/libfoo --sha256 <sha256>

Update dependencies:

joy update
joy update fmtlib/fmt --rev 11.1.0
joy update registry:nlohmann/json --version ^3

Remove dependencies:

joy remove fmtlib/fmt

Inspect the graph:

joy tree
joy tree --locked
joy why nlohmann/json
joy why nlohmann/json --locked
joy outdated
joy outdated --sources registry
joy outdated --sources github
joy outdated --sources git
joy outdated --sources path
joy outdated --sources archive

Registry Discovery and Setup

Configure and inspect registries:

joy registry list
joy registry add internal file:///path/to/internal-index.git --default
joy registry set-default internal
joy registry list --project

Search and inspect package metadata:

joy search json --registry internal --limit 20
joy info nlohmann/json --registry internal

Build, Sync, and Run

Build and run:

joy build
joy sync
joy run
joy run -- --app-arg value

Target-specific execution:

joy build --target tool
joy run --target tool

Lockfile controls:

joy build --locked
joy sync --update-lock
joy --frozen build

Metadata, Diagnostics, and Validation

joy metadata
joy doctor
joy recipe-check
joy verify
joy verify --strict --sbom sbom.json

joy metadata and joy doctor expose compile-db/graph/lockfile state and editor-gate diagnostics for CLI-first workflows.

Cache and Offline Preparation

joy fetch
joy vendor
joy vendor --output third_party/vendor
joy cache gc
joy cache gc --aggressive

Package Publishing Workflow

joy package init acme/widgets --kind header-only --version 0.1.0
joy publish --registry internal --rev v0.1.0
joy owner add acme/widgets alice --registry internal
joy owner list acme/widgets --registry internal
joy yank acme/widgets --version 0.1.0 --registry internal
joy yank acme/widgets --version 0.1.0 --undo --registry internal

Version Metadata

joy version
joy --json version

JSON / Machine Mode

Use --json (alias --machine) for stable automation envelopes:

joy --json tree
joy --json doctor
joy --json publish --registry internal

See Machine Interface and Machine Payload Matrix.

Reproducible Workflows

joy provides lockfile and offline controls intended for repeatable CI and local rebuilds.

joy sync

joy sync resolves dependencies, materializes headers/libs, refreshes graph artifacts, and updates joy.lock when needed without compiling the final binary.

joy sync

--locked

Reject lockfile updates for commands that consume dependency state.

joy build --locked
joy sync --locked
joy run --locked
joy why nlohmann/json --locked
joy tree --locked

Use --locked in CI to reject uncommitted dependency or manifest drift.

--update-lock

Force lockfile refresh when intentional dependency/manifest changes are present.

joy sync --update-lock
joy build --update-lock
joy run --update-lock

--locked and --update-lock are mutually exclusive.

--offline

Disable network access and use only cached dependency data.

joy --offline sync
joy --offline build
joy --offline run
joy --offline outdated

If cache is cold for required sources, commands fail with stable machine-readable error codes.

--frozen

--frozen is strict CI mode:

  • implies offline behavior
  • disallows lockfile changes
joy --frozen sync
joy --frozen build
joy --frozen run

Cache Warmup and Vendoring

Warm cache before strict offline runs:

joy fetch
joy sync

Vendor resolved lockfile sources for controlled source snapshots:

joy vendor
joy vendor --output third_party/vendor

Verify and SBOM in CI

Use joy verify to check provenance and produce SBOM data.

joy --json verify --strict --sbom sbom.json

This validates lock provenance fields by source backend and emits a baseline joy-sbom-v1 component list.

Typical CI Pattern

joy --json doctor
joy fetch
joy --frozen sync
joy --frozen build
joy --json verify --strict --sbom sbom.json

Automation should key off error.code and documented payload fields, not human text.

Workspaces and Targets

Workspaces (Shipped)

joy supports a workspace root manifest that routes project-scoped commands to member projects.

Workspace root joy.toml:

[workspace]
members = ["apps/app", "tools/tooling"]
default_member = "apps/app" # optional
profile = "release"         # optional: dev|release

Run project-scoped commands from the workspace root:

joy -p apps/app build
joy -p apps/app run
joy -p apps/app tree
joy -p apps/app sync
joy -p apps/app verify

If default_member is unset, workspace-root project commands require -p/--workspace-package.

Workspace Lockfile Behavior (Shipped)

When routed from workspace root with -p:

  • lockfile path is <workspace-root>/joy.lock
  • lock hash is computed from root manifest + member manifests
  • lock package entries aggregate across workspace members

Use this to keep workspace member dependency state deterministic in CI.

Workspace Profile Default (Shipped)

If [workspace] profile = "release" is set, sync/build/run default to release profile for workspace-routed commands unless --release explicitly overrides behavior.

Named Targets (Shipped)

Projects can define additional binary targets using [[project.targets]].

[project]
name = "demo"
version = "0.1.0"
cpp_standard = "c++20"
entry = "src/main.cpp"

[[project.targets]]
name = "tool"
entry = "src/tool.cpp"

Build or run a named target:

joy build --target tool
joy run --target tool

Dependency Source Backends

joy supports five direct dependency source backends.

Quick Matrix

  • github: canonical package id (owner/repo) with rev or version
  • registry: package from configured index, requires version
  • git: explicit git URL/path and explicit rev
  • path: local path source
  • archive: URL + required SHA-256 checksum

GitHub Source (source = "github")

joy add nlohmann/json
joy add fmtlib/fmt --rev 11.0.2
joy add fmtlib/fmt --version ^11
joy update fmtlib/fmt --rev 11.1.0

Registry Source (source = "registry")

joy add registry:nlohmann/json --version ^3
joy add registry:nlohmann/json --registry internal --version ^3
joy update registry:nlohmann/json --version ^3

Registry direct dependencies require --version.

Git Source (source = "git")

joy add git:https://github.com/fmtlib/fmt.git --as fmtlib/fmt --rev 11.0.2
joy update fmtlib/fmt --rev 11.1.0

Git source entries require --rev and do not use --version.

Path Source (source = "path")

joy add path:vendor/localdep --as localdep
joy remove localdep

Path dependencies are useful for local integration and monorepo-style source reuse.

Archive Source (source = "archive")

joy add archive:https://example.com/libfoo.tar.gz --as acme/libfoo --sha256 <sha256>
joy update acme/libfoo --sha256 <new-sha256>

Archive source requires both URL and SHA-256.

Source Filters in outdated

joy outdated --sources supports:

  • all
  • registry
  • github
  • git
  • path
  • archive

Example:

joy outdated --sources git

Provenance in joy.lock

Lock entries include backend-specific provenance fields used by verify, vendor, and deterministic refresh flows.

See Lockfile Schema.

Registry Discovery and Configuration

joy supports user-level and project-level registry configuration.

Registry Configuration Commands

List effective registries:

joy registry list
joy registry list --project

Add/remove registries:

joy registry add internal file:///path/to/internal-index.git
joy registry add internal file:///path/to/internal-index.git --default
joy registry remove internal
joy registry set-default internal

Use --project to write project-local config under .joy/.

Search and Package Info

Search package IDs:

joy search json
joy search json --registry internal --limit 50

Inspect package versions:

joy info nlohmann/json
joy info nlohmann/json --registry internal

Typical Setup Flow

joy registry add internal file:///srv/joy-index.git --default
joy search fmt --registry internal
joy info fmtlib/fmt --registry internal
joy add registry:fmtlib/fmt --registry internal --version ^11

Troubleshooting

If registry commands fail:

  • validate registry name and index path/URL
  • confirm joy registry list shows expected default
  • retry with --json and inspect error.code
  • check project-vs-user scope mismatch (--project vs default user scope)

Reference details: Registry Configuration.

Offline Fetch, Vendor, and Cache

These commands support deterministic dependency preparation and cache lifecycle control.

Warm Cache Without Building

joy fetch

fetch resolves current manifest dependencies and pre-populates source caches.

Vendor Lockfile Dependencies

joy vendor
joy vendor --output third_party/vendor

vendor copies lockfile-resolved sources into a project-local directory, grouped by package slug and resolved commit.

Cache Garbage Collection

joy cache gc
joy cache gc --aggressive
  • default gc: clears temporary cache paths
  • --aggressive: also clears source/archive cache roots

Offline + Frozen Workflow

joy fetch
joy --frozen sync
joy --frozen build

For lock/source integrity checks:

joy verify
joy verify --strict --sbom sbom.json

Common Pattern for CI

joy --json doctor
joy fetch
joy --frozen sync
joy --frozen build
joy --json verify --strict --sbom sbom.json

Package Publishing and Ownership

joy supports self-hosted package publishing and ownership workflows backed by registry index mutation.

Initialize a Reusable Package Manifest

joy package init acme/widgets --kind header-only --version 0.1.0

Kinds:

  • header-only (default)
  • cmake

Publish

joy publish --registry internal --rev v0.1.0

If --source-package is omitted, package id from the local package manifest is used.

Manage Owners

joy owner list acme/widgets --registry internal
joy owner add acme/widgets alice --registry internal
joy owner remove acme/widgets alice --registry internal

Yank / Unyank Releases

joy yank acme/widgets --version 0.1.0 --registry internal
joy yank acme/widgets --version 0.1.0 --undo --registry internal

Yanked versions are excluded from normal registry resolution.

Remote Registry Transport (Shipped)

publish, owner, and yank support remote git-backed registry URLs (for example file://... or hosted git remotes) via temporary checkout/commit/push flows.

Transport/auth failures are returned with stable error codes in JSON mode.

joy registry add internal file:///srv/joy-index.git --default
joy package init acme/widgets --kind header-only --version 0.1.0
joy publish --registry internal --rev v0.1.0
joy owner add acme/widgets alice --registry internal
joy yank acme/widgets --version 0.1.0 --registry internal
joy yank acme/widgets --version 0.1.0 --undo --registry internal

Dependencies

joy supports direct dependencies from multiple source backends and uses lockfile provenance to keep resolution deterministic.

Supported Direct Source Backends

  • github
  • registry
  • git
  • path
  • archive

See Dependency Source Backends for user workflows and Manifest Schema for field-level contract details.

Source-Aware Update Reporting

joy outdated --sources supports:

  • all (default)
  • registry
  • github
  • git
  • path
  • archive

Provenance + Integrity

joy.lock records source provenance fields used by:

  • joy verify
  • joy vendor
  • offline/frozen dependency reuse

See Lockfile Schema.

Registry Index Dependencies

Status: Shipped (git-backed registry index mode with named registries and publish/owner/yank workflows)

joy can resolve direct dependencies from configured registry indexes while fetching source content from package source coordinates.

Configure Registries

List effective configuration:

joy registry list
joy registry list --project

Add/set default registry:

joy registry add internal file:///path/to/internal-index.git --default
joy registry set-default internal

See Registry Discovery and Configuration.

Add Registry Dependencies

Use registry: package notation with a semver requirement.

joy add registry:nlohmann/json --version ^3
joy add registry:nlohmann/json --registry internal --version ^3

Registry direct dependencies require --version <range>.

Stored Metadata

In joy.toml, registry dependencies are stored with source = "registry" plus version range and optional named registry.

In joy.lock, selected concrete versions/commits and source provenance fields are recorded for deterministic refresh and verification.

Registry Index Versions

joy supports:

  • registry index version = 1 (release -> source mapping)
  • registry index version = 2 (optional embedded package metadata summaries)

Registry v2 summaries can include kind/include roots/dependencies plus digest for mismatch detection.

Publish/Owner/Yank Integration

Registry commands are part of the self-hosted publish loop:

  • joy publish
  • joy owner list|add|remove
  • joy yank [--undo]

Remote git-backed registry URLs are supported in current shipped behavior.

Current Limitations (Important)

  • Registry alias package support (registry name different from canonical source package id) remains deferred.
  • Broader non-git registry transport models are deferred.

Recipes (Compiled Dependencies)

joy ships a curated recipe index in recipes/ for dependencies that require build/install metadata.

Examples include:

  • fmtlib/fmt
  • madler/zlib
  • gabime/spdlog
  • Neargye/magic_enum
  • jarro2783/cxxopts

How Recipes Fit the Build Pipeline

When a dependency is recipe-backed and compiled:

  1. joy resolves dependency graph + recipe metadata.
  2. It computes ABI-sensitive cache keys/hashes.
  3. It builds dependency artifacts with CMake + Ninja in global cache.
  4. It installs headers/libs into project-local .joy/ state.

Generic CMake Fallback (Shipped)

If a dependency has no recipe but contains a top-level CMakeLists.txt, joy can run a generic CMake-first compiled fallback path.

This keeps more compiled packages usable without waiting for curated recipe additions.

Validate Recipe Metadata

joy recipe-check
joy --json recipe-check

Use this in local development and CI whenever recipe metadata changes.

Troubleshooting

Start with joy doctor and the common failure patterns in the next chapter.

Common Failures

manifest_not_found

You ran a project-scoped command outside a joy project directory.

Fix:

  • run joy init in the current directory, or
  • run joy new <name> and move into the created project

Toolchain / Ninja Not Found

Use joy doctor to inspect compiler and ninja availability.

Common fixes:

  • install a compiler (clang++, g++, or MSVC toolchain)
  • install ninja
  • install cmake if you use compiled dependencies

Workspace Member Errors

If running from a workspace root, use -p/--workspace-package <member> unless default_member is configured.

Common related codes:

  • workspace_member_required
  • workspace_member_not_found
  • workspace_member_invalid

Dependency Source Validation Errors

If joy add/joy update fails with source-validation style errors:

  • verify source-specific argument contract:
    • registry: requires --version
    • git: requires --rev
    • archive: requires --sha256
  • verify prefixes are correct (registry:, git:, path:, archive:)
  • use --json and inspect error.code (invalid_add_args, invalid_update_args, invalid_dependency_source)

Registry Configuration / Lookup Failures

If registry flows fail:

  • verify registry exists with joy registry list
  • ensure expected default registry is set (joy registry set-default <name>)
  • ensure scope is correct (--project vs user default scope)
  • re-run in --json mode and inspect codes such as:
    • registry_not_configured
    • registry_config_error
    • registry_load_failed
    • registry_package_not_found

VSCode / clangd Cannot Find Dependency Headers

If you see include errors after joy add, verify:

  1. joy sync or joy build has been run after adding dependencies.
  2. compile_commands.json exists at project root.
  3. .joy/include/... contains expected dependency headers.

joy writes:

  • project root compile_commands.json
  • target-specific .joy/build/compile_commands.<target>.json

Use joy doctor and joy metadata to inspect compile-db and graph artifact state.

clangd

clangd usually auto-discovers compile_commands.json from workspace root.

VSCode C/C++ Extension (cpptools)

If cpptools does not auto-detect the compile database, set C_Cpp.default.compileCommands to project-root compile_commands.json.

Nested Dependencies Missing

If transitive dependencies appear missing:

  • rerun joy sync to refresh lockfile + graph + materialization
  • run joy tree / joy why <package>
  • inspect joy metadata for graph/lockfile state

If package metadata digests disagree between sources, expect package_metadata_mismatch until metadata is reconciled.

Offline Cache Misses

If joy --offline ... fails:

joy fetch
joy sync

Then retry offline/frozen commands.

lockfile_not_found (verify / vendor)

joy verify and joy vendor require joy.lock.

Fix:

joy sync
# or
joy sync --update-lock

Verification Failures (verify_failed)

When joy verify fails:

  • inspect failing package rows in JSON data.results
  • check source provenance fields in joy.lock
  • for archive sources, ensure source_checksum_sha256 is present and valid
  • if vendored source exists, compare against resolved source checksum

Publishing Transport / Auth Failures

If publish workflows fail against remote registry URLs:

  • confirm registry URL is reachable and writable
  • confirm git credentials/token permissions for push
  • inspect JSON error code:
    • registry_transport_failed
    • registry_auth_failed

Build Failures

When joy build fails, focus on compiler diagnostics first:

  • inspect ninja/compiler stderr/stdout details
  • inspect generated build graph and compile DB paths in JSON payload
  • use --json for stable failure envelopes

Reference

This section documents stable command contracts, schemas, machine envelopes, and operational runbooks.

Use this section when you need precise interfaces instead of task-oriented walkthroughs.

Operational runbooks:

Command Reference

This chapter maps shipped joy commands and grouped subcommands.

Use workflow chapters for task-first guidance and this chapter for quick command/flag lookup.

Global Flags

Global flags apply before subcommands:

joy --json <command>
joy --offline <command>
joy --frozen <command>
joy -p <workspace-member> <command>

Human UX controls:

joy --color auto|always|never <command>
joy --progress auto|always|never <command>
joy --glyphs auto|unicode|ascii <command>

Top-Level Commands

Project Scaffolding

  • joy new <name> [--force]
  • joy init [--force]

Dependency Lifecycle

  • joy add <package> [--as <name>] [--rev <rev>] [--version <range>] [--registry <name>] [--sha256 <sum>] [--no-sync]
  • joy remove <package>
  • joy update [<package>] [--rev <rev>] [--version <range>] [--registry <name>] [--sha256 <sum>]
  • joy tree [--locked]
  • joy why <package> [--locked]
  • joy outdated [--sources all|registry|github|git|path|archive]

Registry Discovery

  • joy registry <subcommand>
  • joy search <query> [--registry <name>] [--limit <n>]
  • joy info <package> [--registry <name>]

Dependency State / Integrity

  • joy fetch
  • joy vendor [--output <dir>]
  • joy verify [--strict] [--sbom <path>]
  • joy cache gc [--aggressive]
  • joy recipe-check

Publishing + Ownership

  • joy package init <id> [--version <semver>] [--kind header-only|cmake] [--force]
  • joy publish [--registry <name>] [--rev <rev>] [--source-package <id>]
  • joy owner <subcommand>
  • joy yank <package> --version <semver> [--undo] [--registry <name>]

Build and Execution

  • joy build [--release] [--target <name>] [--locked] [--update-lock]
  • joy sync [--release] [--locked] [--update-lock]
  • joy run [--release] [--target <name>] [--locked] [--update-lock] [-- <args...>]

Diagnostics and Metadata

  • joy metadata
  • joy doctor
  • joy version

Grouped Subcommands

joy registry

  • list [--project]
  • add <name> <index> [--default] [--project]
  • remove <name> [--project]
  • set-default <name> [--project]

joy owner

  • list <package> [--registry <name>]
  • add <package> <owner> [--registry <name>]
  • remove <package> <owner> [--registry <name>]

joy package

  • init <id> [--version <semver>] [--kind header-only|cmake] [--force]

joy cache

  • gc [--aggressive]

Minimal Example Sweep

joy new demo
joy add nlohmann/json
joy tree
joy why nlohmann/json
joy registry list
joy search json
joy fetch
joy vendor
joy verify --strict
joy cache gc
joy package init acme/widgets
joy publish --registry internal --rev v0.1.0
joy owner list acme/widgets --registry internal
joy yank acme/widgets --version 0.1.0 --registry internal
joy metadata
joy doctor
joy build
joy sync
joy run -- --help

CLI UX Controls

Status: Shipped (human output controls)

joy has separate human and machine interfaces:

  • human mode (default) for local developer experience
  • machine mode (--json / --machine) for automation

Human Output Controls

Global flags:

joy --color auto|always|never ...
joy --progress auto|always|never ...
joy --glyphs auto|unicode|ascii ...

Convenience aliases:

  • --no-progress (same as --progress=never)
  • --ascii (same as --glyphs=ascii)

Environment Variables

joy also consults environment variables for human output behavior:

  • JOY_COLOR
  • JOY_PROGRESS
  • JOY_GLYPHS
  • NO_COLOR
  • CLICOLOR
  • CLICOLOR_FORCE
  • TERM
  • CI

Compatibility Policy

Human output is allowed to improve over time (layout, colors, wording, progress rendering).

Automation should use --json, which remains the stable interface.

Manifest Schema

joy supports three joy.toml document shapes:

  • project manifest ([project])
  • workspace manifest ([workspace])
  • reusable package manifest ([package])

Source of truth: src/manifest.rs.

Project Manifest

[project]
name = "demo"
version = "0.1.0"
cpp_standard = "c++20"
entry = "src/main.cpp"
extra_sources = ["src/lib.cpp"]
include_dirs = ["include"]

[[project.targets]]
name = "tool"
entry = "src/tool.cpp"
extra_sources = []
include_dirs = []

[dependencies]
"nlohmann/json" = { source = "github", rev = "HEAD" }

Required project fields:

  • name
  • version
  • cpp_standard
  • entry

Optional project fields:

  • extra_sources (string[])
  • include_dirs (string[])
  • [[project.targets]] (named target list)

Workspace Manifest

[workspace]
members = ["apps/app", "tools/tooling"]
default_member = "apps/app"
profile = "release"

Required workspace fields:

  • members (string[], non-empty)

Optional workspace fields:

  • default_member (must be present in members)
  • profile (dev|release)

Package Manifest

Used for reusable package publishing workflows.

[package]
id = "acme/widgets"
version = "0.1.0"
kind = "header_only"

[headers]
include_roots = ["include"]

[dependencies]
"fmtlib/fmt" = { source = "github", version = "^11" }

Required package fields:

  • id (canonical package id)
  • version
  • kind (header_only|cmake)

Optional package fields:

  • [headers].include_roots
  • package-level [dependencies]

Dependency Spec Contract

Each dependency entry is a DependencySpec:

  • source
  • optional package
  • source-specific fields: rev, version, registry, git, path, url, sha256

Source Rules

source = "github"

  • supports rev or version (mutually exclusive)
  • does not allow registry, git, path, url, sha256

source = "registry"

  • requires version
  • disallows rev
  • optional registry
  • does not allow git, path, url, sha256

source = "git"

  • requires git and rev
  • does not support version
  • does not allow registry, path, url, sha256

source = "path"

  • requires path
  • disallows rev and version
  • does not allow registry, git, url, sha256

source = "archive"

  • requires url and sha256
  • disallows rev and version
  • does not allow registry, git, path

Dependency Prefix Parsing (CLI Input)

Input prefixes map to source backends:

  • registry:<id> -> registry
  • github:<id> -> github
  • git+<url-or-path> or git:<url-or-path> -> git
  • path:<relative-or-absolute-path> -> path
  • archive:<url> -> archive
  • no prefix -> github

Lockfile Schema

joy.lock is a TOML lockfile used for reproducibility and source provenance.

Source of truth: src/lockfile.rs and lockfile assembly logic in src/commands/build.rs.

Top-Level Fields

version = 1
manifest_hash = "<sha256>"
generated_by = "joy <version>"

[[packages]]
...
  • version: lockfile schema version (currently 1)
  • manifest_hash: hash used for stale-lock detection
  • generated_by: generator version string
  • packages: resolved package entries

Package Entry Fields

Each [[packages]] entry includes:

  • identity and resolution:
    • id
    • source
    • requested_rev
    • requested_requirement (optional)
    • resolved_version (optional)
    • resolved_commit
    • resolved_ref (optional)
  • source provenance:
    • registry (optional)
    • source_package (optional)
    • source_git (optional)
    • source_path (optional)
    • source_url (optional)
    • source_checksum_sha256 (optional, required for archive source)
  • dependency/build metadata:
    • header_only
    • header_roots[]
    • deps[]
    • recipe (optional)
    • metadata_source (optional)
    • package_manifest_digest (optional)
    • declared_deps_source (optional)
    • abi_hash
    • libs[]
    • linkage (optional)

Source Provenance Expectations

  • github: no extra provenance field required
  • registry: should include registry
  • git: should include source_git
  • path: should include source_path
  • archive: should include source_url and source_checksum_sha256

joy verify enforces source-specific provenance checks and checksum policy.

Lockfile Lifecycle

  • sync/build/run may write or refresh lockfile based on lock mode flags.
  • --locked rejects drift.
  • --update-lock forces refresh.
  • --frozen implies locked/offline behavior.

Workspace-routed commands use workspace lockfile semantics when invoked from workspace root with selected member routing.

Registry Configuration

joy merges registry config from user scope and optional project scope.

Source of truth: src/registry_config.rs and src/commands/registry_cmd.rs.

Scopes

  • user scope (default): $JOY_HOME/config/registries.toml
  • project scope (--project): <project>/.joy/registries.toml

Effective config merge behavior:

  1. user config loaded first
  2. project config overlays user entries
  3. project default overrides user default

File Format

version = 1
default = "internal"

[registries.internal]
index = "file:///srv/joy-index.git"

[registries.default]
index = "https://github.com/harnesslabs/joy-registry.git"

Fields:

  • version (must be 1)
  • default (optional registry name)
  • registries.<name>.index (URL/path)

CLI Management

joy registry list
joy registry add internal file:///srv/joy-index.git --default
joy registry remove internal
joy registry set-default internal

joy registry list --project
joy registry add internal file:///srv/joy-index.git --project --default

Validation Rules

  • registry name must be non-empty and [A-Za-z0-9_.-] only
  • registry index path/URL must be non-empty
  • unsupported config versions fail with registry_index_unsupported_version in publishing/index flows or parse/config errors in config loading paths
  • discovery: joy search, joy info
  • dependency resolution: joy add registry:<id> --version <range>
  • publish workflows: joy publish, joy owner, joy yank

Machine Interface

joy supports machine-readable output for all commands via --json (alias --machine).

JSON output always uses one of two top-level envelopes:

{
  "schema_version": "1",
  "joy_version": "<semver>",
  "ok": true,
  "command": "<name>",
  "data": { "...": "..." }
}
{
  "schema_version": "1",
  "joy_version": "<semver>",
  "ok": false,
  "command": "<name>",
  "error": {
    "code": "<stable_code>",
    "message": "<human_text>"
  }
}

Compatibility Policy

  • Top-level envelope keys are stable: schema_version, joy_version, ok, command, plus data or error.
  • Existing payload keys are additive within a fixed schema_version.
  • schema_version changes only for explicit contract revisions.
  • error.code is the automation key; error.message is human-oriented and may evolve.
  • Human output is not a machine contract; automation should use --json.

Workspace Routing Metadata

Project-scoped command payloads may include additive workspace fields when invoked from workspace root routing:

  • workspace_root (string | null)
  • workspace_member (string | null)

Build/Run/Sync Target Metadata

build and run include target metadata:

  • target
  • target_default

build, sync, and run include lockfile metadata:

  • lockfile_path
  • lockfile_updated

Machine Payload Catalog

See Machine Payload Matrix for command-specific key maps.

Error Code Catalog

See Error Codes for stable machine error keys.

Machine Payload Matrix

This chapter summarizes command-specific JSON payload keys emitted under the machine envelope.

For envelope compatibility policy, see Machine Interface.

Core Project Commands

new

  • project_root
  • manifest_path
  • created_paths

init

  • project_root
  • manifest_path
  • created_paths

add

  • project_root, manifest_path
  • dependency, source, registry, source_package
  • fetched (source fetch metadata when applicable)
  • installed (header install metadata)
  • sync (sync-lite result)

remove

  • project_root, manifest_path
  • dependency
  • header_link_removed
  • warnings

update

  • project_root, manifest_path
  • updated_count, manifest_changed
  • updated[] entries with source/resolve/install metadata
  • warnings

tree

  • project_root, manifest_path
  • roots[]
  • packages[] with source/provenance/dependency edges

why

  • project_root, manifest_path
  • package
  • locked
  • roots[], paths[]
  • package_info

outdated

  • project_root, manifest_path, lockfile_path
  • roots[]
  • sources
  • summary (direct/transitive/source counts)
  • packages[]
  • outdated[]

Registry and Discovery

registry (all subcommands)

  • action (add|remove|set-default) for mutating subcommands
  • scope
  • name, index, default_set, removed (as applicable)
  • registries[] and default for list
  • query
  • registry
  • count
  • packages[] (id, latest_version)

info

  • registry
  • package
  • latest_version
  • versions[]

Dependency State, Integrity, and Cache

fetch

  • project_root, manifest_path
  • fetched_count, skipped_count
  • fetched[] with source/provenance/cache metadata

vendor

  • project_root, lockfile_path, output_dir
  • vendored_count, skipped_count
  • vendored[], skipped[]

verify

  • project_root, lockfile_path
  • strict
  • summary (package_count, passed_count, warning_count, failed_count)
  • results[]
  • sbom
  • sbom_path

cache (gc)

  • action (gc)
  • aggressive
  • removed_paths[]
  • cache_root

recipe-check

  • recipes_root
  • recipe_count
  • packages[]

Publishing and Ownership

package (init)

  • action (init)
  • manifest_path
  • id, version, kind

publish

  • package
  • version
  • registry
  • index_path
  • git_committed

owner

  • action (list|add|remove)
  • package
  • registry
  • owners[] (for list)
  • changed (for mutation flows)

yank

  • package
  • version
  • registry
  • yanked
  • changed
  • index_path

Build, Run, and Sync

build

  • project_root, manifest_path
  • build_file, binary_path, source_file, compiled_sources[]
  • target, target_default, profile
  • include_dirs[], link_dirs[], link_libs[]
  • compiled_dependencies_built[]
  • toolchain
  • ninja_status, ninja_stdout, ninja_stderr
  • lockfile_path, lockfile_updated

sync

  • project_root, manifest_path
  • profile
  • include_dirs[], link_dirs[], link_libs[]
  • compiled_dependencies_built[]
  • toolchain (optional)
  • lockfile_path, lockfile_updated

run

  • project_root, binary_path, build_file
  • toolchain, profile, target, target_default
  • args[], exit_code
  • stdout, stderr
  • lockfile_path, lockfile_updated

Diagnostics and Metadata

metadata

  • project_root, manifest_path
  • roots[]
  • artifacts (joy/state/build/compile-db paths + flags)
  • lockfile
  • graph
  • editor_extension_gate

doctor

  • ok, cwd
  • env, tools, toolchain, cache, recipes
  • project, artifacts, lockfile, dependency_metadata
  • editor_extension_gate
  • project_warnings[], project_hints[]

version

  • joy_version
  • schema_version
  • build_target
  • build_profile
  • git_commit

Workspace Metadata Note

Project-scoped command payloads can include additive workspace_root and workspace_member keys when invoked from workspace-routed contexts.

Error Codes

joy command failures return machine-readable error codes via JSON envelopes.

{
  "ok": false,
  "command": "build",
  "error": {
    "code": "manifest_not_found",
    "message": "..."
  }
}

Stability Policy

  • error.code is the stable automation key.
  • error.message is human text and may evolve.
  • New codes may be introduced additively.

Core / CLI

  • cli_parse_error
  • cwd_unavailable
  • io_error
  • output_serialize_failed
  • not_implemented

Manifest / Workspace / Package Identity

  • manifest_not_found
  • manifest_parse_error
  • manifest_write_error
  • manifest_exists
  • manifest_hash_failed
  • non_empty_directory
  • path_exists
  • invalid_package_id
  • invalid_package_version
  • invalid_target
  • dependency_not_found
  • workspace_member_invalid
  • workspace_member_required
  • workspace_member_not_found

Dependency Input / Source Validation

  • invalid_add_args
  • invalid_update_args
  • invalid_dependency_source
  • source_backend_unsupported
  • registry_alias_unsupported
  • package_metadata_mismatch
  • dependency_resolve_failed

Lockfile / Reproducibility

  • invalid_lock_flags
  • lockfile_not_found
  • lockfile_missing
  • lockfile_parse_error
  • lockfile_hash_failed
  • lockfile_stale
  • lockfile_incomplete
  • lockfile_mismatch
  • lockfile_write_failed
  • lockfile_package_assembly_failed

Fetch / Network / Registry

  • fetch_failed
  • offline_cache_miss
  • offline_network_disabled
  • invalid_version_requirement
  • version_not_found
  • invalid_checksum
  • checksum_mismatch
  • archive_format_unsupported
  • registry_not_configured
  • registry_load_failed
  • registry_package_not_found
  • registry_config_error
  • registry_index_parse_error
  • registry_index_serialize_failed
  • registry_index_unsupported_version

Git / Registry Transport

  • git_failed
  • registry_transport_failed
  • registry_auth_failed

Toolchain / Build / Graph Materialization

  • toolchain_not_found
  • toolchain_probe_failed
  • env_setup_failed
  • cache_setup_failed
  • recipe_load_failed
  • recipe_validation_failed
  • dependency_graph_invalid
  • missing_recipe
  • missing_cmake_metadata
  • missing_link_metadata
  • generic_cmake_no_libraries
  • cmake_build_failed
  • library_install_failed
  • header_install_failed
  • include_dir_not_found
  • entry_not_found
  • source_not_found
  • build_failed
  • ninja_file_write_failed
  • ninja_spawn_failed
  • compile_db_write_failed
  • state_index_error
  • state_cleanup_failed
  • state_graph_error

Command Policy / Lifecycle

  • frozen_disallows_add
  • frozen_disallows_remove
  • frozen_disallows_update
  • add_sync_failed
  • run_spawn_failed
  • run_failed
  • publish_version_exists
  • invalid_owner

Verify / SBOM / Vendor

  • verify_failed
  • verify_scan_failed
  • verify_hash_path_missing
  • sbom_serialize_failed
  • vendor_copy_failed

Releasing joy

This chapter documents the release process for joy binaries, GitHub Releases, and release-adjacent notes.

Versioning Policy

  • joy uses semantic versioning with pre-1.0 expectations (0.y.z).
  • Cargo.toml is the source of truth for the version.
  • Git tags use vX.Y.Z (for example v0.4.0).

Current Release Targets

The release workflow currently publishes:

  • x86_64-unknown-linux-gnu
  • aarch64-apple-darwin
  • x86_64-apple-darwin
  • x86_64-pc-windows-msvc
  • x86_64-pc-windows-gnu (compatibility artifact during the beta transition)

Release Workflow

  • Release PR + tag automation: .github/workflows/release-plz.yaml
  • Binary/release asset workflow: .github/workflows/release.yaml
  • Packaging config for release-plz: release-plz.toml

release-plz handles version/changelog PRs and tag creation. Tag pushes (v*) trigger the binary release workflow.

Pre-Release Checklist

  1. Ensure milestone issues and notes are up to date.
  2. Confirm local environment health (joy --json doctor).
  3. Run CI-parity commands (just ci-local; optionally compiled e2e checks).
  4. Validate distribution metadata templates (just dist-metadata-check).
  5. Confirm PR checks (gh pr checks <pr-number>).
  6. Ensure RELEASE_PLZ_PAT secret is configured with contents + pull_requests write permission (RELEASE_PLZ_TOKEN is also supported as a fallback name).
  7. If default-branch protection blocks workflow pushes, set JOY_RELEASE_PAT so release metadata commits can update Formula/joy.rb.
  1. Merge regular changes into main.
  2. release-plz opens/updates a release PR with version and changelog updates.
  3. Merge the release PR.
  4. release-plz release creates vX.Y.Z tag.
  5. .github/workflows/release.yaml builds artifacts, publishes GitHub Release assets, and updates package metadata.

Monitor Actions and verify the GitHub Release contains archives/checksums/signatures/SBOM.

Manual Backstop (If Needed)

git tag -a vX.Y.Z -m "joy vX.Y.Z"
git push origin vX.Y.Z

Packaging Outputs

The release workflow generates and publishes concrete metadata with release checksums:

  • Formula/joy.rb (single-repo Homebrew tap source of truth)
  • packaging/scoop/joy.json

Users can install via Homebrew tap:

brew tap harnesslabs/joy
brew install harnesslabs/joy/joy
brew upgrade joy

Incident Response Runbook

This runbook covers high-priority operational incidents for joy release consumers.

Severity Guidelines

  • SEV-1: active supply-chain risk (malicious dependency, compromised release artifact)
  • SEV-2: broken release affecting installs/builds broadly
  • SEV-3: localized issue with workaround

Scenario A: Compromised Dependency

  1. Freeze releases immediately.
  2. Identify affected versions and package IDs.
  3. Remove/disable affected dependency source in registry metadata.
  4. Publish a patched release and changelog/security advisory.
  5. Notify users with explicit upgrade/rollback instructions.

Scenario B: Bad Release Artifact

  1. Mark the release as yanked in release notes.
  2. Repoint package-manager channels (Homebrew/Scoop metadata) to the prior safe release.
  3. Publish fixed artifacts under a new version tag.
  4. Verify checksums/signatures and smoke test install paths.

Rollback Drill (Per Release)

Perform a rollback drill once per release cycle:

  1. Simulate a bad release in a staging repository.
  2. Execute package-manager metadata rollback.
  3. Confirm install commands resolve to the prior known-good version.
  4. Record elapsed time and gaps in the release notes archive.

Communication Checklist

  • Open internal incident tracker entry.
  • Publish public status update within 24 hours for SEV-1/SEV-2.
  • Update SECURITY.md if policy/process changes were required.

Project Status

This section tracks the current implementation boundary for joy and clearly separates shipped behavior from deferred/planned work.

Snapshot date for this status section: March 6, 2026.

How to Read This Section

  • Shipped: available on main and expected to work today
  • Planned: intended near-term work but not yet guaranteed shipped
  • Deferred: intentionally out of current implementation scope

See Roadmap / Deferred Features for current non-shipped scope.

Roadmap / Deferred Features

Status entries below are intentionally explicit so users know what is not ready yet.

Snapshot date: March 6, 2026.

Planned (Near-Term, Not Fully Closed)

  • Additional offline CI matrix hardening for cold/warm/vendored permutations and deterministic error-path coverage.
  • Further hardening of self-hosted publishing auth/token policy and transport reliability for non-local registry deployments.
  • Continued source/backend parity hardening in resolver/fetch edge-case paths beyond current shipped baseline.

Deferred (Known, Expected Future Work)

  • Broader registry protocols beyond current git-backed index mode.
  • Registry alias package support (registry name different from canonical source package id).
  • Deeper package feature/variant support and additional non-binary target kinds.
  • Full-screen TUI mode (current roadmap remains focused on line-oriented CLI and machine JSON interfaces).
  • Editor extension productization remains gated behind objective CLI/compile-db criteria; CLI-first remains default.

Documentation Notes

Roadmap intent may appear in docs where relevant. Planned/deferred items are never implied as shipped behavior.

Contributing Docs

This chapter is for contributors touching user-facing documentation.

Source of Truth

  • README.md is the project overview and entrypoint.
  • book/ is the in-depth user and reference documentation.
  • docs/ contains legacy entrypoints/stubs that should point to the mdBook.

Writing Guidelines

  • Prefer task-oriented explanations first, then reference detail.
  • Mark incomplete/deferred features explicitly.
  • Keep command examples copy/paste ready.
  • When human output examples are likely to change, avoid over-specifying exact formatting unless the formatting itself is the subject.

Validation

Use local docs commands (defined in justfile) before opening a PR:

  • just docs-build
  • just docs-lint