Desired-State Resources
How CompozyOS stores extensible runtime resources, validates mutations, and reconciles projected runtime state.
Resources are CompozyOS's desired-state layer for runtime objects that must be extensible and agent-manageable. Instead of letting every subsystem invent its own private storage shape, CompozyOS writes canonical resource records, validates their specs through registered codecs, and reconciles them into runtime state.
Use this page when you need to understand why a tool, MCP server, agent definition, package-owned agent sidecar, skill, automation job, trigger, window layout, or bridge instance can be managed through the same control plane.
Mental model
Rendering diagram…
| Term | Meaning |
|---|---|
| Kind | Resource family, such as tool, agent, or automation.job. |
| ID | Stable resource identity inside the kind. |
| Scope | global or workspace; workspace scope requires a workspace id. |
| Owner | Who owns the record, such as daemon control or an extension. |
| Source | Where the record came from, such as an extension, daemon, or session. |
| Version | Optimistic mutation version used to prevent blind overwrites. |
| Spec | Canonical JSON payload for the resource kind. |
Registered resource kinds
The daemon registers typed codecs for these resource families at boot:
| Kind | Projects into |
|---|---|
hook.binding | Hook registry bindings. |
tool | Tool Registry entries. |
mcp_server | MCP server declarations. |
agent | Agent catalog definitions. |
agent.soul | Package-owned SOUL.md defaults for resource-backed agents. |
agent.heartbeat | Package-owned HEARTBEAT.md defaults for resource-backed agents. |
skill | Skill catalog entries. |
automation.job | Scheduled or claimable automation jobs. |
automation.trigger | Automation trigger definitions. |
window_layout | Strict declarative desktop and tiled-group layout templates. |
bridge.instance | Bridge runtime instances. |
Write flow
PUT /api/resources/automation.job/nightly-resource-audit
Content-Type: application/json
{
"scope": { "kind": "global" },
"expected_version": 0,
"spec": {
"id": "nightly-resource-audit",
"name": "Nightly resource audit",
"target_kind": "agent",
"agent_name": "general",
"prompt": "Inspect desired-state resource health.",
"schedule": { "mode": "every", "interval": "24h" },
"enabled": false
}
}What happens:
- CompozyOS validates the path kind and id.
- CompozyOS validates the scope binding.
- If a codec is registered for that kind, CompozyOS canonicalizes the
spec. - The record is written with an owner, source, version, and timestamps.
- Reconcile is triggered for that kind.
Use expected_version = 0 when creating a new record. Use the returned version on update or
delete so stale operators and agents do not overwrite each other silently.
Kinds with a dedicated lifecycle service reject direct mutations with 403. Use the owning API or
CLI so its invariants remain coherent.
Read and filter
GET /api/resources
GET /api/resources/automation.job
GET /api/resources/automation.job/nightly-resource-audit
GET /api/resources/automation.job?scope_kind=workspace&scope_id=ws_projectFilters can narrow by kind, scope_kind, scope_id, owner_kind, owner_id, source_kind,
source_id, and limit.
Delete
DELETE /api/resources/automation.job/nightly-resource-audit
Content-Type: application/json
{ "expected_version": 3 }Deletes also trigger reconcile for the kind. Extension-owned records converge from the package's declared kit, so disable or update the owning extension instead of mutating those children directly.
Failure guide
| Symptom | Likely cause | First check |
|---|---|---|
400 on write | The request body is malformed JSON or cannot be decoded. | Response body and submitted payload. |
403 on write/delete | The resource kind has a dedicated lifecycle service. | Use the kind-specific API or CLI. |
422 on write | Invalid kind, scope binding, or registered-codec spec validation. | Response body and kind-specific docs. |
409 on update/delete | expected_version is stale. | GET /api/resources/:kind/:id for the current version. |
| Record exists but runtime did not change | Reconcile failed or is degraded. | Observe resource/reconcile health and daemon logs. |
| Projected record reappears | An extension or another declarative source owns it. | owner and source fields on the resource record. |
| Workspace write fails | scope.id does not name a valid workspace. | compozy workspace list -o json. |
Next
- Extensions explains how installed packages publish resource records.
- API Reference lists the current resource route families.