Single-machine private deployment (Local v0)
Run TCKDB privately on a single machine — laptop, dev workstation, or single-user host — using the same backend schema, services, authentication model, and upload APIs as every other TCKDB deployment. This is a deployment scenario, not a separate edition or mode of TCKDB. The "Local v0" name comes from the milestone that delivered the first documented form of this scenario; the page describes the scenario, not a fork. See the deployment guide overview for the wider taxonomy.
Same backend, not a lite fork. A single-machine private deployment does not sync to hosted, and it does not import or export contribution bundles. Local-to-hosted contribution is handled separately through contribution bundles.
Need a shared lab/group instance? See shared-private-deployment.md. Submitting from HPC? See client-access-from-hpc.md — HPC is a client environment, not a separate deployment. No Docker available? See native-advanced.md — native install is one infrastructure strategy among several. All of these reuse the same backend, schema, and auth model documented on this page.
The page below uses the Docker Compose quick-start as its infrastructure strategy: containers for PostgreSQL+RDKit and MinIO, plus the host-side conda env for the backend API and upload worker. This is a quick-start recipe for provisioning the application's dependencies — not the definition of a single-machine deployment. Other infrastructure strategies (managed PostgreSQL+RDKit, native install, and the deferred Apptainer packaging) serve the same scenario.
Prerequisites
- Docker and Docker Compose v2 (
docker compose version) - Miniforge/Conda with the
tckdb_envenvironment installed curlfor the smoke-test commands below
The host-side commands all use conda run -n tckdb_env ...; if your
shell already has the env activated you can drop the prefix.
Working directories
The repo is split into backend/ (Python/FastAPI) and frontend/
(Vite/TS). Two cwd contexts in this guide:
- Project root —
docker compose ...commands. The compose files live at the root. backend/— every other host-side command (alembic,uvicorn,python -m app.workers.upload_worker, anything underscripts/). Get there once withcd backendand stay there.
Each section below states which context it assumes.
1. Start the local stack
Run from the project root.
cp backend/.env.local.example backend/.env
docker compose up -d
This brings up:
| Service | Image | Purpose |
|---|---|---|
db |
informaticsmatters/rdkit-cartridge-debian |
PostgreSQL with the RDKit cartridge |
minio |
minio/minio |
S3-compatible artifact storage |
Both services run on network_mode: host so the host-side API reaches
them at 127.0.0.1:5432 and 127.0.0.1:9000 without overrides.
The backend API and upload worker are not containerized in Local v0 — see Future work for the rationale.
2. Initialize the schema
Run from
backend/.
cd backend
set -a; source .env; set +a
conda run -n tckdb_env alembic upgrade head
This applies the full ordered migration chain through the current head. The
baseline revision seeds reference data (for example, reaction_family), and
later revisions upgrade the deployed schema in place. Review the
migration runbook before
upgrading a database that already contains data.
3. Bootstrap the first admin
Run from
backend/.
backend/scripts/bootstrap_admin.py is idempotent: it creates a
new admin if no matching account exists, and promotes (and
reactivates) an existing one if it does. Run it once after the schema
is up.
conda run -n tckdb_env python scripts/bootstrap_admin.py \
--username admin \
--email admin@example.local \
--password "change-me" \
--full-name "Local Admin"
Avoid passing --password on a shared shell — use the env var instead:
TCKDB_BOOTSTRAP_PASSWORD='change-me' \
conda run -n tckdb_env python scripts/bootstrap_admin.py \
--username admin \
--email admin@example.local
Containerized usage. Once a backend image exists (see Future work) the same script will run as
docker compose exec api python scripts/bootstrap_admin.py .... For Local v0 the host-side command above is the supported path.
4. Start the backend API
Run from
backend/.
conda run -n tckdb_env uvicorn main:app \
--host 127.0.0.1 --port 8010 --reload
By default TCKDB_INLINE_WORKER=true (in backend/.env.local.example)
so the upload worker runs as a thread inside the API process. To run
it separately for isolation, set TCKDB_INLINE_WORKER=false and start
a second terminal (also in backend/):
conda run -n tckdb_env python -m app.workers.upload_worker
5. Verify the deployment (smoke test)
a. Health endpoint
curl -sf http://127.0.0.1:8010/api/v1/health
# {"status":"ok"}
b. Log in (or register, if open registration is enabled)
If you bootstrapped an admin in step 3, log in:
curl -sf -c cookies.txt \
-H "Content-Type: application/json" \
-X POST http://127.0.0.1:8010/api/v1/auth/login \
-d '{"username": "admin", "password": "change-me"}'
Or, with AUTH_ALLOW_OPEN_REGISTRATION=true, register a new user:
curl -sf -c cookies.txt \
-H "Content-Type: application/json" \
-X POST http://127.0.0.1:8010/api/v1/auth/register \
-d '{"username": "alice", "password": "change-me-too", "email": "alice@example.local"}'
See Single-user local vs shared lab-server
for when to flip AUTH_ALLOW_OPEN_REGISTRATION off.
c. Create an API key
API keys can only be minted with a session cookie — never with another API key.
curl -sf -b cookies.txt \
-H "Content-Type: application/json" \
-X POST http://127.0.0.1:8010/api/v1/auth/api-keys \
-d '{"label": "local-dev"}'
The response includes a one-time key field (e.g. tck_...). Save
it now — the plaintext value is never shown again.
export TCKDB_API_KEY='tck_...'
d. Confirm anonymous uploads are rejected
curl -i -X POST http://127.0.0.1:8010/api/v1/uploads/...
# HTTP/1.1 401 Unauthorized
e. Confirm API-key auth reaches the route
Any upload route accepts the X-API-Key header. The exact path and
payload depend on the workflow; using a generic placeholder:
curl -sf -X POST http://127.0.0.1:8010/api/v1/uploads/<workflow> \
-H "X-API-Key: $TCKDB_API_KEY" \
-H "Content-Type: application/json" \
--data @payload.json
A 200/202 (or schema-validation 4xx) response confirms the request
reached the upload route under your created_by attribution.
Single-machine private vs shared private deployment
The page above covers the single-machine scenario, where the API
binds to 127.0.0.1 and only the local user can reach it. Open
self-registration is acceptable and keeps the instance self-serve:
AUTH_ALLOW_OPEN_REGISTRATION=true
If the API is instead reachable on a lab network (for example
0.0.0.0:8000 behind a reverse proxy at
http://tckdb.lab.example.org), open registration becomes a soft
account-creation backdoor. Disable it:
AUTH_ALLOW_OPEN_REGISTRATION=false
…and seed every account explicitly with
backend/scripts/bootstrap_admin.py (run from backend/):
conda run -n tckdb_env python scripts/bootstrap_admin.py \
--username alice --email alice@lab.example.org
That posture — closed registration, per-user API keys, reverse proxy, backups, access control — is the shared private deployment scenario, documented in detail in shared-private-deployment.md. It is the same backend you bring up on this page; only operational policy changes.
Generic client targeting
Any TCKDB API client — script, notebook, lab pipeline, workflow tool, future CLI, future frontend — targets a deployment by configuring just two values:
base_urlapi_key
# Local
tckdb:
base_url: "http://localhost:8010/api/v1"
api_key: "tck_..."
# Shared private deployment (e.g. lab service node)
tckdb:
base_url: "https://tckdb.lab.example.org/api/v1"
api_key: "tck_..."
# Hosted
tckdb:
base_url: "https://tckdb.example.org/api/v1"
api_key: "tck_..."
The client behavior is identical across deployments — TCKDB is not tied to a particular workflow tool.
Stop and reset
Stop (preserve data)
docker compose down
Full reset (drop volumes — destroys local DB and artifacts)
docker compose down -v
You'll need to re-run the migration (step 2) and bootstrap admin (step 3) afterward.
Troubleshooting
alembic upgrade headcannot connect. Confirm the DB container is healthy:docker compose ps. The default port (5432) must be free on the host because the compose file usesnetwork_mode: host.- Port 5432 already in use. Either stop the existing Postgres
service or change
DB_PORTin.envand in your environment. The application readsDB_PORTfrom env. 401 Authentication requiredeven with a session cookie. Make sure the client is preserving cookies (-c cookies.txt -b cookies.txtfor curl). Sessions expire — re-login if needed.- API key works but upload returns 403. API keys inherit the user's role. Curators/admins are required for some endpoints — log in as a curator/admin before minting the key, or promote the user via the admin endpoints.
- MinIO bucket missing. The first artifact upload fails until the
bucket exists. Either create it via the MinIO console at
http://localhost:9001(login withS3_ACCESS_KEY/S3_SECRET_KEY) or usemc mb.
What Local v0 explicitly does not include
These are deliberately deferred — they are not bugs:
- Contribution bundle export
- Hosted bundle import
- Local-to-hosted push
- Raw database synchronization
- Lite/reduced schema
- A local-only backend fork
- Apptainer/Singularity images
- Native installer automation
- Frontend bundle UI
- Service accounts
- Production hardening (TLS, secrets management, hardened cookies, etc.)
Local v0 makes TCKDB runnable privately. It does not create a separate product, separate schema, or separate scientific validation path.
Future work
- Backend container packaging. Tracked as its own milestone. Local v0 deliberately runs the API and worker on the host because the repo has not yet standardized a Python dependency manifest. Containerizing the backend without that foundation is out of scope.
- Shared private deployment. Closed registration, reverse proxy, TLS, backups, access control on a lab service node. Documented in shared-private-deployment.md. Same backend; different operational policy.
- Client access from HPC. HPC jobs are TCKDB API clients, not a separate deployment. Patterns and Apptainer/Singularity status are in client-access-from-hpc.md. Apptainer image build files are deferred packaging.
- Native install (advanced infrastructure strategy). Documented in native-advanced.md; installer automation is still out of scope.
Reference
- Deployment guide overview — the full taxonomy
- Shared private deployment — same backend on a shared lab/group machine
- Client access from HPC — HPC jobs as TCKDB clients; Apptainer/Singularity is deferred packaging
- Native advanced install — infrastructure strategy when Docker/Apptainer aren't available