Developer Setup
QDash development starts from a local repository checkout and uses either DevContainer or Nix for the project toolchain.
Prerequisites
Required Tools
| Tool | Version | Description |
|---|---|---|
| Docker | - | Container virtualization platform |
| Docker Compose | v2.24+ | Management of multiple Docker containers |
| go-task | v3.41+ | Task runner for development commands |
| uv | - | Python package manager |
Optional Tools (for local development)
| Tool | Version | Description |
|---|---|---|
| Python | 3.11-3.12 | Backend development |
| Bun | 1.4.0+ | Frontend package manager and runtime |
| Node.js | 24.22.1+ (24.x) or 26.12.1+ | Next.js production runtime |
Clone the Repository
Clone QDash on the development host. Keep the checkout in a directory where you want to store other repositories too:
git clone https://github.com/oqtopus-team/qdash.git
cd qdashThe DevContainer bind-mounts the checkout at /workspace/qdash and, by default, its host parent directory at /workspace/repos. Git changes and commits in QDash are visible from either environment. With the default mount, clones made under /workspace/repos in the container appear beside QDash on the host; clones made beside QDash on the host appear under /workspace/repos in the container. For example, this creates a second checkout from the container:
git clone https://github.com/oqtopus-team/qdash.git /workspace/repos/qdash-copyUse /workspace/qdash for QDash development so its dependencies stay in the separate DevContainer volumes. The initial QDash clone must be made on the host because its Compose file is needed to start the DevContainer. If you want a different shared directory, create it on the host and export QDASH_SHARED_REPOS_DIR with its absolute path before starting the container. The directory must already exist and be writable by your host user. No repository manager is required. Setting the override only in .env does not pass it to the host-side preflight check.
A DevContainer can start without .env. Before running Qubex-backed calibration tasks, follow Operator Setup for .env and CONFIG_PATH configuration.
DevContainer
The recommended way to develop is using the DevContainer:
.devcontainer/check-shared-repos.sh "$PWD" && docker compose -f compose.devcontainer.yaml up -dThe check also runs automatically when opening the DevContainer through VS Code. It prevents a missing shared directory from being created by Compose versions that ignore create_host_path: false.
The DevContainer can start without a local .env; Docker Compose uses .env when present. The checked-in VS Code configuration runs its remote session as the vscode user so files created in the checkout remain writable from the host. Docker Compose direct sessions can still use root when needed.
On Linux, build the vscode user with the host UID and GID so files created in the checkout and shared repository directory remain writable from both the host and the container. VS Code's updateRemoteUserUID handles this for Dev Containers opened through the extension; when starting Compose directly, pass the IDs explicitly:
.devcontainer/check-shared-repos.sh "$PWD" && \
LOCAL_UID=$(id -u) LOCAL_GID=$(id -g) docker compose -f compose.devcontainer.yaml up -d --buildThe container mounts /var/run/docker.sock so devcontainer users can run the local Docker Compose tasks from inside the workspace. The Docker daemon resolves bind mounts on the host, so on start the container links the checkout's host path (QDASH_HOST_WORKSPACE, set by VS Code from ${localWorkspaceFolder} or by compose.devcontainer.yaml from $PWD) to /workspace/qdash; run docker compose from that host path inside the container, never from /workspace/qdash directly. In the default VS Code session, tools under /home/vscode/.local and agent configuration under /home/vscode/.claude and /home/vscode/.codex are persisted in Docker volumes, so they survive container rebuilds. Codex itself is installed under /opt/codex, which remains writable by the vscode user for CLI updates without being hidden by the persisted .local volume. Host-side deployment commands keep their Python environment outside the checkout, so they do not reuse root-owned DevContainer artifacts.
Use the same task commands from the host or DevContainer. Both sessions edit the same source checkout and use the same Docker daemon. Run Git and development commands as the vscode user in the DevContainer; reserve root for container setup. The container sets umask 022 so new files are readable in both environments. Git credentials and global identity are per-user; set user.name and user.email in both environments, and authenticate Git separately in each environment. A repository-local Git setting is shared because .git/config is in the bind mount.
The DevContainer keeps its Python .venv in a Docker volume mounted at the usual workspace path. The host retains its own .venv, so running uv sync or uv run in one environment does not replace packages used by the other. Ruff, mypy, pytest, and coverage caches are also kept inside the container. ui/node_modules and ui/.next also use DevContainer volumes, so its Next.js output does not replace host output. Recreate the DevContainer after changing these mounts; its first uv sync installs dependencies into the new volume.
Then attach to the container using VS Code's DevContainer extension or:
docker compose -f compose.devcontainer.yaml exec --user vscode devcontainer zshCheck the Git identity inside the container before committing because host-level Git settings are not copied into the DevContainer automatically:
git config --global user.name
git config --global user.emailSet them in the container if either command is empty.
Nix Host Shell
Nix can provide the local CLI toolchain without starting the DevContainer. This is useful when you want to run Python tests, UI checks, or Docker Compose tasks from the host shell while keeping the service stack in Docker.
Install Nix with flakes enabled, then enter the development shell:
nix developThe shell provides Python 3.11, uv, Bun, Node.js 24, go-task, Docker CLI/Compose, jq, PostgreSQL client tools, and the secret scanning tools used by the project. It also sets UV_PYTHON to the Nix-provided Python 3.11 so uv sync does not accidentally select Python 3.12 on macOS, where some workflow backend dependencies may fail to build. It does not start MongoDB, PostgreSQL, Prefect, API, or UI services by itself; use the existing Docker Compose tasks for those services.
After entering the Nix shell for the first time, install project dependencies:
task dev-local-setupThen start the lightweight development stack:
task dev-localThis starts MongoDB, PostgreSQL, Prefect, the deployment service, and the user flow worker with Docker Compose, then runs the API and UI on the host. The UI is available at http://localhost:5714.
Stop the host API/UI processes and Docker Compose services:
task dev-local-downRefresh Dependencies
The DevContainer includes Lychee and installs Python, frontend, and Lefthook dependencies during creation. To refresh dependencies and install the local documentation checker manually, run:
task dev-local-setupThe setup task installs Lychee in $HOME/.local/bin when the active environment does not already provide the pinned version. Keep that directory on PATH when running host-side tasks outside the DevContainer or Nix shell.
Run the Development Stack
task dev-localThis starts the supporting services in Docker Compose and runs the API and UI directly on the host. Use this flow when editing backend or frontend code frequently.
The component tasks are:
task dev-services: start MongoDB, PostgreSQL, Prefect, deployment-service, and user-flow-workertask dev-api-local: run the FastAPI app on the host against Docker servicestask dev-ui-local: run the Next.js app on the host against the local API
Access Points
| Service | URL |
|---|---|
| QDash UI | http://localhost:5714 |
| API Documentation | http://localhost:5715/docs |
| Prefect Dashboard | http://localhost:4200 |
| MongoDB Admin | http://localhost:8081 |
Use Developer Commands for linting, tests, builds, generation, and documentation tasks. Use Operator Setup when you need the full Compose deployment rather than the host-side development stack.
Secret Scanning Tools
The DevContainer and the Nix host shell provide Betterleaks, Trufflehog, and Lefthook. Both pin Betterleaks to BETTERLEAKS_VERSION in .devcontainer/Dockerfile, and every leak scan rejects any other release; see scanner version. A Betterleaks installed by Homebrew or from the latest GitHub release is usually newer and fails that check, so on the host run checks and commits through nix develop.
Enable the git hooks from the Nix shell or the DevContainer:
lefthook installThe pre-commit hook requires the pinned Betterleaks release. Commit from the Nix shell (
nix develop -c git commit) or the DevContainer; with a missing or different release, commits fail closed instead of bypassing the staged leak scan.