Scout is an open source community webapp local to Washington DC intended to help disabled folks navigate the city with confidence. It's still in early active development. There's a lot to figure out! I'm a single maintainer. Contribution and feedback welcome!
ScreenRecording_07-02-2026.19-30-08_1.MP4
I am using a lot of agentic development, partly due to my hand mobility problems that make voice-to-text much easier than typing. I've tried to make this repo friendly to practical and efficient agent development, and I share in the contribution guidelines a preferred LLM that is trained on energy grids that are largely powered by sustainable energy and value privacy. I've also used pi.dev + ollama + local small model evaluation somewhat heavily here to good effect.
Dev setup guide is below!
Want to contribute to offset Scout's hosting costs? Get me here. Anything over the cost of Scout's hosting & domain registration will be donated to one DC's ward mutual aid organizations.
- Python ≥ 3.12 —
uv - Node ≥ 20 + pnpm — needed once
apps/web/package.jsonexists (pnpm install). - pre-commit —
brew install pre-commitoruv tool install pre-commit(install guide). - go-pmtiles -
brew install pmtiles, used for working with (pmtiles archives)[https://github.com/protomaps/go-pmtiles] - uv -
brew install uv
Only OpenRouteService needs a real signup per se. Address and place-name
searching uses Washington, DC's public-domain Master Address Repository
snapshot bundled with Scout (make ingest-dc-addresses, make ingest-dc-pois).
Refuge Restrooms is keyless, and the Protomaps basemap extract is built by a
script. You can skip this entire section
if you only ever run make docker-up (the default boots a stack with routing,
geocoding, and restrooms stubbed — no outbound calls except whatever you enable).
The credential below is required for make docker-up-realistic-run, production
deploys, and manual verification against OpenRouteService.
Create your local env file first, then fill in the values below:
cp .env.example .env-
OpenRouteService (
SCOUT_ORS_API_KEY) — required for real routing.-
Sign up at https://openrouteservice.org/dev/#/signup, confirm your email, sign in, and request a token from the dashboard. The free Standard plan is plenty for local dev (a few thousand directions requests/day, ~40 req/min).
-
Tokens can take a few minutes to activate after issuance.
-
Paste the token into
.env:SCOUT_ORS_API_KEY=eyJ...your-token-here -
The backend surfaces a clear "missing key" error on the first
/api/routecall if this is left blank.
-
-
DC address snapshot load (
make ingest-dc-addresses) — no signup. After Postgres has thedc_addressestable (Alembic0002+), ingest the committeddata/dc_addresses.jsonlinto local or Compose-attached Postgres so/api/geocode/*serves real MAR rows (DEC-023). Compose does not auto-load MAR data yet; run this once whenever you recreate the DB volume. -
DC points-of-interest snapshot load (
make ingest-dc-pois) — no signup. After Postgres has thedc_points_of_interesttable (Alembic0003+) and the address snapshot above is already loaded (this ingest joins named places to their street address byMAR_ID), load the committeddata/dc_points_of_interest.jsonlso/api/geocode/*also resolves landmark names like "National Building Museum" (DEC-026). Run this once whenever you recreate the DB volume, right after step 2. -
Refuge Restrooms — no key, no signup. The default
SCOUT_REFUGE_BASE_URLin.env.exampleis the public API (https://www.refugerestrooms.org/api/v1). Nothing further to do. -
Protomaps basemap tiles — no key. Run
scripts/build_pmtiles.shonce after cloning to populateapps/web/public/tiles/dc.pmtilesfor the interactive MapLibre basemap (requires thepmtilesCLI from the prerequisites above).
git clone https://github.com/RSid/scout.git
cd scout
make bootstrap
pre-commit install
make syncRun the full stack locally (PostGIS + backend + web) with Docker Compose and stubs for map — no host account required:
make docker-up # http://localhost:3000 + :8080
make docker-downOnce .env has the values above, boot the live stack:
make docker-up-realistic-runSee infra/README.md for the Compose layout (including Testing on a phone) and CONTRIBUTING.md for the
end-to-end dev loop. make help lists every shortcut (lint, tests,
Compose, make ingest dry tally, make ingest-write, …). Copy .env.example to .env and adjust
SCOUT_* variables when you need host-side overrides.
First-time production setup (Hetzner VPS, Hostinger DNS, Docker Compose, host
Caddy for HTTPS) is documented step-by-step in
infra/first-deploy.md.
(Wondering why Hetzner? See docs/proposals/green-hosting-shortlist.md)
Set SCOUT_DEPLOY_HOST in .env (e.g. root@your-server), then from your
laptop:
make release # patch bump (v0.1.0 → v0.1.1)
BUMP=minor make release # minor bump
BUMP=major make release # major bumpThis SSHes into the server, pulls, rebuilds, waits for a health check, then tags the deployed commit with a semver git tag and pushes it.
To roll back to a previous release:
make rollback VERSION=v0.1.0What happens automatically:
- The app container rebuilds when source changed and restarts.
alembic upgrade headruns on boot — schema migrations apply with the deploy; no manual migration step.- The health check (
/api/health) is verified before tagging.
What you usually do not repeat:
- Data ingest (
ingest-features,ingest-addresses) — only needed after recreating the database volume or when intentionally refreshing data. Seeinfra/runbooks/refresh-dc-addresses.md.
Manual verification after deploy (optional — make release already checks
health):
curl -fsS https://yourdomain.com/api/health
ssh $SCOUT_DEPLOY_HOST 'docker compose --project-directory /opt/scout -f /opt/scout/infra/docker-compose.prod.yml logs --tail=50 app'If the deploy fails, check app logs first; the container exits when uvicorn, Next, or the in-image Caddy fails to start.
Editing .env does not affect a running container — Compose reads env vars at
create time. After changing secrets or other app settings (e.g.
SCOUT_ORS_API_KEY), recreate the app service from the repo root:
docker compose --project-directory . -f infra/docker-compose.prod.yml up -d --force-recreate appA plain docker compose restart app reuses the old environment and will not
pick up the new values. Use --build as well if you also pulled code changes
(the full subsequent-deploy command above already covers that).
If you changed SCOUT_DB_PASSWORD, you must also recreate db and app
together so Postgres and the app stay in sync — only do this with a plan, since
the existing pgdata volume keeps the old password until you migrate it
manually.
Once the stack is up, populate the features table from DC OpenData
(scripts/ingest_dc.py, M1-F11):
docker compose --project-directory . -f infra/docker-compose.yml \
--profile ingest run --rm ingestmake ingest runs the same script in dry-run mode against your active
SCOUT_DATABASE_URL — useful for previewing counts before a real write.
Once the stack is up (make docker-up), Postgres/PostGIS is exposed on your
machine. In your SQL client choose a PostgreSQL connection.
| Setting | Typical value |
|---|---|
| Host | 127.0.0.1 or localhost |
| Port | 5432 by default (SCOUT_DB_HOST_PORT in .env if you remapped it; see .env.example) |
| Database | scout |
| User | scout |
| Password | scout |
| SSL | Off |
In Beekeeper Studio: New Connection → PostgreSQL, then paste the settings above (Save connects without SSL).
db and port 5432 inside Compose are only for containers on the Compose
network. From your laptop, use localhost + the published host port (the one
Docker maps into the VM), not hostname db. If connecting fails, confirm the
containers are healthy and adjust SCOUT_DB_HOST_PORT if another Postgres on
5432 conflicts.
Ingest (make ingest-write / scripts/ingest_dc.py) loads .env from your
current working directory (usually the repo root when using make). When
SCOUT_DB_HOST_PORT is set there, the script rewrites SCOUT_DATABASE_URL
if its hostname is db, localhost, or loopback so it connects to
127.0.0.1:<SCOUT_DB_HOST_PORT> with the same user, password, and database
name.
alembic / make migrate applies the same rewrite from process environment
variables only: SCOUT_DATABASE_URL and SCOUT_DB_HOST_PORT must be
exported (or your shell must source .env) before make migrate so Alembic
sees them.
Use the GitHub Issues tab. You'll see two structured templates:
- Bug report — something in Scout itself isn't working.
- Data is wrong about a place — a feature is mis-described in the
underlying DC dataset. (Scout surfaces public data; the city owns the
source-of-record. An in-app correction flow lands with
M3-F25.)
Security vulnerabilities do not go in public issues — open a private
security advisory on this repo's Security tab instead. See
CONTRIBUTING.md and SECURITY.md for the full disclosure process.
Washington, DC accessibility navigation — monorepo layout per PRD §8 (docs/02-prd.md): apps/, data/, scripts/, infra/, docs/.