Skip to content

Native development (no Docker)

Editions: OSS, Cloud, Enterprise. Unless stated otherwise, everything on this page ships in OSS.

Use this when the stack runs on the machine (a Linux VM, a spare host, or Cursor Cloud) instead of docker compose up. Compose remains the default for laptops that have Docker.

Dockerfile.dev at the repo root installs the same system packages. Cursor Cloud builds it via .cursor/environment.json. Humans can build it and mount the checkout:

docker build -f Dockerfile.dev -t preloop-native-dev .
docker run --rm -it -v "$PWD:/workspace" -w /workspace \
  -p 8000:8000 -p 5173:5173 -p 5432:5432 -p 4222:4222 \
  preloop-native-dev

The image does not copy the app. Cursor checks out the commit; a local run must bind-mount the repo as above.

System packages

  • PostgreSQL 16 with pgvector
  • NATS Server with JetStream
  • Python 3.12 (python3-venv, libpq-dev, build-essential)
  • Node.js 22

On Ubuntu 24.04 those are what Dockerfile.dev installs. On another distro, install the same services and keep the ports below.

One-time app install

From the repo root:

./scripts/native-dev-install.sh

That creates .venv, installs the hash-pinned dev lock (.github/requirements/app-dev.txt), installs this checkout with pip install --no-deps -e ".[dev]", installs frontend from its lockfile with npm ci, and writes a gitignored .env if one is missing. Re-run after dependency changes; it is idempotent. npm ci replaces frontend/node_modules.

Start the stack

Each process in its own terminal (or tmux session):

Service Start command (from repo root) Port Notes
PostgreSQL + pgvector ./scripts/native-dev-deps.sh 5432 User postgres / password postgres, DB preloop. The script also starts NATS.
NATS (JetStream) (started by native-dev-deps.sh) 4222 (mon 8222) The API fails startup without NATS.
Backend API (role=all) source .venv/bin/activate && ./start.sh --init-test-data 8000 start.sh loads .env, runs scripts/init_db.py --force, then python -m preloop.server. Omit --init-test-data after the first boot.
Frontend (Vite) cd frontend && npm run dev -- --host 0.0.0.0 5173 Proxies /api, /mcp, /openai, /anthropic to :8000. Open the app here.

scripts/init_db.py also refreshes the global flow preset catalog on upgrades, without propagating template changes to account flows. If you apply migrations without running that initializer, run python scripts/sync_flow_presets.py --no-propagate before starting the service. The review publication requirement is a per-flow opt-in: an existing reviewer flow without its own git_clone_config.review_publication block is never gated by the stored catalog. Set review_publication.required: true on the flow once the API build that writes receipts is serving.

PostgreSQL data (schema, migrations, seeded rows) lives on the host. After the first boot you normally only restart processes, not reinstall or re-migrate.

Optional workers (preloop-sync scheduler, preloop-sync worker) are only needed for issue-tracker sync or flow execution, not for login, MCP, or gateway smoke tests. Post-deploy data backfills need both processes; if you skip them, run python -m preloop.models.backfills to complete pending backfills and enable readers such as the stored agent-usage totals.

.env

start.sh only exports variables that are not already set. A missing file is recreated by native-dev-install.sh with:

DATABASE_URL=postgresql+psycopg://postgres:postgres@localhost/preloop
NATS_URL=nats://localhost:4222
SECRET_KEY=dev-secret-not-for-production
PRELOOP_SERVICE_ROLE=all
PRELOOP_DISABLE_TELEMETRY=true
DEBUG=true
HOST=0.0.0.0
PORT=8000

PRELOOP_SERVICE_ROLE=all serves the API and the model gateway in one process. No separate gateway is required for local dev.

Gotchas

  • Liveness is GET /api/v1/health. API docs are /docs/api and ReDoc is /docs/redoc. Both pages load Swagger UI and ReDoc from the local /static mount, so a Content-Security-Policy can allow 'self' instead of a documentation CDN. ReDoc still requests one logo from cdn.redoc.ly and hides it if that request fails. There is no /health or /docs on the API port.
  • POST /api/v1/auth/register (and the UI form) require a username in addition to email / password.
  • With DEBUG=true, uvicorn hot-reloads on code changes. Restart the process after dependency changes.