> ## Documentation Index
> Fetch the complete documentation index at: https://druks-codex-registry-contract-follow-up.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Develop Druks

> Set up the repository, understand its architecture, change the database, and run verification.

This guide is for changes to Druks itself. The backend and Vite development
server operate on the host. Compose supplies isolated Postgres and Redis.

## Set up

From the repository root:

```bash theme={null}
docker compose -f deploy/compose.dev.yaml up -d
uv sync --locked --dev
cp druks.toml.example druks.toml
cp .env.example .env
python3 -c 'import base64, os; print(base64.b64encode(os.urandom(32)).decode())'
```

Paste the generated value into `secrets.secrets_key` in `druks.toml`, then
initialize the development database:

```bash theme={null}
uv run druks init-db
```

Settings reads `./druks.toml` from the current directory. The example uses
`[identity].mode = "none"`. The loopback dashboard has no authentication and
exactly one operator account. Your first provider connection creates this
account.

To use `header` mode with the development server, set
`identity.mode = "header"` in `druks.toml`. Set `identity.header` in the same
file. Then send the header with a browser add-on or
`curl -H 'X-Edge-Email: you@example.com'`.

The dev Compose project creates two databases:

* **Development:** `druks_dev` for the host-run server.
* **Tests:** `druks_test` for pytest. The suite rebuilds this schema.

`.env.example` points the server at `druks_dev`. The suite reaches
`druks_test` and Redis index 15 through `DRUKS_TEST_DATABASE_URL` and
`DRUKS_TEST_REDIS_URL`. It does not use the server settings. Thus, the two
databases cannot be confused.

Start the backend:

```bash theme={null}
uv run uvicorn druks.api.server:app --host 127.0.0.1 --port 8001
```

In another terminal:

```bash theme={null}
npm --prefix frontend ci
npm --prefix frontend run dev
```

Vite proxies API traffic to the backend. The production backend image instead
contains the built SPA and serves it from FastAPI.

## Architecture map

| Path                                                      | Responsibility                                                      |
| --------------------------------------------------------- | ------------------------------------------------------------------- |
| `backend/druks/workflows.py`                              | Public workflow, step, gate, scheduling, and start API              |
| `backend/druks/agents.py`                                 | Public agent descriptor and output contract                         |
| `backend/druks/durable/`                                  | DBOS integration, run projection, lifecycle internals               |
| `backend/druks/apps/`                                     | Entry-point loading, discovery, author settings                     |
| `backend/druks/ui/`                                       | Page declarations, the block/value/field catalog, the page API      |
| `backend/druks/events/`, `signals.py`                     | Event log, feed, and reactions                                      |
| `backend/druks/webhooks/`                                 | Authenticated delivery framework and deduplication                  |
| `backend/druks/harnesses/`                                | Harness invocation, authentication, usage, and capability manifests |
| `backend/druks/sandbox/`                                  | Drukbox lifecycle, SSH execution, workspace delivery                |
| `backend/druks/api/`                                      | FastAPI composition and platform routes                             |
| `backend/druks/{mcp,skills,notifications,user_settings}/` | Shared operator services                                            |
| `backend/druks/contrib/software_factory/`                 | Bundled reference app, not framework core                           |
| `frontend/src/`                                           | Shared dashboard shell and bundled app UI                           |
| `frontend/src/druksui/`                                   | The renderer for an app's Python pages                              |
| `backend/migrations/`                                     | Core/bundled schema history                                         |
| `deploy/`, `scripts/`                                     | Images, Compose, Caddy, setup, and deployment                       |

The API process embeds DBOS and executes workflows. App modules register
capabilities during boot, after DBOS initialization and before launch.

## App test surface

The main package registers bundled apps through `pyproject.toml`. CI also
installs `backend/tests/druks-field_notes` as a real editable distribution and
runs the proof-app tests. Those tests are the executable contract for:

* Headless and boot-time entry-point loading
* Role-module discovery
* Route and subject read-side mounting
* Independent migrations and table-prefix enforcement
* Workflow start, settings, and feed formatting.

If you change the author API, update the scaffold, proof app, author guide, and
tests together.

## Database changes

Core and bundled historical tables use the core Alembic history:

```bash theme={null}
uv run alembic -c backend/alembic.ini revision --autogenerate -m "describe change"
uv run druks init-db
```

For an independently packaged app:

```bash theme={null}
uv run druks makemigrations <app-name> -m "describe change"
uv run druks init-db
```

The app owns its migration directory and version table. Review every
autogenerated revision before applying it.

An encrypted column seals each value with `<table>.<column>` as the AAD. A
migration that renames that table or column must also decrypt every row under
the old name and re-encrypt it under the new one, or every stored value stops
decrypting. `e3a9c7d1b5f4_providers_own_logins.py` shows the step.

## Verification

Backend checks:

```bash theme={null}
uv pip install -e backend/tests/druks-field_notes   # once per environment
uv run ruff check backend
uv run ruff format --check backend
uv run pytest backend/
```

The suite builds its subjects from `field_notes`, the proof app. This standalone
distribution depends on Druks. It installs like an author app, not as a Druks
dependency. Install it one time for the full suite.

The pull-request backend
workflow does the same. Pyright is available for local and editor use. It is not
a CI gate.

Frontend checks:

```bash theme={null}
npm --prefix frontend run lint
npm --prefix frontend test
npm --prefix frontend run build
```

The frontend CI workflow runs those three commands on Node 22.

For documentation-only changes, also run these commands:

```bash theme={null}
git diff --check
cd docs
mint validate
mint broken-links --check-anchors --check-redirects
```

Mintlify builds `docs/` directly. Its GitHub App owns deployments and
pull-request previews. The repository does not require a documentation-specific
GitHub Actions workflow.

## Working with sandboxes

Backend tests mock most provider boundaries. For a real local sandbox, run
Drukbox on the host from its own checkout
(`DOCKER_SSH_USERNAME=druks make dev` in
[czpython/drukbox](https://github.com/czpython/drukbox)) and set:

```toml theme={null}
[sandbox]
service_url = "http://127.0.0.1:8000"
service_token = "dev-token"
image = "ghcr.io/czpython/druks/sandbox:latest"
```

`uv run druks doctor --sandbox` creates a real host. If you require a real
sandbox test, run this command. It is not part of the normal test suite.

## Frontend ownership

An app's screens are Python. It declares them in `pages.py`, and the shell
renders them through `frontend/src/druksui/`. An installed wheel therefore
adds pages without touching the JavaScript bundle. The
[Druks UI contract](druks-ui.md) is the one description of what those pages
carry; change it first, then the renderer.

Two escape hatches remain, and both are for an app that needs full control of
its interface. An app can ship a standalone static frontend in its package's
`dist/`, served at `/app/<name>`. React code that joins the bundled dashboard
shell must already be in the SPA and register through
`frontend/src/apps/index.ts`; a wheel cannot put routes into that existing
JavaScript bundle.

See the [frontend guide](https://github.com/czpython/druks/blob/main/frontend/README.md)
before adding dashboard pages.
