Codumentor logo Codumentor

Per-Repo Dev Servers

Codumentor can launch, track, and stop long-running dev servers for the
repos it knows about — your Django/Node/etc. project, or Codumentor
itself during dogfooding. One declarative config, one CLI verb
(codumentor dev), one shared registry the admin panel lists and
operates on.

This page is for users editing their Codumentor config and developers
wiring .codumentor/ into their repo. The launcher is single-host and
single-user; multi-host scheduling is explicitly out of scope.

When to use this

If your dev server is short-lived (one command, one terminal, you
Ctrl-C when done), you don't need this — just keep using your existing
workflow.

Configuration

Add a dev_server block under the existing per-repo project_setup
entry (or the top-level project_setup if you only have one repo):

project_setup:
  setup_script: .codumentor/setup.sh   # optional; already supported
  watch:                                # optional; already supported
    - requirements.txt
  dev_server:
    command: "python -m myapp serve --port ${PORT}"
    health_path: "/health"             # optional; GET expected to 2xx
    open_url: "http://127.0.0.1:${PORT}/"
    ready_timeout_seconds: 30          # optional; default 30
    env:                                # optional extra env for the child
      MYAPP_CONFIG: dev.yaml

Field reference

FieldRequiredMeaning
commandyesShell-style command line. ${PORT} and any other ${VAR} references are substituted at spawn time. The child inherits the parent env plus PORT and the entries under env:.
health_pathnoIf set, the launcher polls GET http://127.0.0.1:${PORT}<health_path> until it returns 2xx (or the timeout elapses). Without it, TCP-accept is the only ready signal.
open_urlnoThe URL the admin panel links to. Defaults to http://127.0.0.1:${PORT}/. Templated the same way as command.
ready_timeout_secondsnoHow long to wait for the server to become healthy before treating the launch as failed (default 30).
envnoExtra environment variables passed to the child process. Values are templated.

${VAR} templating

The launcher uses Codumentor's regular ${VAR} substitution. It runs
twice:

  1. Load time: real env vars like ${HOME} are resolved when the config file is read. ${PORT} is left literal because it isn't set in the parent environment yet.
  2. Spawn time: a second pass over the dev_server block with PORT=<allocated> injected. command, open_url, and the values under env: are templated then.

So you can freely mix ${HOME}, ${VIRTUAL_ENV}, and ${PORT} in any
of those fields and they'll Just Work.

Port allocation

The launcher binds to :0, reads back the kernel-assigned port, and
closes before spawning. Cheap, no range exhaustion. Ports are not
stable across restarts — every launch gets a fresh port and a fresh
instance id.

CLI: codumentor dev

Four verbs. All of them respect the same --repo NAME semantics as
codumentor setup. If --repo is omitted, the launcher infers the repo
from your current working directory.

codumentor dev [--repo NAME] [--no-wait]
    Allocate a port, ensure project_setup is current, spawn
    dev_server.command, register the instance, wait for the
    healthcheck. --no-wait skips the wait but still registers.

codumentor dev status [--json]
    List running instances. Reconciles dead PIDs out first.
    --json prints a machine-parseable payload that includes
    orphan processes.

codumentor dev stop [--repo NAME | --id ID | --all]
    SIGTERM the matched instances, wait, SIGKILL on timeout,
    drop the registry entries. One of the three selectors is
    required — there is no implicit default.

codumentor dev logs [--repo NAME | --id ID] [-f/--follow] [-n N]
    Print or tail the log file an instance writes to. With -f,
    streams new lines until interrupted or the server exits. -n
    sets how many trailing lines to seed; -n 0 dumps the whole
    file.

Logs and where they live

Stdout and stderr from the spawned server go to
~/.codumentor/dev-logs/<instance-id>.log. The file is append-only;
the launcher never rotates or truncates it. Each launch gets a fresh
id (and so a fresh file), so the on-disk footprint grows by one file
per launch.

codumentor dev logs -f is the usual way to look at output:

$ codumentor dev logs -f
==> /home/me/.codumentor/dev-logs/codumentor-main-58231-a1b2c3.log  [id=codumentor-main-58231-a1b2c3, repo=Codumentor, pid=12345, port=58231]  (following — Ctrl-C to stop)
INFO:     Started server process [12345]
INFO:     Uvicorn running on http://127.0.0.1:58231 (Press CTRL+C to quit)
…

The follow loop exits cleanly when the server's PID disappears — so a
logs -f against a crashed server returns instead of hanging.

Selecting an instance

When you only have one preview running, the CLI uses it. When you have
more than one, pick with either --repo NAME (any unique repo) or
--id ID (use dev status to list ids). The error message lists the
valid options.

The admin-panel widget

The admin page renders a "Dev-Server Previews" panel that lists running
instances with:

Below the table there are two optional sections:

Limits, soft caps, and resource pressure

Each preview is a full server process — venv, sqlite, whatever else
your app pulls in. The launcher emits a soft warning to stderr if you
go above 3 running instances. There's no hard limit; CI and scripted
callers are never blocked, just reminded.

Selfdev (Codumentor's own dev_server)

codumentor-selfdev.yaml ships with a working dev_server block so
you can dogfood the launcher on Codumentor itself:

project_setup:
  dev_server:
    command: "${VIRTUAL_ENV}/bin/codumentor -c codumentor-selfdev.yaml api --port ${PORT}"
    health_path: "/health"
    open_url: "http://127.0.0.1:${PORT}/"

scripts/setup-worktree.sh --dev builds a worktree (its own venv + UI
build) and execs codumentor dev against it. Combined with the admin
panel, you get one row per worktree, clickable URLs, and a single
place to stop them when you're done.

Troubleshooting

SymptomLikely cause
No repo … has a dev_server.commandThe config in use has no dev_server block, or you passed --repo for a repo that doesn't have one.
Health check fails after 30sIncrease ready_timeout_seconds, or check codumentor dev logs — the process may be failing to bind.
dev status shows a dead instanceA reconcile pass on the next CLI invocation drops it; you can force one with codumentor dev status itself.
Orphan process in dev statusA registry was deleted (or moved by changing CODUMENTOR_DEV_REGISTRY) while a child kept running. Kill the listed PID by hand — the plugin won't.

See also