DiresQ

Running it

Ten minutes from a clean machine to a board with someone overdue on it.

Ten minutes from a clean machine to a working board with a responder already overdue on it.


What you need

Python 3.10 or newer. That’s it — no database server, no Node, no Docker. The database is a file.

Node 22 is only needed if you want to build this documentation site, which is a separate thing under site/ and not required to run the app.

python --version

If that says 3.9 or lower, install a newer Python before going further.

The short way: one file from the Releases page

Every tagged release carries two launcher scripts. Download the one for your system, run it, and it does the rest — fetches the source for that version, builds an isolated environment, seeds the demo database, starts the server and opens the browser.

# macOS and Linux
chmod +x diresq-macos-linux.sh && ./diresq-macos-linux.sh
# Windows
powershell -ExecutionPolicy Bypass -File diresq-windows.ps1

Everything lands in a folder beside the script. Delete the folder and nothing is left on the machine.

These are scripts, not installers, and there is deliberately no binary. Nothing in this project is compiled, so there is no 32-bit download and no 64-bit download, no Intel build and no Apple Silicon build — Python source has no architecture. A page of downloads that differed only in their filenames would look more professional and tell you less.

SHA256SUMS.txt is published alongside them. Verify before running anything you downloaded:

sha256sum -c SHA256SUMS.txt --ignore-missing
Get-FileHash diresq-windows.ps1 -Algorithm SHA256

Get it running from a clone

git clone https://github.com/Skythe7/DiresQ.git
cd DiresQ

python -m venv .venv
.venv\Scripts\activate          # Windows
source .venv/bin/activate       # macOS / Linux

pip install -r requirements.txt

flask --app app init-db
flask --app app seed
flask --app app run --debug

Open http://127.0.0.1:5000 and sign in as londo with the password diresq.

Use the virtual environment. Skipping it works right up until pip install -r requirements.txt silently downgrades the Flask your other projects rely on. We know because we did it.

What the commands do

CommandWhat it does
init-dbDrops every table and rebuilds from schema.sql. Destructive.
seedLoads an incident already two hours old
sweepFiles reports for anyone gone quiet. Also runs on page loads
node-key <user>Shows or --rotates a responder’s radio key

init-db really does drop everything. There are no migrations — a schema change means rebuilding, which is fine at this size and would not be fine in production.

What you should see

The seed loads a disaster already in progress, not an empty to-do list. That’s deliberate: an empty board makes this look like a task tracker.

Straight after seeding:

If the board is empty or the feed has five reports instead of eight, you’re running an old database — run init-db and seed again.

Accounts

Every seeded account uses the password diresq.

UsernameRoleCapabilities
londoresponderboat, medical
skytherespondertruck, chainsaw
m.torresresponderboat, swiftwater
j.okaforrespondertruck, chainsaw, generator
d.nguyenrespondermedical
s.reyesresponderboat, medical — the overdue one
kiyanreporter
a.whitlockreporter

Configuration

Copy .env.example to .env. Real environment variables beat the file, so your shell and CI always win.

VariablePurpose
DIRESQ_SECRET_KEYSigns session cookies. Without it a fresh key is generated every boot, which signs you out on every reload
DIRESQ_DEV_USERStay signed in as this user with no login. Full auth bypass — development only
DIRESQ_DBPath to the SQLite file. Defaults to diresq.db
DIRESQ_HTTPS_ONLYSet to 1 behind HTTPS so cookies are marked Secure. Leave unset on localhost or you won’t stay signed in

Generate a key with:

python -c "import secrets; print(secrets.token_hex(32))"

Seeing the interesting bits

The offline queue. Open a report page, DevTools → Network → tick Offline, press Check in. The button says “Saved — will send” and a pill appears bottom-left. Untick Offline and it sends within fifteen seconds. The board shows the time you pressed the button, not the time it synced.

The dead man’s switch, on demand. Join a report, then age the assignment past its deadline:

python -c "import sqlite3,datetime; d=sqlite3.connect('diresq.db'); \
d.execute(\"UPDATE assignments SET joined_at=?\", \
((datetime.datetime.now(datetime.timezone.utc)-datetime.timedelta(minutes=60)).isoformat(),)); \
d.commit()"
flask --app app sweep

It prints what it filed. Run it twice — the second time files nothing, because one open report per person is the whole idempotency rule.

A check-in over the radio path. No hardware needed:

flask --app app node-key londo      # note the id and key
python tools/gateway.py send --responder 1 --key <hex> \
    --lat 29.7858 --lng -95.8244 --age 3

Eighteen signed bytes, through the same code path a real LoRa gateway would use. Change one character of the key and it’s rejected.

The ICS-214 export. /board → the ICS-214 button. Opens in any spreadsheet, built from real timestamps.

Tests

pip install -r requirements-dev.txt
pytest -q

Every test gets a throwaway database, so order never matters. Add -k queue or -k break to run one area.

Lint is errors-only, not style:

ruff check --select=E9,F .

The documentation site

Separate project. Needs Node 22.

cd site
npm install
npm run dev

npm run sync copies ../docs/*.md into src/pages before every build, so the site can’t drift from the repo. Edit docs/, never site/src/pages/*.md — those are generated and gitignored.

When it goes wrong

table report_flags already exists — a half-finished init-db on an old database. Run init-db again; it’s safe to repeat.

no such column: node_key — your database predates a schema change. There are no migrations. init-db then seed.

coverage_gap_count is undefined — you’re running a server started before a code change. Restart it, and use --debug so it reloads on save.

Signed out on every reload — no DIRESQ_SECRET_KEY, so a new signing key is made each boot.

The map opens over the ocean — old seed data with no located reports. Run seed again.