Local Development Setup
June 17, 2026 ยท View on GitHub
This repository contains the API, processor, shared Python code, frontend, Supabase migrations, and the deadtrees development CLI.
Prerequisites
- Docker Desktop or Docker Engine with
docker compose - Python 3.12+
- Node.js 20+ and
npm - Supabase CLI
make,curl, andunzip
First-Time Bootstrap
These are the steps that currently produce a working local setup from a fresh clone.
1. Clone with submodules
git clone https://github.com/Deadwood-ai/deadtrees.git
cd deadtrees
git submodule update --init --recursive
2. Create the Python environment and install the CLI
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -e ./deadtrees-cli[test]
This installs the deadtrees command used for local stack management and test execution.
3. Install frontend dependencies
npm --prefix frontend ci
4. Create local env files from the checked-in examples
cp .env.example .env
cp frontend/.env.local.example frontend/.env.dev.local
The example values are set up for local Supabase CLI defaults and the local Docker test stack.
Keep manual SSH/VPN credentials, MCP bearer tokens, and other agent access notes
out of .env and frontend env files. See docs/agents/environment-and-access.md.
5. Start local Supabase
supabase start
Useful local endpoints after supabase start:
- API gateway:
http://127.0.0.1:54321 - Postgres:
postgresql://postgres:postgres@127.0.0.1:54322/postgres - Studio:
http://127.0.0.1:54323 - Mailpit:
http://127.0.0.1:54324
If you need a clean local database:
supabase db reset
Optional: isolated Supabase per worktree
For parallel local worktrees, generate a separate Supabase workdir, project id,
and port band without changing the tracked supabase/config.toml:
bash scripts/setup-worktree.sh --skip-assets
scripts/qa/env.sh up
scripts/qa/env.sh reset
scripts/qa/validate-isolated-env.sh
This writes ignored runtime state under .local/supabase/<worktree-slug>/,
starts the light Supabase profile, app API/nginx/Mailpit services, Vite, and
seeds deterministic QA data. The validation command checks that the generated
URLs are local-only, the services are reachable, QA fixtures are present, and
Auth recovery mail reaches the worktree's Mailpit instance.
To point shell commands, Docker Compose, Vite, and Playwright local E2E tests at that isolated stack:
set -a
source "$(scripts/dev/isolated-supabase.sh env)"
set +a
docker-compose.test.yaml, npm --prefix frontend run dev:local, and
npm --prefix frontend run test:e2e:local use the generated ports when these
variables are present. The default isolated Supabase profile excludes Studio,
Realtime, Storage, Mailpit, Edge Runtime, analytics/logging, and PgMeta; it
keeps Postgres, Kong, PostgREST, and Auth for normal local app flows.
Useful commands:
scripts/qa/env.sh status
scripts/qa/validate-isolated-env.sh
scripts/qa/env.sh down
# Lower-level Supabase-only controls:
scripts/dev/isolated-supabase.sh status
scripts/dev/isolated-supabase.sh stop
This layer only proves isolated dev instances. Browser Use, built-in Browser, and multi-agent QA orchestration are a separate layer on top of the running isolated frontend/API/Supabase stack.
6. Download required local assets and test fixtures
make download-assets
This now downloads the fixtures required for the local API test suite:
assets/test_data/test-data.tifassets/test_data/test-data-small.tifassets/test_data/yanspain_crop_124_polygons.gpkgassets/test_data/raw_drone_images/test_no_rtk_3_images.zipassets/test_data/raw_drone_images/test_minimal_5_images.zipdata/assets/dte_maps/*.tiftest clips- model and GADM assets
For the extra processor-only support data, run:
make download-processor-assets
This downloads:
assets/biom/terres_ecosystems.gpkgassets/pheno/modispheno_aggregated_normalized_filled.zarrassets/test_data/worldview_uint16_crop.tif
7. Start the local development stack
deadtrees dev start
This builds and starts the local test/dev services, including:
api-testprocessor-testnginxmailpit
To rebuild from scratch:
deadtrees dev start --force-rebuild
To stop the stack:
deadtrees dev stop
8. Start the frontend
npm --prefix frontend run dev:local
What Should Work After Bootstrap
Local services
- Frontend:
http://127.0.0.1:5173 - API via nginx:
http://127.0.0.1:8080/api/v1/ - API docs:
http://127.0.0.1:8080/api/v1/docs - Downloads docs:
http://127.0.0.1:8080/api/v1/download/docs - COGs:
http://127.0.0.1:8080/cogs/v1/ - Thumbnails:
http://127.0.0.1:8080/thumbnails/v1/ - Supabase Studio:
http://127.0.0.1:54323
Tests
Prefer the deadtrees CLI for tests and debugging.
source venv/bin/activate
# Full API suite
deadtrees dev test api
# Single API file
deadtrees dev test api api/tests/routers/test_download.py
# Processor suite
deadtrees dev test processor
# First-time processor test bootstrap on a new machine
make setup-local-test-ssh
make download-processor-assets
deadtrees dev test processor
# Frontend tests
npm --prefix frontend test
Current known-good local result:
deadtrees dev test apipasses end-to-end after the bootstrap steps abovenpm --prefix frontend testpassesdeadtrees dev test processorpasses aftermake setup-local-test-sshandmake download-processor-assets- the remaining processor skips are the intentionally skipped comprehensive GeoTIFF standardization tests
Debugging tests
deadtrees dev debug api --test-path=api/tests/routers/test_download.py
deadtrees dev debug processor --test-path=processor/tests/test_processor.py
Optional: Extra ODM Test Data
For the basic local API suite, make download-assets is enough.
If you want the larger locally generated ODM ZIP fixtures as well:
./scripts/create_odm_test_data.sh
This is optional and mostly useful for extra processor/ODM experiments.
Processor test prerequisites
The processor integration suite uses SSH against the local nginx test container to mimic the storage server.
Generate the local test-only keypair once before running processor tests:
make setup-local-test-ssh
make download-processor-assets
This writes an ignored keypair to .local/ssh/processing-to-storage and .local/ssh/processing-to-storage.pub.
If you prefer a different key location, set absolute paths in LOCAL_TEST_SSH_PRIVATE_KEY_PATH and
LOCAL_TEST_SSH_PUBLIC_KEY_PATH before running docker compose -f docker-compose.test.yaml ....
Processor-server validation workflow
This local laptop/worktree is good for API, shared, frontend, docs, and non-GPU checks. Processor tests that need NVIDIA runtime, model checkpoints, or full combined-model execution should run on the processing server dev checkout:
ssh processing-server
cd /home/jj1049/dev/deadtrees
git fetch origin <branch>
git checkout -B <branch> origin/<branch>
source venv/bin/activate
deadtrees dev test processor processor/tests/test_processor.py
deadtrees dev test processor processor/tests/test_process_deadwood_treecover_combined_v2.py::test_model_loads
If /home/jj1049/dev/deadtrees is dirty or not the current monorepo checkout,
move it aside first instead of deleting it:
cd /home/jj1049/dev
ts=$(date -u +%Y%m%dT%H%M%SZ)
mv deadtrees "deadtrees.backup-$ts"
git clone https://github.com/Deadwood-ai/deadtrees.git deadtrees
cd deadtrees
git fetch origin <branch>
git checkout -B <branch> origin/<branch>
Copy ignored local test assets or .env from the backup only when needed. For
new status columns or tables, apply the migration to the dev/test Supabase DB
before running processor tests and reload the PostgREST schema cache. Never use
this workflow against /home/jj1049/prod/deadtrees unless the user explicitly
asks for a production operation.
Codex app worktrees
If you use the Codex desktop app, configure a project Local Environment so new Dead Trees worktrees bootstrap themselves automatically.
- Setup script:
bash scripts/setup-worktree.sh - Suggested actions:
source venv/bin/activate && deadtrees dev test apisource venv/bin/activate && deadtrees dev test processornpm --prefix frontend test
The setup script auto-detects the primary checkout from git worktree list,
so a Codex-created worktree will reuse the main checkout for shared assets,
data, .local/ssh, local .codex project config, and ignored local
docs/ops playbooks without hardcoding a machine-specific path.
Tracked Codex instructions live in root AGENTS.md, frontend/AGENTS.md, and
docs/agents/. Local-only access details stay in .codex/*, docs/ops/*, and
ignored env files.
Project structure
/assets - Downloaded data and models
/gadm - GADM geographic data
/models - ML models for deadwood segmentation
/test_data - Test GeoTIFF files
/api - FastAPI application
/src - Source code
/tests - API tests
/processor - Data processing service
/src - Source code
/tests - Processor tests
/shared - Shared code between API and processor
API - Deployment
Additional requirements
So far I found the following packages missing on the Hetzner ubuntu image:
apt install -y make unzip
Setup user
create a user for everyone to log in (using root)
useradd dendro
usermod -aG docker dendro
Init git and download repo
Next upload SSH keys for developers to home/dendro/.ssh/authorized_keys
Add Env variables to the key, to set the git user for each developer
command="export $GIT_AUTHOR_NAME='yourname' && export $GIT_AUTHOR_EMAIL='your-email';exec $SHELL -l" key
Next change the /home/dendro/.bashrc to configure git, add to the end:
if [[ -n "$GIT_AUTHOR_NAME" && -n "$GIT_AUTHOR_EMAIL" ]]; then
git config --global user.name "$GIT_AUTHOR_NAME"
git config --global user.email "$GIT_AUTHOR_EMAIL"
fi
Now, you can download the repo, including the private repo.
# Clone the repository
git clone git@github.com:Deadwood-ai/deadtrees.git
cd deadtrees
# Initialize and update submodules
git submodule update --init --recursive
Create a .env file with required environment variables:
On the Storage server, only the necessary env is set
SUPABASE_URL=your_supabase_url
SUPABASE_KEY=your_supabase_key
Download required assets:
# Create assets directory and download test data, models, and GADM data
make
Build the repo
Optionally, you can alias the call of the correct docker compose file. Add to .bashrc
alias serv='docker compose -f docker-compose.api.yaml'
Certificate issueing & renewal
The certbot service can be used to issue a certificate. For that, the ACME challange has to be served by a temporary nginx:
server {
listen 80;
listen [::]:80;
server_name default_server;
server_tokens off;
# add ACME challange for certbot
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
}
Then run the certbot service with all the mounts as configured in the docker-compose.api.yaml:
serv certbot certonly
Once successfull, you can start a cronjob to renew the certificate:
crontab -e
And add the following cronjob to run every Sunday night at 1:30. Currently we are not notified if this fails and thus needs to be monitored for now:
30 1 * * 0 docker compose -f /apps/deadtrees/docker-compose.api.yaml run --rm certbot renew