Quickstart

This guide covers the fastest way to get Heartlock running, either locally for development or in production via Docker Compose.

Prerequisites

Component

Version

Python

3.14+

Node.js

22

Go

1.22+ (CLI only)

Docker

24+ with Compose v2

Local development

The dev stack uses SQLite by default and Django’s built-in runserver.

  1. Clone and configure:

    git clone https://yena.dev/Jouleworks/heartlock.git
    cd heartlock
    cp .env.example .env
    

    Edit .env and adjust values (it is git-ignored). When DATABASE_URL is unset, SQLite (db.sqlite3) is used. When HEARTLOCK_ENCRYPTION_KEY is unset in DEBUG=True, one is generated and persisted to heartlock_data/secret.key.

  2. Set up the Python environment:

    python3 -m venv .venv
    make install
    make migrate
    make superuser
    make seed
    
    • make install resolves pip dependencies into .venv

    • make migrate runs Django migrations

    • make superuser creates an admin account (optional)

    • make seed populates demo data (optional)

  3. Start the backend:

    make run
    

    The server is reachable at http://localhost:8082.

  4. Start the frontend dev server (optional):

    The Angular dev server proxies /api and /oidc to the backend on port 8082 (see ui/proxy.conf.json):

    make ui-install
    make ui-serve
    

    The UI is now at http://localhost:4200.

Docker Compose (production)

The Compose file runs PostgreSQL 17 + the backend image.

  1. Generate an encryption key:

    Warning

    The command below writes the key to your shell history. For production use, consider passing the key via a file or Docker secret instead.

    export HEARTLOCK_ENCRYPTION_KEY=$(python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
    
  2. Start the stack:

    docker compose up --build
    

    The backend is reachable at http://localhost:8082. The image runs as a non-root user and ships a HEALTHCHECK against GET /api/v1/health/.

  3. Common commands:

    docker compose up -d          # start detached
    docker compose logs -f backend  # tail logs
    docker compose down            # stop (keeps the DB volume)
    docker compose down -v          # stop and DELETE the DB volume
    

    Caution

    docker compose down -v permanently deletes the database volume and all stored data. Back up your database before using this command.

Note

This Compose setup terminates no TLS. It sets SECURE_SSL_REDIRECT=False so Django does not force HTTPS. Behind a TLS-terminating proxy, remove that override and let the proxy send X-Forwarded-Proto.

First-run onboarding

When Heartlock starts with no administrator account, it boots into a guided first-run setup wizard. Open the instance in your browser and the UI redirects you automatically. The wizard has three steps:

  1. Configure — set the public Base URL, choose the database (SQLite file path, or a PostgreSQL host/port/db/credentials), and optionally provide a Fernet encryption key (generate one with the Generate button, or leave it blank to have one created for you).

  2. Restart — the database connection and encryption key are loaded at startup, so Heartlock restarts to apply them. Migrations run automatically against the new database on the next boot.

    Caution

    Back up your database before a restart that triggers migrations. A failed migration can leave the database in an inconsistent state. If you supplied your own key it

    is shown once for you to save; copy it — it cannot be recovered and is required to decrypt your secrets.

  3. Admin — create the first administrator account directly against the now-active database, then sign in.

Everything the wizard collects is persisted to heartlock_data/appconfig.json. The Docker Compose stack mounts a named volume there so your configuration survives container recreation. For the settings it manages, the config file takes precedence over DATABASE_URL / HEARTLOCK_APP_URL / environment defaults — i.e. what you configure in the UI wins.

Opting out of onboarding

Deployments bootstrapped entirely through environment variables or OIDC SSO that never want a local bootstrap admin can set HEARTLOCK_DISABLE_ONBOARDING=true. Existing instances that already have a superuser are never prompted.

Building the CLI

The Go CLI authenticates via the browser, fetches project secrets, and injects them into child processes.

cd cli
make build
# produces ./cli/bin/heartlock
./cli/bin/heartlock login --server http://localhost:8082
./cli/bin/heartlock whoami

See CLI Configuration for the full command reference and configuration details.