Skip to content

Daemon

The APX daemon (apx-daemon) is the host layer of the system — a long-running local HTTP server on 127.0.0.1:7430 that owns everything that needs to persist across commands: project state, plugin lifecycle, the scheduler, conversation files, and the super-agent endpoint.

Every surface — apx CLI, TUI, the web admin panel, the MCP bridge, the Desktop window — talks to the daemon over HTTP. See Architecture for the full picture.

apx
$ apx daemon status
✓ daemon running
  pid        48213
  port       7430        http://127.0.0.1:7430
  version    1.38.0
  uptime     2h 14m
  pid file   ~/.apx/daemon.pid

plugins
  ● telegram   running   1 channel polling
  ● desktop    running   0 clients

• 3 projects loaded
apx daemon status — port, pid, uptime, plugins, projects

You never need to start the daemon manually. The first apx call that needs it spawns it in the background and waits a moment for it to become healthy. Subsequent calls reuse the running process.

The daemon is a singleton: a PID file at ~/.apx/daemon.pid prevents double starts. If the file exists but the process is dead, the next start cleans it up automatically.


Terminal window
apx daemon start # boot the daemon explicitly
apx daemon stop # send SIGTERM and wait
apx daemon reload # re-read ~/.apx/config.json only — does NOT restart the process
apx daemon status # print health: port, pid, uptime, plugins, projects
apx daemon logs # legacy stdout log tail
apx daemon logs --tail 200

apx daemon reload is the lightweight option: it picks up config changes (Telegram channels, model keys, permission mode) without dropping in-flight requests. Use apx daemon restart (a stop + start) when you change source code or prompts.


APX writes to two places:

PathWhatCommand
~/.apx/daemon.logLegacy stdout redirect (daemon process output)apx daemon logs --tail N
~/.apx/logs/apx.logUnified structured log — every module writes hereapx log

apx log is the recommended way to follow the system:

Terminal window
apx log # print the last 200 lines
apx log -f # follow (tail -f style)
apx log --tail 500
apx log --errors # only ERROR-level lines
apx
$ apx log -f
[2026-06-14 09:31:58.004] [INFO ] [daemon  ] apx-daemon 1.38.0 listening on http://127.0.0.1:7430
[2026-06-14 09:31:58.061] [INFO ] [telegram] plugin telegram initialized
[2026-06-14 09:31:58.088] [INFO ] [telegram] polling channel "default" (chat 123456789)
[2026-06-14 09:32:14.220] [INFO ] [scheduler] routine "morning-standup" due → exec_agent
[2026-06-14 09:32:16.733] [INFO ] [super-agent] roby routed 1 task to sofia
[2026-06-14 09:32:19.510] [WARN ] [engine  ] anthropic 429 — backing off 2s, retry 1/3
[2026-06-14 09:32:21.998] [INFO ] [engine  ] anthropic ok — 612 tok in / 248 out
apx log -f — live unified log stream from all modules

The unified log format is:

[2026-05-30 14:22:01.123] [INFO ] [telegram] plugin telegram initialized
[2026-05-30 14:22:01.456] [INFO ] [daemon ] apx-daemon 1.22.2 listening on http://127.0.0.1:7430

Secrets are scrubbed from every log line before it hits disk, in two layers:

  1. By key — structured metadata fields whose name looks secret (token, api_key, authorization, bot_token, …) are replaced with [redacted].
  2. By value — at boot (and again whenever the config changes) the daemon registers every known secret value: engine API keys, TTS/transcription/embeddings keys, Telegram bot tokens, and MCP env/header tokens. Any of those values appearing anywhere in a log line — a provider error that echoes your key, tool output, a stack trace — is replaced with a ***…XXXX marker (last 4 characters kept so you can tell which secret it was).

Both layers apply to ~/.apx/logs/apx.log and to the error traces in ~/.apx/logs/errors.jsonl. Masking is always on — there is no config key to disable it — and it never fails a log write: if masking itself errors, the line is written as-is rather than dropped.


On boot the daemon discovers two built-in plugins:

PluginWhat it does
telegramLong-poll Telegram bot gateway, inbound routing, contact roster
desktopWebSocket bridge for the Electron floating window

The PluginManager calls each plugin’s init() once, then start() after the HTTP server is listening. On SIGTERM / SIGINT it calls stop() on each plugin before closing the server.

Plugins can also install their own Express routes (routes(app) hook). The daemon registers these immediately after the core API routes, so plugin paths are available at the same base URL.

You can inspect plugin state at any time:

Terminal window
apx plugins list # all loaded plugins + running/stopped
apx plugins status telegram # detailed status for one plugin

The daemon runs a RoutineScheduler that ticks every 5 seconds and checks which routines are due. When a routine fires, the scheduler:

  1. Reads the routine definition from .apc/routines.json.
  2. Resolves the kind handler (heartbeat, exec_agent, super_agent, telegram, shell).
  3. Runs any configured pre_commands, evaluates the skip_prompt_on condition, runs the main action, then runs post_commands.
  4. Writes the updated last_run / next_run timestamps back to the routine store.

See Routines for the full spec and apx routine add flags.


Every agent conversation thread is stored as a Markdown file on disk — no database required for the core data, only for the regenerable index. The layout:

  • Directory~/.apx/
    • config.json
    • identity.json
    • daemon.pid
    • daemon.token
    • daemon.log
    • Directorylogs/
      • apx.log
      • errors.jsonl
    • Directorymessages/
      • Directorytelegram/
        • YYYY-MM-DD.jsonl
    • Directoryprojects/
      • Directory<project-id>/
        • Directorymessages/
        • Directoryagents/
          • Directory<slug>/
            • Directorysessions/ ← one .md per runtime invocation
            • Directoryconversations/ ← LLM conversation threads
          • Directorydefault/
            • Directorysessions/

The daemon reads ~/.apx/config.json on start. Key fields:

{
"port": 7430,
"host": "127.0.0.1",
"log_level": "info",
"super_agent": {
"enabled": true,
"model": "claude-3-5-sonnet",
"permission_mode": "automatico"
},
"telegram": { "enabled": true, "channels": [] },
"engines": {
"anthropic": { "api_key": "sk-ant-..." }
}
}

After editing config, run apx daemon reload (for config-only changes) or apx daemon restart (for code/prompt changes). The apx config set and apx model key commands write this file and call the reload endpoint automatically.