Skip to main content

The cosmonicd Daemon

cosmonicd is a daemon that embeds the wasmCloud host. Cosmonic Desktop's graphical interface is a client; if the client is closed, workloads keep running, because the daemon is what's actually running them.

Overview

The cosmonicd daemon performs the following roles:

  • Reconciles: continuously compares your declared Workload specs (plain YAML under workloads/<namespace>/<name>.yaml in the state directory) against what's actually running, and converges—pulling, starting, stopping, and restarting with backoff. Permanent failures fail fast with the cause in the Logs view.
  • Pulls and verifies: fetches component images into a content-addressed OCI cache (browsable as the Local Registry), digest-pins them at apply time, and checks cosign signatures on every start.
  • Resolves configuration and secrets: merges config sources and resolves secret references (keychain, 1Password, AWS Secrets Manager, environment) at workload start—values live in memory, injected into the component, and are zeroized after use.
  • Serves ingress: one local HTTP listener (default 127.0.0.1:8200) routing to workloads by hostname.
  • Answers the API: JSON over a Unix socket (macOS/Linux) or per-user named pipe (Windows). No TCP listener and no tokens—the peer-checked socket is the trust boundary. The app, the tray, and the MCP server are all clients of this one API.

How it runs

The daemon is a per-user service, configured by the installer or the app's first run: a LaunchAgent on macOS, a systemd user service on Linux (with lingering on package installs, so workloads survive logout), and a login-started process on Windows. The install guides cover each platform's specifics, and Troubleshooting covers restarting it.

Daemon logs stream to the Logs view; on macOS they also land in ~/Library/Logs/Cosmonic/cosmonicd.log.

The cosmonicd CLI

The daemon binary is also a small CLI (where it lives per platform):

CommandWhat it does
cosmonicd runRun the daemon in the foreground (what the service invokes). --mcp also enables and auto-registers the MCP server at boot; --telemetry opts this run into crash reporting.
cosmonicd pathsPrint the resolved state directory, socket/pipe, and runtime locations.
cosmonicd oci export <image> <path>Export an image (and its cosign signature, when available) from the local cache as an OCI image-layout directory or .tar—the outbound half of air-gap transfer.
cosmonicd oci import <path>Import an OCI layout or .tar into the local cache, so workloads can start with no registry access.
cosmonicd mcp serveServe the Model Context Protocol over stdio—spawned by MCP clients, not run by hand.
cosmonicd uninstall [--purge]Deregister the daemon and tray login services and stop the tray; --purge also deletes the state directory. See Uninstall.

Environment variables

Set these on the daemon process (for a service, in the unit/agent definition; for ad-hoc runs, in the shell):

VariableEffect
COSMONIC_STATE_DIRRelocate the state directory (Workload specs, OCI cache, config stores).
COSMONIC_HTTP_ADDRBind the workload HTTP ingress elsewhere (default 127.0.0.1:8200).
COSMONIC_FLAG_<KEY> / COSMONIC_DRAFTPre-set feature flags (the toggles under Settings → Labs).
COSMONIC_TELEMETRY / COSMONIC_USAGEPre-decide the two telemetry consents and lock their toggles.

Headless use

Nothing about the daemon requires the app: it starts workloads from specs on disk, serves the same API over the socket, and can be driven by an agent over MCP or by scripts. Here's a complete headless session—isolated daemon, deploy, call—using only curl and jq:

# 1. Start a daemon with isolated state and a custom ingress port.
#    (Keep the state dir path short — the API socket lives inside it,
#    and Unix socket paths have a ~100-character limit.)
export COSMONIC_STATE_DIR=/tmp/cosmonic COSMONIC_HTTP_ADDR=127.0.0.1:8210
cosmonicd run &

# 2. Draft a Workload from an image reference — the same synthesis the
#    app's run flow performs, capability inference included.
curl -sS --unix-socket /tmp/cosmonic/cosmonicd.sock \
  -X POST http://localhost/v1/synthesize \
  -H 'content-type: application/json' \
  -d '{"source":"ghcr.io/cosmonic-labs/control-demos/hello-world:0.1.2"}' \
  | jq .workload > workload.json

# 3. Review workload.json (this is your approval step), then apply it.
curl -sS --unix-socket /tmp/cosmonic/cosmonicd.sock \
  -X POST http://localhost/v1/workloads \
  -H 'content-type: application/json' -d @workload.json

# 4. Watch it converge, then call it.
curl -sS --unix-socket /tmp/cosmonic/cosmonicd.sock http://localhost/v1/workloads \
  | jq '.[0].status.state'          # → "running"
curl -H 'Host: hello-world.localhost' http://127.0.0.1:8210/
# → Hello from Cosmonic Control!

Combined with cosmonicd oci import, the same pattern runs fully air-gapped: import the artifact bundle first and the apply starts with no registry access. A standalone daemon binary for server use is available on request—and when you outgrow one machine, the same Workload specs run under Cosmonic Control on Kubernetes.