Install Extensions
Install extensions from curated, GitHub, git, or local sources; understand trust and consent; update, enable, disable, remove, and inspect provenance.
Extensions are installable packages that add static resources or runtime behavior to CompozyOS. An extension can ship skills, agent definitions with authored-context sidecars, automation, layouts, hooks, loops, MCP servers, memory backends, model sources, and subprocess tools.
CompozyOS stores installed extensions in the global registry and copies managed installs into
<COMPOZY_HOME>/extensions/<name>.
Install sources
compozy extension install <source> takes one closed source union. The CLI infers the source from
the argument:
| Argument form | Source | Notes |
|---|---|---|
./dir, ../dir, /abs/dir | local_path | A directory containing extension.toml, extension.json, or plugin.json. |
github:owner/repo[@ref] | github | GitHub release asset. |
git:https://host/owner/repo[@ref] | git | Shallow clone at the ref. Needs the git executable on the daemon host. |
owner/repo[@ref] | curated | Tries the curated catalog first, falling back to github only on a 404. |
compozy extension install ./hello
compozy extension install github:acme/hello@v0.1.0
compozy extension install git:https://github.com/acme/hello@v0.1.0
compozy extension install acme/linear-bridgeA path that does not exist fails naming that path instead of degrading into a slug lookup, and a git URL carrying embedded credentials is rejected before any request. HTTP, UDS, and native-tool callers submit the structured union directly; the shorthand is a CLI convenience.
--version and --asset select a specific registry version or archive for published sources.
Detected package layouts
CompozyOS detects the package layout after acquiring the source. You do not select a format flag.
| Root files | Detected format | Result |
|---|---|---|
extension.toml or extension.json | compozy | The native manifest owns identity, resources, permissions, and runtime behavior. |
plugin.json only | agent-plugin | CompozyOS reads Agent Plugins schema 1.0.0 and synthesizes a resource-only manifest. |
Native manifest and plugin.json | compozy | The native manifest wins. Install records an informational diagnostic about the unused file. |
An Agent Plugins package may provide immediate child directories under skills/ and MCP servers in
mcp.json. CompozyOS maps stdio and streamable-http servers, records unsupported components as
skipped diagnostics, and keeps the rest of the package usable. Client-specific layouts are rejected
instead of being guessed.
compozy extension install ./acme-tools --allow-unverified --yes
compozy extension install git:https://github.com/acme/acme-tools@v1.2.0 \
--allow-unverified --yesBoth managed and dev instances receive absolute PLUGIN_ROOT and PLUGIN_DATA paths. Managed data
is stable across updates at <COMPOZY_HOME>/extension-data/<name>/; a dev instance is isolated at
<COMPOZY_HOME>/extension-data/<name>@ws-<workspace-id>/. See
Agent Plugins interoperability for the full mapping.
Trust and consent
Two things gate a published install: an operator policy and a per-request consent.
| Install | Policy needed | Consent needed |
|---|---|---|
Curated entry with an official or community tier | none | none |
Curated unverified tier, github, git, local_path | extensions.trust.allow_unverified (default true) | --allow-unverified |
| Any of the above in machine-readable output | same | plus --yes |
Human output prompts on --allow-unverified unless you pass --yes. Structured output requires
--yes, so an agent cannot hide an interactive trust decision.
Deterministic failures:
| Reason code | Meaning | Fix |
|---|---|---|
extension_unverified_policy_blocked | Live policy is off. Evidence path /settings/extensions. | Review the source, then compozy config set extensions.trust.allow_unverified true. |
extension_checksum_unverified | Policy is on but the request carried no consent. | Add --allow-unverified (and --yes for structured output). |
extension_archive_digest_mismatch | A curated archive differs from the catalog-pinned bytes. | Stop. No --allow-unverified bypass exists for this. |
extension_git_unavailable | No git executable on the daemon host. | Install git or use another source. |
extension_git_version_unsupported | The daemon host has Git older than 2.37. | Install Git 2.37 or newer, then retry. |
extension_agent_plugin_client_layout | The package uses a client-specific plugin layout. | Repackage it with root plugin.json, skills/, and optional mcp.json. |
extension_agent_plugin_schema_unsupported | plugin.json declares a schema other than 1.0.0. | Publish an Agent Plugins 1.0.0 package. |
extension_agent_plugin_not_manifest | plugin.json is not a valid Agent Plugins manifest. | Replace the file with a valid object containing the required identity. |
extension_agent_plugin_manifest_invalid | The portable manifest failed semantic validation. | Fix the named field and validate the package again. |
Both consent failures are 422.
Curated installs download the feed-owned artifact, verify the catalog-pinned SHA-256 before extraction, and persist the catalog entry, archive digest, and extracted-tree checksum separately. Any digest failure aborts before the registry write, so no partial install survives.
A GitHub release may carry an <asset>.sha256 sidecar. When one exists the daemon verifies the
archive against it and records digest_matched. That is an integrity fact only: it never raises
registry_tier above unverified, never sets checksum_verified, and never removes the consent
requirement.
Registry tier and digest verification are provenance signals, not safety guarantees.
Permission ceilings
Published installs (curated, github, git) run under the marketplace tier, whose Host API grants
are limited to logs.read, memory.read, observe.read, session.read, skills.read, and
tool.read. A published extension may declare more; anything outside the ceiling is dropped at grant
time with a recorded diagnostic instead of silently succeeding.
Local-path installs and dev links carry no ceiling. Full detail: Extension Permissions.
Managed installs do not allow runtime dependency symlinks to escape the extracted extension root. If
a packaged node_modules or runtime dependency symlink resolves outside that root, installation
fails instead of copying content from the host filesystem.
Configure sources and trust policy
[extensions.trust]
# Operator policy for non-curated installs. Per-install consent still applies.
allow_unverified = true
[extensions.sources.github]
enabled = true
base_url = "https://api.github.com"
[extensions.sources.git]
enabled = true
[marketplace.catalog]
base_url = "https://raw.githubusercontent.com/compozy/compozy/main/catalog"
ttl = "1h"
timeout = "10s"base_url accepts https or http; http logs an insecure-scheme warning. The GitHub client reads
GITHUB_TOKEN from the daemon environment for authenticated release access and higher rate limits.
After changing policy, reconcile and inspect the daemon's apply record:
compozy config reload -o json
compozy config apply-history --limit 5 -o jsonFull key reference: config.toml.
Search across sources
compozy extension search notes --sources curated,github --limit 20
compozy extension search notes --cursor <cursor>Search fans out across the curated catalog and GitHub. A rate-limited or unreachable source degrades
to cached-or-omitted results with a sources_degraded marker rather than failing the whole query.
compozy marketplace search <query> --kind extension -o json returns the same catalog projection
with installed state and trust fields.
List, inspect, and update
compozy extension list
compozy extension status hello
compozy extension provenance hellolist uses the daemon when it is running and falls back to the local registry when it is offline. It
carries an Update column driven by the update_available projection, so a pending update is
visible without asking for it. status and provenance require the daemon.
Both read the global installed set. A workspace-scoped dev instance is not part of it — read those
through GET /api/extensions?workspace=<id>, through an agent caller whose session binds the
workspace, or through compozy extension logs <name>. See
Develop Extensions.
status returns registry and runtime truth together:
| Field | Meaning |
|---|---|
name, version, source, format | Registry identity, install source, and detected compozy or agent-plugin format. |
type | resource when no subprocess is required; subprocess otherwise. |
enabled | Registry flag. Disabled extensions are skipped by the manager. |
state | disabled, enabled, registered, active, or error. |
health | unknown, healthy, or unhealthy. |
capabilities, permissions | Manifest-declared provide surfaces and Host API methods. |
consecutive_failures, restart_backoff_ms | Crash-loop honesty: a looping extension is distinguishable from a stably-failed one. |
dev, overrides_published, origin_path, generation_hash, workspace_id | Dev-overlay identity when one is active. |
update_available, remote_version | Passive update projection. |
requires_env, missing_env, bound_env_keys | Declared names, currently missing names, and names bound through Vault. |
network_requirement_digest, network_confirmation_required | The exact Live requirement digest and whether lifecycle consent is pending. |
provenance, trust | Install source, checksums, registry tier, allow_unverified, installer identity, decision, diagnostics. |
diagnostics | Deterministically ordered ingest and runtime issues; portable component skips remain visible here. |
Apply updates:
compozy extension update hello
compozy extension update --allUpdate targets extensions installed from a source with remote version metadata; local installs and
dev links are not update targets. A successful update replaces the managed directory, updates the
registry row and provenance, reloads runtime resources, and writes an extension.updated event.
A batch update stops at the first failing target without discarding earlier progress: the
response is 200 and carries every completed item plus the failed one, whose status is failed
with extension_update_failed. Targets after the failure are not attempted.
The runtime activation is the commit point. If the new version is active but CompozyOS cannot remove a
staging directory or backup, the result is still status: "updated" with an
extension_update_cleanup_failed warning naming the residual path.
Inspect and preview the kit
Use inventory to compare every resource shipped by the global installed extension with the records currently live in the daemon:
compozy extension inventory hello -o jsonPortable inventory preserves the detected format and ingest diagnostics even when every MCP
component was skipped. An empty live resource set is therefore distinguishable from a native empty
extension.
Use preview before enable to calculate the same desired state without writing records, starting automation, launching a subprocess, or recording Network consent:
compozy extension preview hello -o jsonPreview reports the publish set, agent-name conflicts, unbound environment names, automation that would start, and the current Network requirement digest. Resolve conflicts and bindings before the enable mutation.
Enable, disable, remove
compozy extension disable hello
compozy extension enable hello
compozy extension remove hello
compozy extension remove hello --globalEnable and disable require the daemon. Enable publishes the complete static kit, starts effectively
enabled package automation, loads runtime behavior, and writes an extension.enabled event. Its
result lists the automation definitions that became runnable. Disable removes the extension-owned
live resources, stops runtime behavior, and writes extension.disabled.
When the manifest requires Live Network participation, an unconfirmed enable returns
extension_network_confirmation_required with the current digest. Review it, then retry with
--confirm-network-requirement <digest>. An update that changes the digest refuses before replacing
the installed package and uses the same exact-digest confirmation flag.
Inside a workspace, remove unlinks a dev overlay and lets the published installation resume;
--global removes the published installation. Removal deletes the managed install directory and
registry row with rollback handling. Post-commit cleanup failure leaves status: "removed" with an
extension_remove_cleanup_failed warning and a residual path — treat that path as cleanup debt, not
as a live extension.
For an Agent Plugins installation, removal also deletes PLUGIN_DATA. If direct deletion fails,
CompozyOS first renames the directory into the extension-data quarantine and reports the cleanup
warning. If the quarantine rename also fails, removal fails and the extension remains installed.
Validate an Agent Plugins package
Validation detects the same format and component mapping as install without writing registry state or starting code:
compozy extension validate ./acme-tools -o jsonThe result includes format: "agent-plugin", would_ingest, and positioned issues. Use it before a
local or git install to catch an unsupported schema or client-specific layout. Unsupported portable
components appear as skipped issues rather than hiding the resources that can still be ingested.
For a valid package with two skills, two usable servers, and three skipped components, the shipped CLI reports:
Extension bundle validation
===========================
Status: valid
Format: agent plugin
Package: acme.tools 1.2.0
Issues: 3
Would ingest
============
Kind Name Transport
---- ---- ---------
skill release
skill review
mcp_server local stdio
mcp_server remote streamable-http
WARN mcp:legacy-events: sse transport is not supported
WARN mcp:malformed: invalid mcp server entry
WARN skill:skipped: name must match the directory "skipped"Declare required environment
Extensions declare the environment variable names they need:
[extension]
name = "daytona"
version = "1.2.3"
min_compozy_version = "0.3.0-beta.1"
requires_env = ["DAYTONA_API_KEY", "DAYTONA_ORGANIZATION_ID"]CompozyOS validates requires_env at install and status load. Diagnostics report names, never values.
An unset or empty variable surfaces through missing_env in list, status, the API, and the
settings page.
Bind required values through the instance-scoped Vault flow described in Extension secret bindings. Bindings survive an update of the same managed instance, while stale bindings remain visible but are never injected when the new manifest stops declaring that name.
Agent surfaces
Extension lifecycle is agent-manageable. The compozy__extensions toolset reaches the same services
as the CLI:
| Action | Tool | CLI |
|---|---|---|
| Scaffold | compozy__extensions_init | compozy extension init |
| Build | compozy__extensions_build | compozy extension build |
| Validate | compozy__extensions_validate | compozy extension validate |
| Dev-link / reload | compozy__extensions_dev/_reload | compozy extension dev/reload |
| Read logs | compozy__extensions_logs | compozy extension logs |
| Search | compozy__extensions_search | compozy extension search |
| List installed | compozy__extensions_list | compozy extension list |
| Inspect one | compozy__extensions_info | compozy extension status |
| Inspect provenance | compozy__extensions_provenance | compozy extension provenance |
| Inspect kit | compozy__extensions_inventory | compozy extension inventory |
| Preview enable | compozy__extensions_preview | compozy extension preview |
| Install | compozy__extensions_install | compozy extension install |
| Update | compozy__extensions_update | compozy extension update |
| Remove | compozy__extensions_remove | compozy extension remove |
| Enable / disable | compozy__extensions_enable/_disable | compozy extension enable/disable |
| Publish | compozy__extensions_publish | compozy extension publish |
Tools that execute author or extension code (build, dev, reload) are interaction-gated;
publish is additionally open_world; validate is read-only. Deterministic denials include
EXTENSION_SOURCE_FORBIDDEN, EXTENSION_APPROVAL_REQUIRED, EXTENSION_NOT_INSTALLED, and
EXTENSION_VALIDATION_FAILED. Credentials never appear in tool inputs: the publish token is resolved
server-side and registered for redaction.
Native dev and reload never build. Call compozy__extensions_build first and pass its
generation_hash.
Extension-owned desired-state records are inspectable through compozy__resources_list,
compozy__resources_info, and compozy__resources_snapshot. An extension declares the corresponding
Host API methods in permissions.requires, such as resources/list, resources/get, and
resources/snapshot.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
extension_unverified_policy_blocked | Live policy is off. | Review the source, then set extensions.trust.allow_unverified and reload. |
extension_checksum_unverified | Consent missing on the request. | Add --allow-unverified (--yes for structured output). |
extension_archive_digest_mismatch | Curated archive differs from the pinned bytes. | Stop; refresh the catalog or wait for the entry to be corrected. |
extension_git_unavailable | No git binary on the daemon host. | Install git or install from another source. |
extension_git_version_unsupported | The daemon host has Git older than 2.37. | Install Git 2.37 or newer, then retry. |
extension_agent_plugin_client_layout | The package uses a client-specific plugin layout. | Repackage it with the portable root layout. |
extension_agent_plugin_schema_unsupported | The package declares an unsupported Agent Plugins schema. | Publish a package using schema 1.0.0. |
Extension is enabled but not active | Daemon offline, disabled, or activation failed. | compozy extension status <name> and compozy extension logs <name>. |
| Enable asks for Network confirmation | The extension declares an unconfirmed or changed Live requirement digest. | Review the digest, then retry with --confirm-network-requirement. |
bridge.adapter install rejected | External bridge authoring is a planned follow-up. | See Develop Extensions. |
| Daemon-required error on a marketplace verb | Search, install, update, remove, enable, status, and provenance are daemon-owned. | Start the daemon and retry. |
Related references: