Kommand discovers, schedules, runs, and observes small Automations on one Mac. An Automation can be driven by a conventional Python entry point or by Codex as an agentic orchestrator. Every Run gets a fresh Docker container; Settings, history, logs, State, and Markdown Deliverables remain ordinary local files. There is no database or always-running Kommand process.
- macOS
- Python 3.14+
- uv
- Docker Desktop, running locally
- For Codex-driven Automations, a host Codex CLI login created with
codex login
Create ~/Library/Application Support/kommand/kommand.toml and declare each
Automation as either a linked local path or a public GitHub repository:
config_version = 1
[[automations]]
path = "~/code/my-local-automation"
[[automations]]
url = "https://github.com/example/public-automation"uv sync
uv run kommand sync
uv run kommand validate
uv run kommand dashboardThe dashboard opens at http://127.0.0.1:8765/ and binds only to localhost. It
is the main interface for enabling Automations, editing Settings, starting and
stopping Runs, and reading history, output, progress, cost, and Deliverables.
Automation sources are declared in
~/Library/Application Support/kommand/kommand.toml. Run kommand sync after
editing that file. Local paths are linked in place; public GitHub repositories
are downloaded from their current default branch into Kommand-managed storage.
Discovery creates local Settings from each Automation's Manifest defaults, and
newly registered Automations start disabled. Enablement lives only in those
generated local Settings; it is not part of the source-controlled Manifest.
To run schedules in the background, install the five-minute macOS LaunchAgent:
uv run kommand scheduler enable
uv run kommand scheduler statusFailed scheduled Runs retry automatically after 5 minutes and then after 15 minutes. Each attempt uses the Automation's normal timeout. Manual Runs, stopped Runs, and Deliverable publication failures do not retry.
Use uv run kommand --help for the full CLI. The available operations are
init, sync, list, status, history, logs, validate, run, stop,
enable, disable, wipe, trash, tick, scheduler, and dashboard.
Use uv run kommand enable ID or uv run kommand disable ID to control
scheduled runs. Disabling does not stop an active Run or prevent manual Runs.
kommand run ID prints the Run outcome and, when one was published, the
absolute path to its Markdown Deliverable.
Use kommand status for a compact operational summary and kommand status ID
for one Automation. Use kommand history ID and
kommand logs ID --run RUN_ID for details. Add --json to any of these
commands for compact, structured output.
Each Automation source repository or linked directory contains:
manifest.toml identity, schedule, capabilities, and Settings defaults
prompt.md task instructions for a Codex-driven Automation (optional)
requirements.in human-edited Python dependencies
requirements.lock hashed, pip-compatible dependency lock
src/ Python driver or agent-callable tools
A Manifest declares the Automation's cron schedule, timezone, timeout, network
policy, runtime, secrets, mounts, and Settings defaults. The runtime defaults to
python; runtime = "codex" requires an outbound network and a relative
prompt file. Codex Automations default to model = "gpt-5.6-sol" and
reasoning_effort = "high"; either can be set explicitly in the Manifest.
timezone = "local" follows the Mac's IANA timezone and daylight-saving
changes.
For each Run, Kommand builds or reuses an image from the Automation source,
lockfile, and Kommand SDK, then starts a read-only container with one CPU,
512 MiB of memory, no Linux capabilities, and an ephemeral /tmp. Network,
secrets, and host folders are available only when declared in the Manifest.
State is the built-in writable persistent directory.
For a Codex Run, Kommand installs a pinned Codex CLI in the image and invokes
codex exec non-interactively. It copies only the host's existing
$CODEX_HOME/auth.json into a private Kommand credential directory and mounts
that directory into the container, so normal ChatGPT-backed CLI authentication
and token refresh continue without an API key or interactive container login.
It does not mount the rest of $CODEX_HOME or load host Codex configuration.
Local data lives under:
~/Library/Application Support/kommand/ source configuration, installed source,
Settings, history, State, Deliverables
~/Library/Logs/kommand/ Run output and scheduler logs
~/Library/Caches/kommand/ container build caches
kommand wipe ID moves an Automation's generated data to the macOS Trash while
leaving its source and .env secrets intact.
Create a portable Automation repository in a new or dotfiles-only directory:
uv run kommand init ~/code/my-automationThe directory name becomes the stable Automation ID by default; use --id when
the repository name and Automation ID should differ. The generated Automation
starts disabled with no network, secrets, or host mounts. Link it by adding its
path to kommand.toml, then synchronize:
config_version = 1
[[automations]]
path = "~/code/my-automation"uv run kommand syncPublic GitHub repositories use the same configuration with url in place of
path. GitHub sources are refreshed from their repository's current default
branch on each successful sync.
After changing requirements.in, regenerate its lockfile from the Automation
repository root:
uv pip compile requirements.in \
--output-file requirements.lock \
--python-version 3.14 --generate-hashesThe repository itself uses pyproject.toml plus uv.lock. Automation
lockfiles are separate because Docker builds install them with pip and verify
their hashes.
The bundled SDK provides Deliverables, progress, structured logs and metrics, monitoring reports, and OpenRouter usage telemetry. After a Run ends, Kommand's host runner sends a macOS notification for an operational failure, an active monitoring alert, or a successful Run that publishes a Deliverable:
from kommand.sdk import deliver, progress
progress(1, 2, "Collecting data")
deliver("# Result\n\nEverything completed successfully.")An Automation can report a condition that needs attention without making the Run fail:
from kommand.sdk import report_monitoring
report_monitoring(
"alert",
key="tomorrow.social-plan",
title="Tomorrow needs a social plan",
message="No qualifying social event was found.",
)Use a stable key for each monitor. Report healthy when the condition is clear:
report_monitoring("healthy", key="tomorrow.social-plan")The last report for each key in the Run is its final state. An operational failure stays separate from monitoring state. Kommand applies monitoring reports only when the Run succeeds. An active alert replaces the generic Deliverable notification, so one Run sends at most one host notification.
To make Codex the driver instead, provide a prompt and keep deterministic code as tools the agent may call:
id = "my-automation"
runtime = "codex"
prompt = "prompt.md"
model = "gpt-5.6-sol"
reasoning_effort = "high"
cron = "0 9 * * 1"
timezone = "local"
timeout = "15m"
network = "outbound"
secrets = []
[settings]The agent's final response becomes the Markdown Deliverable. The container is still the primary sandbox, so Codex runs without its inner approval sandbox and can call any packaged tool. Only trusted local Automation source should use this mode: a child process in the same container can technically read the mounted Codex credential file even though the runtime prompt explicitly forbids it.
A successful Run may publish one immutable Markdown Deliverable. Repeated
deliver() calls replace the staged document; only the final version is
published.
Copy .env.example to .env and add values there. An Automation receives only
the variables named in its Manifest, and Kommand redacts those values from
captured output and SDK telemetry. Treat every secret declaration and outbound
network permission as a security decision.
uv run pytest
uv run ruff check .
uvx pyright --pythonpath .venv/bin/python src tests
uv buildThe longer architecture tour is in docs/index.html.
