Bundle lifecycle
Author with the bundle builder, ship, re-apply, test, and the full schema reference.
How a bundle goes from authored to applied to re-applied across a fleet. Plus the full schema.
Bundle builder
In-browser form for authoring bundles without hand-writing JSON. No data leaves the browser. Available in the EasyCC docs site under Bundle builder.
Fill the sections, see the JSON preview update on the right, download bootstrap.json when ready.
Tips:
- Start with a built-in template. Three templates cover the common shapes (MSP one-agent, AI consulting multi-agent, internal IT lockdown).
- Import an existing bundle to round-trip edits — drag your
bootstrap.jsoninto the import area. - Secrets stay as keychain references. The builder never accepts raw values; you push values via your MDM separately.
- Toggle policy kind per field. Every policy field has Unset / Recommend / Enforce — pick the right level.
Re-apply and idempotency
When EasyCC sees a bootstrap.json, the apply step is idempotent: applying the same bundle twice does no extra work. A marker file <config_dir>/EasyCC/bundle-applied.json records what was applied:
{
"bundleId": "acme-easycc-v1",
"schemaVersion": 1,
"sectionsApplied": {
"branding": true,
"provider": true,
"agents": { "acme-marketing": true, "acme-ops": true }
},
"appliedAt": "2026-05-11T18:32:14Z"
}Same bundleId: no-op
EasyCC reads the marker, sees a match, skips apply. Quiet, no log noise, no user disruption.
Different bundleId: re-apply smartly
Each section has its own re-apply rule:
| Section | Re-apply behavior |
|---|---|
branding, policy | Always overwrite — cheap, no user state to preserve |
provider | Overwrite if values differ; user credentials preserved |
agents[] | New id → scaffold. Removed id → leave existing agent alone (never auto-delete user work). Same id in both → update only differing details |
agents[].displayName | Rename is safe — user's bookmarks and chats stay |
agents[].relativePath | Don't move existing folders — preserves the user's filesystem layout |
agents[].claudeMd | Overwrite only if hash matches previous shipped version (user hasn't edited) |
agents[].skills[] | Per-content-hash. New skills add, updated skills overwrite only if user hasn't edited |
agents[].mcpServers[] | Keyed by (agentId, mcpName). Disabled servers stay disabled |
userSkills[], userMcpServers[] | Content-hashed; new entries add; updates only if unmodified by user |
Partial failures recover: if one section fails (a missing secret, a malformed skill), the marker records which sections succeeded. The next launch retries only the failed sections.
Force re-apply by deleting bundle-applied.json and relaunching.
Things re-apply will never undo: agents the user created, skills the user wrote, chats, user settings overrides. The user's work is always preserved.
Logging: search the EasyCC log for [bundle] entries — per-section updates, new/unchanged counts, timestamps.
Testing your bundle
Roughly a 10-minute loop. Get this clean once and you can iterate on bundles confidently.
Build the bundle
If your skills/CLAUDE.md live as source files, run your build script to produce bootstrap.json. Otherwise, edit by hand or via the bundle builder.
Drop the bundle to the platform sidecar
# Windows
Copy-Item bootstrap.json 'C:\ProgramData\EasyCC\bootstrap.json'# macOS
sudo cp bootstrap.json '/Library/Application Support/EasyCC/bootstrap.json'
sudo chmod 644 '/Library/Application Support/EasyCC/bootstrap.json'Push every referenced secret to the keychain
For each secret_ref in the bundle, push the corresponding value:
cmdkey /add:com.easycc.app /user:auth_claude_api_key /pass:<value>security add-generic-password -s com.easycc.app -a auth_claude_api_key -w '<value>' -U -A /Library/Keychains/System.keychainUninstall any prior EasyCC, reinstall, launch with --silent-first-run
A clean slate makes failures obvious. Run with --silent-first-run to skip the onboarding wizard — the bundle's onboarding.skipSageOnboarding: true should also handle this, but the flag belt-and-suspenders.
Verify in the UI
Title bar shows the org name. Agent roster has the pinned agent at the top. Skills panel shows your bundled skills. Settings → Integrations shows the right MCP transport badges. Settings → Advanced shows the provider correctly (and locked if you set it enforced). Start a chat and confirm Claude responds.
Check the marker file
// <config_dir>/EasyCC/bundle-applied.json
{
"bundleId": "acme-easycc-v1",
"sectionsApplied": { /* every section true */ }
}Every section should show true. Anything false → that section failed; check the log.
Test re-apply
After confirming v1 works:
- Bump
bundleIdtoacme-easycc-v2and change something (new skill, updatedCLAUDE.md, different provider). - Replace the sidecar file.
- Restart EasyCC.
- Verify the marker updated, new sections applied, and user state was preserved — chats still there, edited skills not overwritten, disabled MCP servers still disabled.
Production checklist
- JSON parses with
jq - Every
secret_refreferences a keychain slot your MDM will push - Test apply matches expected first-launch behavior
- Test re-apply preserves user work
- Log shows no
unknown fieldwarnings - Bundled skills run end-to-end
- Bundled MCP servers connect successfully
- Branding renders correctly in the title bar and welcome screen
Full schema
The top-level fields:
| Field | Type | Notes |
|---|---|---|
schemaVersion | integer | Required. Always 1 for v1. |
bundleId | string | Required in production. Stable identifier; same ID → no re-apply work. |
issuedAt | ISO-8601 string | Optional. When the bundle was built. |
issuer | object | Optional. { name, url, contactEmail }. |
branding | object | orgName, welcomeMessage, supportUrl, supportEmail. |
policy | object | Managed-policy fields as { "kind": "enforced" | "default", "value": ... }. See Policy fields reference. |
provider | object | type plus type-specific fields. customGateway requires baseUrl + apiKeySecretRef. |
agents[] | array | id, displayName, createMode, relativePath, claudeMd, pinned, skills[], mcpServers[], files[]. |
userSkills[] | array | name, content, approved. |
userMcpServers[] | array | name, transport, URL/command, auth. |
onboarding | object | skipSageOnboarding, firstLaunchView (command-center | project | sage), requiredFields (apply fails if any are missing). |
Forward compatibility: unknown top-level keys log a warning but don't fail apply. A bundle authored for a newer EasyCC version will still apply the fields the running version understands.
The auto-generated schema at Reference → Bundle schema is regenerated from EasyCC's source on every build, so it never drifts from the actual implementation.
v2 roadmap: bundle signing, bundle_url remote fetch, additional secret_ref schemes (env:, oauth:, provision:, prompt:), and SVG/logo branding.
How is this guide?