Skip to content
WIP

This guide is a work in progress. Barry is also a work in progress.

bag.yaml

bag.yaml is a bag’s manifest. It sits at the root of the bag’s directory, and Barry reads it whenever it registers, builds, or loads the bag. The schema is strict: Barry rejects any key it doesn’t know, so a misspelled key fails the whole manifest instead of loading as a bag that quietly does nothing. The manifest schema is the source of truth for every key on this page.

Every bag needs name and description. Most bags add one or two more keys: stores for data, services for long-running processes, jobs and schedules for background work, or traits and bounds for policy. A bag’s tools come from code (tools.ts or src/tools.ts), not from the manifest, and its instructions, skills, and actions come from the instructions/, skills/, and actions/ directories beside the manifest. To create a bag with a starter manifest, run barry new. To validate a bag directory without installing it, run barry bag check.

Barry substitutes constants before it validates the manifest: anywhere a value appears, {constant: name} or {constant: bag:name} replaces the whole value with one the constants key defines. Barry also accepts the retired keys verbs and nouns and ignores them, so a remote server that still sends them keeps loading. It rejects tool-metadata and deployments with a message that names their replacement.

Every key bag.yaml accepts, with its type. Each key has its own section under Fields.

name: string # required
description: string # required
version: string
extends: string
instructions: string
bags: list of strings or map of <name>
<name>: object or null
repo: string
rev: string
ref: string
path: string
subdir: string
requires: map of <bag>
<bag>: object
reason: string
optional: boolean # default false
resource: string
allow-remote: boolean
dependencies: map of <package> or string
<package>: object
reason: string # required
bin: string
optional: boolean
install: object
macos: string
linux: string
permissions: list of objects
kind: string # required
app: string
reason: string # required
tools: list of single-key maps
prompts: object
entry: string # required
traits: map of <name>
<name>: object
description: string # required
access: string # default read
includes: list of strings
harnessTraits: map of strings
nativeTools: map of string lists
bags: list of strings
namespaces: list of strings
skills: list of strings
instructions: list of strings
bounds: list of strings
bound: object
bounds: map of <name>
<name>: object
description: string # required
bound: object # required
constants: map of any value
credentials: map of <ENV_VAR>
<ENV_VAR>: object
source: string # required
item: string # required
field: string # required
vendors: list of objects
id: string # required
label: string # required
baseUrl: string or object
credential: object or null # required
compatible: list of objects
models: list of objects # required
mcp-servers: map of <name>
<name>: object
type: string
url: string
command: string
args: list of strings
env: list of strings
session-scoped: boolean
auth: object
command: string # required
args: list of strings
check: object
cli: object
alias: string
default_notifier: object
tool: string # required
target: string
slash-commands: object
entry: string # required
commands: list of objects # required
hooks: object or list
agent: list of objects
event: string # required
run: string # required
args: list of strings
matcher: string
cursor-matcher: string
cursor-events: list of strings
timeout: number
barry: list of objects
store: object
owned_by: string # required
engine: string # required
migrations: string # required
stores: map of <name>
<name>: object
owned_by: string # required
engine: string # required
migrations: string # required
snapshots: map of <store>
<store>: object
entry: string # required
restores: map of <store>
<store>: object
entry: string # required
services: map of <name>
<name>: object
description: string # required
entry: string
command: string
args: list of strings
env: list of strings
secrets: list of strings
optional-secrets: list of strings
working-directory: string
autostart: string # default login
serves: string
health: string
socket: string
user: string
local-only: boolean
apps: map of <name>
<name>: object
name: string # required
description: string # required
platform: string # required
build: string or object # required
output: string # required
id: string
protocol-version: number
capabilities: object
macos: object
ios: object
jobs: map of <name>
<name>: object
description: string
version: string or number
runs: object # required
using: string # required
secrets: list of strings
env: list of strings
working-directory: string
requires: object
preflight: object
validate: object
input_schema: object
output_schema: object
timeout: number or string
bounds: list of strings
schedules: map of <name>
<name>: object
description: string
every: string
cron: string
timezone: string # default UTC
at: string
job: string
action: string or object
entry: string
args: list of strings
env: list of strings
secrets: list of strings
timeout: number or string
identity: string
inputs: object
traits: list of strings
bounds: list of strings
overlap: string # default skip
catch_up: string # default once
enabled: boolean # default true
scripts: map of <name>
<name>: object
description: string # required
entry: string
command: string
args: list of strings
env: list of strings
secrets: list of strings
working-directory: string
infra: object
provider: string # required
root: string # required
variables: list of strings
provides: object
local: object
tunnel: object
reach: list of strings # required
gates: list of strings
  • Type: string
  • Required: yes

The bag’s name. Barry registers the bag under it, qualifies the bag’s traits and bounds with it (name:trait), and names the bag’s automatic trait after it. An extension’s name must be a plain bag name of letters, digits, dots, underscores, and hyphens.

name: weather
  • Type: string
  • Required: yes

One line that says what the bag provides. Barry shows it wherever it lists bags, such as barry bag list and barry bag show.

description: Current conditions and forecasts from the National Weather Service
  • Type: string
  • Required: no

The bag’s own release, such as v0.1. Barry doesn’t branch on it: the manifest format has one version, so this field describes the bag, not the schema. Barry’s first-party bags omit it.

version: v0.1
  • Type: string
  • Required: no

Makes this bag an extension of the named parent bag. An extension lives in <parent>/extensions/<name>/, registers as <parent>/<name>, stamps its tools with the parent’s namespace, and receives the parent’s grant instead of an automatic trait of its own. An extension may declare only name, extends, description, version, instructions, tools, dependencies, schedules, and scripts; Barry rejects every other key with a message that says to declare it on the parent.

extends: sessions
  • Type: string
  • Required: no

Legacy guidance text. Barry turns it into an always-on instruction named after the bag. Prefer a file under the bag’s instructions/ directory: a file with the bag’s name supersedes this string, which is how a bag migrates off it.

instructions: Use the weather tools for forecasts instead of searching the web.
  • Type: list of strings or map of <name>
  • Required: no

Sub-bags this bag composes. A composed bag absorbs its children’s tools, skills, instructions, actions, traits, and other capabilities, and the parent’s automatic trait expands to its children’s namespaces.

The list form names bags that the registry already holds. The map form also says where each child comes from, so installing the parent fetches it. A map entry with no value composes the child without naming a source.

bags:
tracker:
locks:
path: ./locks
notes:
repo: perintyler/notes-bag
ref: main
  • Type: object or null
  • Required: no

Where one child comes from. Set exactly one of repo or path.

  • Type: string
  • Required: no

A Git remote: owner/name shorthand or any URL Git can clone.

  • Type: string
  • Required: no

The exact commit to check out. Needs repo; conflicts with ref.

  • Type: string
  • Required: no

The branch or tag to track when rev is absent. Needs repo.

  • Type: string
  • Required: no

A directory relative to this bag’s directory. Conflicts with repo, rev, ref, and subdir.

  • Type: string
  • Required: no

The directory inside the clone that holds the bag, for a repository that holds several bags. Needs repo.

  • Type: map of <bag>
  • Required: no

Other bags this bag calls but doesn’t absorb. A requirement gives you a registered bag, never its tools. The MCP server, the bag command group, and the Slack server check requirements when they load the bag: an unmet requirement drops the bag’s tools with one clear message instead of failing in the middle of a tool call. Use bags to absorb a bag and dependencies for host programs.

requires:
sessions:
reason: an event can name the session it came from
  • Type: object
  • Required: no

The required bag, by registered name.

  • Type: string
  • Required: no

Why this bag needs the other one. Barry quotes it in the unmet-requirement message.

  • Type: boolean
  • Required: no
  • Default: false

When true, an unmet requirement produces a warning and the bag loads anyway.

  • Type: string
  • Required: no

Which of the required bag’s services this bag calls. Omit it when this bag needs the other bag only to exist.

  • Type: boolean
  • Required: no

When true, a remote registry entry for the required bag satisfies the requirement.

  • Type: map of <package> or string
  • Required: no

Host programs the bag needs at run time. Barry looks each one up on PATH, reports a missing one in barry bag show and barry bag doctor, and prints its install command.

Instead of a map, the value can be a path inside the bag to a YAML file with the same map, or to a Brewfile of plain brew "name" and cask "name" lines.

dependencies:
ripgrep:
bin: rg
reason: powers the Glob and Grep tools
install:
macos: brew install ripgrep
  • Type: object
  • Required: no

One package, by its package-manager name.

  • Type: string
  • Required: yes

What the bag uses the program for.

  • Type: string
  • Required: no

The executable to look for, as a bare name or an absolute path, when it differs from the package name. A package key that contains / needs it.

  • Type: boolean
  • Required: no

When true, a missing program produces a warning instead of a failure.

  • Type: object
  • Required: no

The command that installs the program, per platform.

  • Type: string
  • Required: no

The install command on macOS.

  • Type: string
  • Required: no

The install command on Linux.

  • Type: list of objects
  • Required: no

macOS privacy permissions the bag’s code needs, granted by a person in System Settings under Privacy & Security. barry bag doctor uses this list to say what to grant, and to which program, before a tool fails on a silent denial.

permissions:
- kind: automation
app: Pages
reason: exports documents to PDF
  • Type: string
  • Required: yes
  • Values: automation, accessibility, screen-recording, full-disk-access

The permission.

  • Type: string
  • Required: no

The app the bag sends Apple events to. Required when kind is automation.

  • Type: string
  • Required: yes

What the bag needs the permission for.

  • Type: list of single-key maps
  • Required: no

Visibility overrides for tools the bag defines in code. Each entry maps one tool name to enabled, deferred, or disabled. A deferred tool stays out of the session’s tool list but remains callable and findable through tool search; a disabled tool leaves the bag. Overrides need a tools.ts or src/tools.ts in the bag, and each tool may appear once.

tools:
- search_instructions: deferred
- publish_instruction: disabled

Tool metadata itself belongs in the tool’s code. Only a manifest that a remote bag serves over MCP may carry a list of tool metadata objects here.

tools: []
  • Type: object
  • Required: no

A module that adds computed text to the system prompt of every session that loads the bag. Barry builds the entry, runs it in a separate process at session start, and skips it with a warning when it fails or runs over its time budget.

prompts:
entry: prompt.ts
  • Type: string
  • Required: yes

The module, relative to the bag directory. Its default export is a definePrompt contributor.

  • Type: map of <name>
  • Required: no

Named grants the bag publishes. Barry qualifies each one with the bag’s name, so a trait review in bag review-policy becomes review-policy:review. A trait may also carry restrictions through bounds or bound. Packing the bag on an identity syncs its traits; after you edit them, run barry bag sync-traits. See traits.

traits:
services:
description: Observe and restart this instance's services
bags: [mcp]
access: readwrite
  • Type: object
  • Required: no

One trait.

  • Type: string
  • Required: yes

What the trait grants.

  • Type: string
  • Required: no
  • Default: read
  • Values: read, readwrite, write

The tool access the trait grants. Barry reads write as readwrite.

  • Type: list of strings
  • Required: no

Other traits this trait includes. Barry expands them, and rejects a cycle or an unknown name.

  • Type: map of strings
  • Required: no

A trait to include per harness, keyed by harness id. Barry picks the entry for the running harness, and fails when that harness has no entry.

  • Type: map of string lists
  • Required: no

The harness’s built-in tools this trait grants, keyed by harness id. Native tools stay off until a trait grants them.

  • Type: list of strings
  • Required: no

The tool namespaces the trait grants. A bag’s tools carry its name as their namespace, so this usually lists bag names.

  • Type: list of strings
  • Required: no

The legacy spelling of bags. Barry reads bags first.

  • Type: list of strings
  • Required: no

Skill names, the directory names under the bag’s skills/, that the trait grants.

  • Type: list of strings
  • Required: no

Instruction names the trait points at. The text lives in files under an instructions/ directory, not in the manifest.

  • Type: list of strings
  • Required: no

Named bounds whose restrictions the trait carries: this bag’s own by bare name, or another bag’s as bag:name.

  • Type: object
  • Required: no

A restriction declared inline on the trait, in the shape that bounds describes. Barry validates it against the real bound schema, so a misspelled key fails instead of restricting nothing.

  • Type: map of <name>
  • Required: no

Named restrictions the bag defines. A trait refers to one in its bounds list; other bags refer to it as bag:name.

bounds:
no-bash:
description: Disable Barry shell tools
bound:
deniedTools: [Bash, BashOutput, KillShell]
  • Type: object
  • Required: no

One bound.

  • Type: string
  • Required: yes

What the bound restricts.

  • Type: object
  • Required: yes

The restriction itself: denied tools, file paths, shell programs, network access, and native tools.

  • Type: map of any value
  • Required: no

Named values that other keys reference as {constant: name}. Barry replaces the whole reference with the value before it validates the manifest, and leaves strings such as ${API_KEY} alone. To use another bag’s constant, write {constant: bag:name} and compose or require that bag.

constants:
shell-denials: [git, rm]
bounds:
safe-shell:
description: Keep shell commands read-only
bound:
bash:
denyPrograms: {constant: shell-denials}
  • Type: map of <ENV_VAR>
  • Required: no

For a harness bag: where each credential its sessions authenticate with comes from when the identity doesn’t define that variable itself. The identity’s own value wins. The value always comes from the identity’s vault, never from the manifest. See secrets.

credentials:
ANTHROPIC_API_KEY: { source: vault, item: anthropic-api-key, field: password }
  • Type: object
  • Required: no

One environment variable, in capitals, digits, and underscores.

  • Type: string
  • Required: yes
  • Values: vault

Where the value comes from.

  • Type: string
  • Required: yes

The vault item.

  • Type: string
  • Required: yes
  • Values: username, password, notes, uri

The field of the vault item.

  • Type: list of objects
  • Required: no

A catalog of model vendors and the models each serves. Barry reads the catalog as data and attaches call code separately; installing the bag makes its models available. Barry rejects a vendor listed twice, a model listed twice in one vendor, and two models of one kind that claim the same role. See adapters.

vendors:
- id: ollama
label: Ollama
baseUrl: { env: BARRY_OLLAMA_URL, fallback: "http://127.0.0.1:11434" }
credential: null
models:
- { id: qwen3:4b, label: Qwen3 4B, kind: completion, local: true }
  • Type: string
  • Required: yes

The vendor’s id.

  • Type: string
  • Required: yes

The vendor’s display name.

  • Type: string or object
  • Required: no

Where the vendor listens: a fixed URL, or an object with env (a variable that overrides the URL), fallback (the URL when the variable is unset), and path (appended to the URL). Omit it for a vendor Barry never calls directly.

  • Type: object or null
  • Required: yes

The key the vendor needs: {env, item, field} names the variable and an optional default vault item, {placeholder} sends a fixed non-secret value, and null means the vendor needs none.

  • Type: list of objects
  • Required: no

Other vendors’ APIs this vendor also serves. Each entry takes with (the other vendor’s id), baseUrl, and optional structuredOutput and egressSandbox.

  • Type: list of objects
  • Required: yes

At least one model. Each takes id, label, and kind (completion, embedding, or decision), plus optional roles (default, small), note, local, dimensions, and structuredOutput (native or prompted).

  • Type: map of <name>
  • Required: no

MCP servers this bag owns. One bag owns each server declaration; another bag reuses a server by composing its owner. See external bags.

mcp-servers:
github:
command: npx
args: [-y, "@modelcontextprotocol/server-github"]
env: [GITHUB_TOKEN]
  • Type: object
  • Required: no

One server. Declare url, command, or both.

  • Type: string
  • Required: no
  • Values: http

The transport for a url server.

  • Type: string
  • Required: no

The server’s URL.

  • Type: string
  • Required: no

The program that starts a local server.

  • Type: list of strings
  • Required: no

Arguments for command.

  • Type: list of strings
  • Required: no

Names of the secrets the server needs. Barry resolves each one from the identity and passes it to the server.

  • Type: boolean
  • Required: no

When true, every session gets its own connection instead of sharing one. Set it for a server that holds state between calls, such as a browser tab, since each session costs one server process.

  • Type: object
  • Required: no

A login command for a vendor whose own command-line tool runs the browser login. <identity> bag auth runs check first and stops when it exits 0, then runs command interactively. The credentials stay in the vendor tool’s own store.

auth:
command: temporal
args: [cloud, login]
check:
command: temporal
args: [cloud, namespace, list]
  • Type: string
  • Required: yes

The login program.

  • Type: list of strings
  • Required: no

Arguments for command.

  • Type: object
  • Required: no

A command that exits 0 when the login already holds. Takes command and args.

  • Type: object
  • Required: no

How the bag’s tools and scripts appear in the command-line tool.

cli:
alias: wx
  • Type: string
  • Required: no

The command group name to use instead of the bag’s name.

  • Type: object
  • Required: no

A notification tool this bag offers when the identity hasn’t configured one. An explicit notifier and the identity’s own setting both take precedence. The tool must be one this bag provides and must work without a target.

default_notifier:
tool: notify_desktop
  • Type: string
  • Required: yes

The tool to call.

  • Type: string
  • Required: no

A destination for the notification. Usually omitted, since a default that needs configuring isn’t a default.

  • Type: object
  • Required: no

Slack slash commands the bag provides, such as /oncall. The Slack app bag imports entry at startup and registers one route per command. These aren’t coding-agent slash commands, which come from actions.

slash-commands:
entry: src/slash-commands.ts
commands:
- name: oncall
description: Show who is on call
  • Type: string
  • Required: yes

The module that exports one handler per command name.

  • Type: list of objects
  • Required: yes

At least one command, each with a name and a description.

  • Type: object or list
  • Required: no

Scripts Barry runs when something happens. agent hooks fire on the coding agent’s lifecycle, and Barry maps the neutral event names onto each harness. barry hooks fire when Barry records an event. A bare list is a permanent second spelling of agent.

hooks:
agent:
- event: session-start
run: hooks/session-tracker/index.ts
args: [start]
barry:
- event: event-created
run: hooks/forward-event.ts
  • Type: list of objects
  • Required: no

Hooks on the agent’s lifecycle.

  • Type: string
  • Required: yes
  • Values: session-start, session-end, user-prompt, stop, pre-tool, post-tool

The lifecycle event.

  • Type: string
  • Required: yes

The script, relative to the bag directory.

  • Type: list of strings
  • Required: no

Arguments appended to the command, which often carry the hook’s mode.

  • Type: string
  • Required: no

A Claude tool-name matcher for pre-tool and post-tool. Omitted, the hook matches every tool.

  • Type: string
  • Required: no

A Cursor matcher, which matches by substring.

  • Type: list of strings
  • Required: no

Cursor event names that replace the default mapping for this hook.

  • Type: number
  • Required: no

Seconds each run may take.

  • Type: list of objects
  • Required: no

Hooks on Barry records. Each takes event (only event-created), run, args, and timeout.

  • Type: object
  • Required: no

A single SQLite store, named default. Use stores for more than one; declaring both fails. See services and stores.

store:
owned_by: identity
engine: sqlite
migrations: ./migrations/catalog
  • Type: string
  • Required: yes
  • Values: identity, node

identity gives each identity its own database; node gives the machine one database.

  • Type: string
  • Required: yes
  • Values: sqlite

The database engine.

  • Type: string
  • Required: yes

The directory of SQL migrations that builds the store’s schema.

  • Type: map of <name>
  • Required: no

The bag’s SQLite stores, keyed by lowercase name. Each entry takes the same owned_by, engine, and migrations keys as store.

stores:
events:
owned_by: identity
engine: sqlite
migrations: ./migrations/events
unscoped:
owned_by: node
engine: sqlite
migrations: ./migrations/events
  • Type: object
  • Required: no

One store. The name uses lowercase letters, digits, hyphens, and underscores, and starts with a letter or digit.

  • Type: string
  • Required: yes
  • Values: identity, node

identity gives each identity its own database; node gives the machine one database.

  • Type: string
  • Required: yes
  • Values: sqlite

The database engine.

  • Type: string
  • Required: yes

The directory of SQL migrations that builds the store’s schema.

  • Type: map of <store>
  • Required: no

Snapshot hooks for stores whose database refers to companion files. Each key names a declared store (default for store), and entry names a compiled JavaScript module inside the bag that exports captureSnapshot.

store:
owned_by: identity
engine: sqlite
migrations: ./migrations/catalog
snapshots:
default:
entry: ./dist/snapshot.js
  • Type: object
  • Required: no

The hook for one declared store.

  • Type: string
  • Required: yes

A .js or .mjs path inside the bag.

  • Type: map of <store>
  • Required: no

Restore hooks that prepare a store from a snapshot, used by barry db restore. Each key names a store, and entry names a compiled JavaScript module inside the bag that exports prepareRestore.

restores:
default:
entry: ./dist/restore-adapter.js
  • Type: object
  • Required: no

The hook for one declared store.

  • Type: string
  • Required: yes

A .js or .mjs path inside the bag.

  • Type: map of <name>
  • Required: no

Long-running processes the bag ships. A service runs continuously, and Barry restarts one that exits. The service manager assigns an HTTP service its port and passes it as PORT; where the service is reachable from is installation policy, not manifest data. Set exactly one of entry or command.

services:
web:
description: Serves the bag's dashboard
entry: src/server.ts
working-directory: .
serves: http
health: /health
secrets: [BARRY_SECRET]
  • Type: object
  • Required: no

One service.

  • Type: string
  • Required: yes

What the service does.

  • Type: string
  • Required: no

A JavaScript or TypeScript file inside the bag, run with Barry’s Node.

  • Type: string
  • Required: no

A program to run instead of an entry.

  • Type: list of strings
  • Required: no

Arguments for the entry or command.

  • Type: list of strings
  • Required: no

Non-secret KEY=VALUE literals. Put credentials in secrets.

  • Type: list of strings
  • Required: no

Names of the secrets the service needs.

  • Type: list of strings
  • Required: no

Secrets the service runs without when they’re absent.

  • Type: string
  • Required: no

The directory the service starts in, relative to the bag.

  • Type: string
  • Required: no
  • Default: login
  • Values: login, manual

login starts the service when you log in; manual leaves starting it to something else.

  • Type: string
  • Required: no
  • Values: http

The protocol the service speaks. Omit it for a service that serves nothing.

  • Type: string
  • Required: no

A readiness path, such as /health. Needs serves or socket.

  • Type: string
  • Required: no

A Unix socket to listen on instead of a port, relative to the service’s state directory for a user service and to the bag otherwise.

  • Type: string
  • Required: no

A macOS service account to run as, starting with an underscore, such as _barryvault. sudo barry setup --system creates the account and installs the service as a LaunchDaemon.

  • Type: boolean
  • Required: no
  • Values: true

Marks a service whose security assumes callers on this Mac. Barry refuses any tunnel route to it. Needs serves or socket.

  • Type: map of <name>
  • Required: no

macOS or iOS apps the bag builds and registers. Unlike a service, a person launches an app and may quit it. See apps.

apps:
menubar:
name: Weather
description: Menu bar forecast
platform: macos
build: build:app
output: dist/Weather.app
macos:
bundle-id: rocks.barry.weather
login-item: true
  • Type: object
  • Required: no

One app.

  • Type: string
  • Required: yes

The app’s display name.

  • Type: string
  • Required: yes

What the app does.

  • Type: string
  • Required: yes
  • Values: macos, ios

The platform. It decides whether macos or ios applies.

  • Type: string or object
  • Required: yes

A package script name, or an Xcode build as project (a .xcodeproj path), scheme, and configuration (default Release).

  • Type: string
  • Required: yes

The .app bundle the build produces, inside the bag.

  • Type: string
  • Required: no

The app’s id.

  • Type: number
  • Required: no
  • Values: 1, 2

The app protocol the app speaks: 1 for the legacy protocol, 2 for AG-UI.

  • Type: object
  • Required: no

Protocol capabilities, each a boolean: streaming, gating, listener, attachments, approvalReview, and replay. Needs protocol-version.

  • Type: object
  • Required: no

macOS registration: bundle-id, url-schemes, login-item, and keep-alive.

  • Type: object
  • Required: no

iOS registration: bundle-id, url-schemes, and device, which says whether the app can install onto a device.

  • Type: map of <name>
  • Required: no

Work the bag ships for Barry to run: on a schedule, from the SDK, or for a subagent. A job pairs a contract (what it needs, takes, and produces) with runs, which says what does the work. Job names are kebab-case. See background work.

jobs:
reaper:
description: Close sessions whose process is gone
runs:
using: node
main: src/reaper.ts
secrets: [BARRY_SECRET]
env:
- BARRY_REAPER_DRY_RUN=0
timeout: 10m
  • Type: object
  • Required: no

One job.

  • Type: string
  • Required: no

What the job does.

  • Type: string or number
  • Required: no

The job’s version. A versioned job runs on other machines as bag:name@version.

  • Type: object
  • Required: yes

What does the work. using decides which other keys apply.

  • Type: string
  • Required: yes
  • Values: node, process, action

node runs the JavaScript or TypeScript file in main with Barry’s Node. process runs command, a list of program and arguments, without a shell. action hands the work to an agent through action, this bag’s action or bag:name.

  • Type: list of strings
  • Required: no

For node and process: names of the secrets the process needs.

  • Type: list of strings
  • Required: no

For node and process: non-secret KEY=VALUE literals.

  • Type: string
  • Required: no

For node and process: the directory to run in, relative to the bag.

  • Type: object
  • Required: no

What must exist for the job to run: tools, secrets, bins, and labels, the worker labels a machine needs to take the job.

  • Type: object
  • Required: no

A probe Barry runs before the job: command must exit 0 within timeout seconds (60 at most), and hint tells a person how to fix a failure. A job whose probe fails doesn’t start.

  • Type: object
  • Required: no

Checks Barry makes when the job finishes: files_exist, command, and timeout in seconds (600 at most).

  • Type: object
  • Required: no

JSON Schema for the job’s inputs. Not for an action job, which takes it from the action.

  • Type: object
  • Required: no

JSON Schema for the job’s result. Not for an action job.

  • Type: number or string
  • Required: no

How long a run may take: seconds, or a number with s, m, h, or d.

  • Type: list of strings
  • Required: no

Named bounds the job’s process runs under. A bounded job must use node with a single .js or .mjs file, and can’t declare preflight or validate.

  • Type: map of <name>
  • Required: no

Work the scheduler runs on a trigger. Each schedule sets exactly one trigger (every, cron, or at) and exactly one payload (job, action, or entry). Schedule names are kebab-case. See schedules and workers.

schedules:
maintenance:
every: 1d
traits: [bash, artifacts]
job: maintenance
digest:
cron: "0 9 * * 1"
timezone: America/New_York
action: slack:weekly-digest
inputs: { channel: eng }
  • Type: object
  • Required: no

One schedule.

  • Type: string
  • Required: no

What the schedule does.

  • Type: string
  • Required: no
  • Values: 1m, 5m, 15m, 30m, 1h, 1d

A recurring trigger on the calendar: 5m fires at minutes 0, 5, 10, and so on.

  • Type: string
  • Required: no

A five-field cron expression.

  • Type: string
  • Required: no
  • Default: UTC

The IANA time zone for every or cron.

  • Type: string
  • Required: no

login to run once per login session, or an ISO 8601 time with an offset to run once.

  • Type: string
  • Required: no

A job to run: this bag’s by name, or bag:name, optionally with @version.

  • Type: string or object
  • Required: no

An action to run, by name or bag:name, or an inline action: the body of an action.yaml without name.

  • Type: string
  • Required: no

A script inside the bag to run directly. Only an entry schedule takes args, env, secrets, and timeout.

  • Type: list of strings
  • Required: no

Arguments for entry.

  • Type: list of strings
  • Required: no

Non-secret KEY=VALUE literals for entry.

  • Type: list of strings
  • Required: no

Secret names for entry.

  • Type: number or string
  • Required: no

How long an entry run may take: seconds, or a number with s, m, h, or d.

  • Type: string
  • Required: no

The identity an agent action runs as.

  • Type: object
  • Required: no

Inputs for the action, checked against its input schema when it runs.

  • Type: list of strings
  • Required: no

Traits the run gets.

  • Type: list of strings
  • Required: no

Bounds the run runs under.

  • Type: string
  • Required: no
  • Default: skip
  • Values: skip, queue

What to do when a fire arrives while the last run is still going.

  • Type: string
  • Required: no
  • Default: once
  • Values: none, once

Whether to run a fire that the machine missed while it slept or was off.

  • Type: boolean
  • Required: no
  • Default: true

Set it to false to keep the schedule from firing.

  • Type: map of <name>
  • Required: no

Commands the bag ships for a person to run on demand from the bag’s command group. Barry reads scripts from the manifest alone, so a script runs even when the bag’s tool code fails to load. Set exactly one of entry or command.

scripts:
audit:
description: Check an identity's secret references and bag credentials
command: barry
args: [identity, check]
  • Type: object
  • Required: no

One script.

  • Type: string
  • Required: yes

What the script does.

  • Type: string
  • Required: no

A JavaScript or TypeScript file inside the bag.

  • Type: string
  • Required: no

A program to run instead of an entry.

  • Type: list of strings
  • Required: no

Arguments for the entry or command.

  • Type: list of strings
  • Required: no

Non-secret KEY=VALUE literals.

  • Type: list of strings
  • Required: no

Names of the secrets the script needs.

  • Type: string
  • Required: no

The directory to run in, relative to the bag. Defaults to the bag directory.

  • Type: object
  • Required: no

A Terraform root the bag owns. barry bag infra runs Terraform there with the declared variables set, and barry bag check fails when the root doesn’t exist.

infra:
provider: terraform
root: infra
variables: [CLOUDFLARE_API_TOKEN]
  • Type: string
  • Required: yes
  • Values: terraform

The infrastructure tool.

  • Type: string
  • Required: yes

The Terraform root directory, relative to the bag.

  • Type: list of strings
  • Required: no

Environment variables Terraform needs.

  • Type: object
  • Required: no

For a hosting provider bag: what hosting it can do. Barry checks routes in the installation’s hosting policy against it and refuses a route the provider can’t honor, such as a public route with no gate. Name local, tunnel, or both.

provides:
local: {}
tunnel:
reach: [private, public]
gates: [access]
  • Type: object
  • Required: no

An empty object: the provider can give services readable names on this Mac.

  • Type: object
  • Required: no

The provider can carry routes in from elsewhere.

  • Type: list of strings
  • Required: yes
  • Values: private, public

Where routes can come from.

  • Type: list of strings
  • Required: no
  • Values: access

The gates the provider can put in front of a route.

Tools in src/tools.ts, a host program they call, and a trait that grants them.

name: weather
description: Current conditions and forecasts from the National Weather Service
dependencies:
jq:
reason: shapes forecast JSON for the summary tool
install:
macos: brew install jq
traits:
forecasts:
description: Read forecasts and current conditions
bags: [weather]
access: read

A bag with a service, a store, and background work

Section titled “A bag with a service, a store, and background work”

A dashboard service, an identity-owned store, and a nightly job.

name: reading-list
description: Saved articles with a reading dashboard and a nightly cleanup
requires:
sessions:
reason: links each saved article to the session that saved it
stores:
articles:
owned_by: identity
engine: sqlite
migrations: ./migrations/articles
services:
web:
description: Serves the reading dashboard
entry: src/server.ts
working-directory: .
serves: http
health: /health
secrets: [BARRY_SECRET]
jobs:
prune:
description: Archive articles read more than 30 days ago
runs:
using: node
main: src/prune.ts
secrets: [BARRY_SECRET]
timeout: 5m
schedules:
prune:
cron: "30 4 * * *"
timezone: UTC
traits: [bash, reading-list]
job: prune
scripts:
export:
description: Write every saved article to a Markdown file
command: npx
args: [tsx, scripts/export.ts]

Constants, named bounds, and traits that compose them, with no code at all.

name: review-policy
description: Reusable review restrictions
constants:
shell-denials: [git, rm]
bounds:
safe-shell:
description: Keep shell commands read-only
bound:
bash:
denyPrograms: {constant: shell-denials}
traits:
review:
description: Review restrictions
bounds: [safe-shell]
restricted:
description: Composed review policy
includes: [review]