Skip to content

overview

github-actions[bot] edited this page Sep 19, 2026 · 12 revisions

How it fits together

devctl is one product with four faces on the same supervisor.

flowchart TB
  tui["TUI — OpenTUI screens and keys"]
  cli["CLI — start / stop / logs / llm / traffic / auth"]
  mcp["MCP — http://127.0.0.1:port/mcp"]
  web["Web — loopback explorer and control"]
  sup["Supervisor"]
  disk["~/.devctl/state/repoID/"]

  tui --> sup
  cli --> sup
  mcp --> sup
  web --> sup
  sup --> runtime["Host processes + containers"]
  sup --> proxy["Proxy + token endpoint"]
  sup --> logs["Log buffer"]
  sup --> disk
Loading

The same supervisor drives every surface — here the loopback web console showing services, profiles, live proxy requests, and recent errors together:

The devctl web console overview — service health, KPI tiles, profiles, proxy requests, and recent errors, all from one supervisor

Nothing in the application knows your services by name. The supervisor reads .devctl/, starts argv (or explicit shell) processes and optional Docker/Podman containers, injects resolved env, and reports health.

Supervisor

The supervisor is the long-lived process. It:

  • Starts, stops, and restarts host processes and optional Docker/Podman containers in dependency waves
  • Optionally starts the proxy and the MCP listener
  • Ingests stdout/stderr, health, auth, and proxy events into one log buffer
  • Persists session state under ~/.devctl/state/<repoID>/ (state.json, devctl.lock, rpc-token, and on Unix devctl.sock)

repoID is the first 16 hex characters of sha256(absolute repo root). Two checkouts get two state directories. A leftover ~/.devctl/sessions/<id>/ is migrated once.

devctl start always ensures a daemon and leaves it running after the command exits (--detach is deprecated and no longer changes that). devctl (no args) and devctl attach dial the session socket: devctl.sock on macOS/Linux, \\.\pipe\devctl-<repoID> on Windows. Attach never starts a supervisor; the default TUI may. devctl down stops the daemon (and, unless --keep-services, its services).

Override the home directory with DEVCTL_HOME (default ~/.devctl).

TUI

The TUI is an OpenTUI React app. It attaches to a supervisor and paints status, logs, identity, doctor, config, and settings. It does not own child processes. Closing the TUI can stop services or detach, depending on shutdown.stop_services_on_exit — see TUI.

Startup locates and attaches to an already-running daemon first, independent of whether the on-disk config still parses — an attached daemon's config_snapshot (its own last-known-good in-memory config) is always the effective config, never a local reparse. Local parsing only comes into play when no daemon is reachable: a valid config spawns a fresh one, a missing config opens setup, and anything else (invalid YAML, a schema violation) is a real boot error with nothing started. Once attached, a /reload (or an external edit picked up by the config-file watcher) refetches the snapshot on success; a failed reload leaves a banner up under the nav bar until the next one succeeds.

CLI

The CLI is the same controller over the same socket. Use it for scripts, CI, and one-shot status. See CLI.

MCP

Coding agents cannot keep a TUI child alive, so MCP is a localhost Streamable HTTP server on the supervisor. Default off. See MCP.

Web

An opt-in loopback console on the same supervisor (services, traces, logs, LLM inspector, graph). Off by default. devctl web start prints a URL with a session token (reused across listener restarts for 7 days); devctl status shows the listen address without it. See Telemetry.

Configuration vs preferences

What Where
Services, profiles, HTTP recipes, proxy, Google project .devctl/config.yaml and modular YAML
Machine overlay (gitignored) .devctl/config.local.yaml and ~/.devctl/config.local.yaml
Session overlay (optional, committable) .devctl/overlays/<name>.yaml via devctl start --overlay
TUI theme, keys, MCP listen, web appearance ~/.devctl/tui.json plus per-checkout ~/.devctl/state/<repoID>/tui.json (or DEVCTL_TUI_CONFIG)
Session / lock / socket ~/.devctl/state/<repoID>/
Persisted logs ~/.devctl/logs/
Log exports ~/.devctl/exports/
Credential files (if no keychain) ~/.devctl/credentials/ (mode 0600)

Typical loop

flowchart LR
  setup[".devctl/config.yaml<br/>or devctl setup"] --> validate["devctl config validate"]
  validate --> doctor["devctl doctor"]
  doctor --> run["devctl<br/>or devctl start"]
  run --> logs["Logs"]
  logs -.-> auth["Auth only if Google is required"]
Loading

Local-only services (the demo platform) run without gcloud.

Related

Clone this wiki locally