Spec · v1

Satchel Format

A satchel is a folder you can read with any editor: one satchel.json manifest, Markdown for everything a person would want to read, and a checksums file. Packed, it is a single .satchel file.

Folder layout

harbor/
  satchel.json                 # manifest (required)
  identity/persona.md
  instructions/house-rules.md
  memory/stack.md
  skills/changelog-writer/SKILL.md
  skills/changelog-writer/scripts/collect_prs.sh
  checksums                    # "sha256  path" per entry

Tools, routines, permissions and model settings are small structured records, so they live in the manifest. Everything a human would read lives in its own Markdown file.

Manifest

{
  "$schema": "https://satchery.pro/schema/satchel-1.json",
  "format": "satchel",
  "formatVersion": 1,
  "agent": { "id": "<uuid>", "name": "Harbor", "slug": "harbor", "description": "…" },
  "origin": { "platform": "claude-code", "packedAt": "2026-11-15T00:00:00.000Z", "packedBy": "satchery 0.1.0" },
  "secrets": [ { "name": "GITHUB_TOKEN", "item": "tools/github", "where": "env.GITHUB_TOKEN" } ],
  "items": [ … ]
}
  • agent.id is a UUID that stays the same across moves, so the next platform can tell it is the same agent.
  • agent.slug (optional) uses lowercase letters, digits, dot, dash or underscore.
  • origin.platform names the platform the satchel was packed from.

Items

Every item has a category, an id (unique within its category), a name, and where it applies a file (a safe relative path) and a data object. Items can carry a source with the platform and path they were read from.

{ "id": "github", "category": "tools", "name": "github",
  "data": { "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"],
            "env": { "GITHUB_TOKEN": "{{secret:GITHUB_TOKEN}}" } },
  "source": { "platform": "claude-code", "path": ".mcp.json" } }

Category rules

  • identity, instructions, memory, skills need a file. Skills may list extra files (scripts, references) in data.files.
  • tools: data.transport is stdio (needs command), http or sse (need url).
  • routines: need data.prompt; data.schedule is a cron string. A routine that is not paused is paused on import.
  • permissions: data.mode is one of readonly, ask, auto, full; allow, ask and deny are lists of rule strings.
  • models: need data.model; data.provider is optional.

Secret references

Secret values are taken out before anything is packed. API keys, tokens, bearer headers, private keys, passwords in URLs, .env-style values and MCP env values are replaced by {{secret:NAME}}. The manifest lists the names and where each one was used, so you know what to reconnect. Names use capital letters, digits and underscores. Detection is pattern-based: it covers known key and token formats, secret-looking NAME=value lines, bearer headers, URL passwords and every MCP env value, so read the plan before you share a satchel.

The .satchel file

A .satchel is a gzip-compressed tar of the folder, including checksums: one line per entry with its sha256 and path, sorted by path. Packing the same satchel twice gives the same bytes.

Checksums make changes visible; they are not a signature. A .satchel is not encrypted and not signed, so only open satchels from people you trust.

Limits a reader enforces

  • Only regular files and folders. Symbolic and hard links are refused.
  • No absolute paths, no .., no backslashes, no duplicate entries.
  • At most 2,000 entries, 10 MB per file, 64 MB unpacked, 32 MB compressed.
  • Every entry must match its checksum, and every checksum must have its entry.

Validation

The inspector runs the same reader and validator as the CLI, in your browser. Errors stop a move; warnings (for example a routine that is not paused) are shown and handled.