Skip to content

Releases: SysAdminDoc/project-nomad-desktop

v7.32.0 — Deep Security & Reliability Audit

15 Apr 22:33

Choose a tag to compare

Deep Security & Reliability Audit (Waves 1 + 2)

Comprehensive end-to-end audit across 13 files targeting security, correctness, and frontend hardening. 913 tests pass.

Security

  • PBKDF2-SHA256 credential hashing — PIN and password hashing upgraded from plain SHA-256 to PBKDF2-SHA256 with random 16-byte salt (100k iterations). Legacy hashes transparently upgraded on successful login. Zero user-facing changes.
  • Removed hardcoded API credentials from VIPTrack (dead defaultCredentials object with clientId/clientSecret)
  • Services bind to APP_HOST (127.0.0.1 default) instead of 0.0.0.0 — Ollama and Kiwix no longer exposed on all interfaces
  • Path traversal fix in media uploads — posixpath normpath sanitizer replaces naive replace('..', '')
  • SSE event type injection prevention — newline/carriage return stripping on broadcast events
  • 15+ innerHTML XSS vectors escaped across system info, benchmarks, maps, AI models, widget config, and phrase cards
  • Frontend fetch timeouts — 30s AbortController on safeFetch/apiFetch, 60s on fullSync

Bug Fixes

  • Alert rules cooldown crashdatetime.now() vs timezone-aware ISO string comparison now uses utcnow() with tzinfo stripping
  • AI context window — system message preserved when truncating conversation history (was silently dropped)
  • Monthly task recurrence — calendar-aware month arithmetic instead of timedelta(days=30) drift
  • Garden seed expirationcalendar.monthrange() replaces hardcoded day cap at 28 (was truncating dates to the 28th)
  • Version mismatch — launcher now uses Config.VERSION instead of stale hardcoded 7.10.0
  • Inventory CSV import — per-row error handling instead of all-or-nothing crash on bad data
  • Rate limiter memory leak — prune empty IP deques when count exceeds 500

Reliability

  • DB pool TOCTOU fix — moved _pool.get_nowait() inside _pool_lock
  • WAL mode — double-checked locking for init, PASSIVE checkpoint for backups
  • Federation sync — single atomic commit for data + vector clocks + sync log (was 3 separate commits)
  • Config save — wrapped in thread lock for concurrent access safety
  • Zombie process cleanup — service manager properly waits/kills on start error
  • RSS feed worker — error logging instead of silent swallowing

Input Validation

  • Coordinate validation (-90..90 lat, -180..180 lng) on waypoint create/update and emergency rally points
  • Triage category validation — rejects values outside immediate/delayed/minimal/expectant/unassigned
  • Medical vitals CHECK constraints — bp, pulse, temp, spo2, pain, gcs ranges enforced at DB level
  • Patient CHECK constraints — age 0-200, weight 0-700 kg

Build

  • 30+ missing PyInstaller hidden imports for newer blueprints that were lazy-imported
  • Installer version synced to 7.32.0

Build artifacts auto-attached by CI workflow.

v7.28.0 — Auth foundation + validation expansion

15 Apr 02:47

Choose a tag to compare

Auth foundation + validation expansion (Roadmap H2/H4 + M1/M2)

Builds on v7.27.0 hardening with the auth enforcement layer the audit flagged as the largest remaining security gap (H4), plus continued expansion of input validation, pagination, and activity logging.

H4 — Authentication enforcement layer

  • New web/auth.py with require_auth(role='user') decorator
  • Desktop mode (default): decorator is a no-op; g.current_user set to a synthetic admin so downstream code works unchanged. Existing single-user installs require zero migration.
  • Multi-user mode: opt-in via NOMAD_AUTH_REQUIRED=1 env var. Validates session token from Authorization: Bearer <token> header or ?token= query against Phase 19's app_sessions/app_users tables. Localhost always exempt so the pywebview shell works.
  • Role hierarchy: admin > user > viewer > guest. @require_auth('admin') rejects lower-rank sessions with 403, missing/invalid sessions with 401.
  • Demo coverage: applied to all 8 mutating financial endpoints. Pattern is a one-line decorator addition for any blueprint.

H2 — Input validation expansion

  • medical_phase2: 9 routes wrapped — pregnancies, dental, chronic conditions, vaccinations, vet, mental health
  • vehicles: 4 routes wrapped — vehicles + maintenance (create + update)
  • Schemas enforce types, max lengths, numeric bounds (year 1900-2100, mpg 0-1000, mood/anxiety 0-10), and choices enums for severity/species/status

M1 — Pagination expansion (4 more blueprints)

  • agriculture, group_ops, readiness_goals, land_assessment
  • Total: 22 list endpoints across 11 of 19 blueprints

M2 — Activity logging expansion

  • checklists (create/update/delete) and weather (action rules)
  • Total: 4 of 11 audit-flagged blueprints

Stats

  • 14 files changed, 276 insertions, 21 deletions
  • New file: web/auth.py
  • No DB schema changes — uses Phase 19 tables
  • Backward compatible — desktop mode default preserves existing behavior

Remaining for v7.29.0+

M5 FTS5 search, M6 connection pooling, full M1/M2/H2 rollout, frontend modernization (#8 deep / #9 safeFetch / #14 aria-labels). See ROADMAP-v8.md.


Build artifacts auto-attached by CI workflow on tag push.

v7.27.0 — Hardening & Polish

15 Apr 02:39

Choose a tag to compare

Hardening & Polish — 17 audit findings closed

This release closes 17 items from the v7.27.0 hardening punch list spanning security, reliability, and schema integrity. No new features — focused entirely on production readiness.

Critical / High

  • H1 sensor_readings table conflict resolved (Phase 18 renamed to iot_sensor_readings)
  • H3 Mutating rate limit actually enforced — per-IP sliding window (60/min by default), localhost exempt
  • H5 Path traversal hardening on Windows — os.path.commonpath() replaces fragile normcase prefix check in NukeMap & VIPTrack static routes
  • H2 (partial) Input validation schemas applied to all 8 mutating financial endpoints
  • #10 Disk-space pre-check before yt-dlp downloads

Medium

  • M1 (partial) Server-side pagination via shared get_pagination() across 17 list endpoints in 7 blueprints (financial, daily_living, training_knowledge, hunting_foraging, disaster_modules, movement_ops, evac_drills)
  • M2 (partial) log_activity() audit trail added to contacts + vehicles CRUD
  • M3 SSE client-limit race condition collapsed into single locked path
  • M4 access_logs renamed to platform_access_log with idempotent migration (disambiguates from access_log used by physical-security)
  • M7 _env_int() helper — bad NOMAD_PORT no longer crashes app at import time
  • #11 Ollama streaming resilience — corrupt JSON chunks skipped, not forwarded to client
  • #12 Streaming CSV import for contacts (no full-buffer load, batched 500-row commits)
  • #13 Duty roster cleanup on pod member removal

Low

  • L2 Hidden input name= attrs in _tab_daily_living.html
  • L3 sys.exit(0) replaces os._exit(0) so WAL frames flush
  • L4 Double preparedness import consolidated
  • L6 PID recycling — psutil exe basename match in is_running()

XSS hardening (#8 partial)

  • esc() helper seeded in all 9 Phase 17-20 partials (foundation in place)
  • _tab_medical_phase2, _tab_agriculture, _tab_hunting_foraging fully escaped — 5 render loops + shared badge helpers
  • _tab_group_ops statusBadge and _tab_security_opsec classificationBadge/categoryBadge wrapped (high call-site fan-out)

Remaining for v7.28.0+

H4 auth middleware, M5 FTS5 search, M6 connection pooling, full M1/M2/H2 rollout, #9 safeFetch migration. See ROADMAP-v8.md for the staged release plan.


Build artifacts (Windows portable + Inno installer, Linux, macOS, AppImage) will be attached automatically once the build workflow completes.

NOMAD Field Desk v7.7.0 — Daily Operations Brief

13 Apr 03:35

Choose a tag to compare

Daily Operations Brief

A one-page compiled snapshot of what matters today — weather, proximity alerts, inventory gaps, tasks, and family status.

Click Generate on Home to compile. Print opens a framed printable variant for physical carry.

Sections (each optional)

  • Emergency Mode status (if active)
  • Weather + conditions
  • Proximity alerts within home radius (v7.2.0)
  • Inventory expiring (14 days) + low-stock items
  • Tasks due today
  • Family check-in summary (v7.6.0)

Every section gracefully degrades. A fresh install with no data still returns a usable brief.

Why

Rich dashboards rely on users remembering to look at them. The brief inverts the relationship — it pulls the signal to you.

Tests

889 passed (was 882) — 7 new tests.

NOMAD Field Desk v7.6.0 — Family Check-in Board

13 Apr 03:18

Choose a tag to compare

Family Check-in Board

The hardest single question in a real crisis: where's everyone and are they OK?

New card on Home shows each household member with a 4-status board:

  • OK — safe, at expected location
  • EN ROUTE — moving to rally point
  • NEEDS HELP — injured or in trouble
  • UNACCOUNTED — no contact (sorted first, pulses)

One tap to change anyone's status. Summary chip row at the top ("3 OK · 1 EN ROUTE · 1 UNACCOUNTED"). Cross-tab sync via SSE — mark someone OK on one tab and the board updates everywhere.

Separate from Contacts

Contacts is your stable reference directory (who, what role, what frequency). Check-in state is high-frequency mutation that doesn't belong there. Family Check-in gets its own family_checkins table.

Reset All

One button to reset every member back to OK after a drill or situation ends.

Tests

882 passed (was 870) — 12 new tests covering create, update, delete, 404s, duplicate rejection (409), summary counts, sort order, reset-all.

NOMAD Field Desk v7.5.0 — Emergency Mode (capstone)

13 Apr 03:02

Choose a tag to compare

Emergency Mode — the big red button

NOMAD already had every component you need for a live event. SITREP, watch rotation, incidents, contacts, AI copilot, Situation Room, proximity alerts. But nothing that orchestrated them when it mattered.

Emergency Mode is that orchestrator.

Enter

Red EMERGENCY button in the sidebar (always visible). Click → modal prompts for a brief reason ("Tornado warning", "House fire", "Medical"). On confirm:

  • Writes a persistent state flag so the banner survives page reloads
  • Creates a critical incident in the timeline
  • Broadcasts an SSE event → every open NOMAD tab on the machine syncs instantly
  • Voice Copilot (v7.1.0) announces the activation via TTS

Active state

A persistent red banner sits across every tab with:

  • Reason, live duration counter (updated every minute)
  • Animated pulse (respects prefers-reduced-motion)
  • An always-visible EXIT button

The shell picks up a 3px red glow inset. Clean, unmistakable, not in your way.

Exit

Click EXIT → closeout modal asks for a free-form note (what happened, what you did, what you'd do differently). On confirm:

  • Total duration is computed
  • A closeout incident is appended: "Emergency mode exited (4.2h): Tornado warning — damage minimal, shelter worked"
  • Banner dismisses; all tabs drop out of red mode

You get an auditable after-action review entry without any separate form.

Idempotent

  • Entering while already active returns the existing state (no double-log, no overwrite)
  • Exiting while inactive is a no-op (no error toasts for bad state)

Multi-tab safe

Every open NOMAD window on the same machine syncs via the existing SSE event bus. Works across reloads, browser restarts — the state lives in the settings table.

Tests

870 passed (was 861) — 9 new tests covering enter/exit/status, idempotency, incident creation, closeout, default reason, and length truncation.


This closes the 5-feature arc:

  • v7.1.0 Voice Copilot (hands-free AI)
  • v7.2.0 Location-aware Situation Room (proximity board)
  • v7.3.0 Interactive Kit Builder (personalized kits from mission params)
  • v7.4.0 Route Plan with Milestones (timed waypoints + sun + corridor)
  • v7.5.0 Emergency Mode (this)

Each feature stands alone but they compound — Emergency Mode uses the Voice Copilot for audible announcements, the Situation Room proximity for context, and the Kit Builder's resource model sits one tab over for quick reference during a live event.

NOMAD Field Desk v7.4.0 — Route Plan with Milestones

13 Apr 02:45

Choose a tag to compare

Route Plan — timed milestones, sun overlay, resupply corridor

NOMAD already has routes, waypoints, elevation profiles, and a sun calculator — but they didn't talk to each other. Bug-out route planning required mental math and a calculator tab. Not anymore.

New capability

Click Plan on any saved route to open the planner. Set:

  • Pace (km/h) — walking ≈ 5, hiking ≈ 4, running ≈ 8, vehicle ≈ 50
  • Corridor (km) — how wide a band alongside your route to search for resupply / shelter
  • People
  • Departure

The planner returns:

  • Timed milestones at every waypoint — cumulative distance + ETA ("At WP-Bravo by 14:30")
  • Sun overlay — sunrise and sunset for the departure and arrival days; milestones that fall in darkness are flagged
  • Corridor search — other waypoints from your map within the corridor are surfaced as resupply / shelter candidates
  • Resource budget — total water (L) and calories per person × trip duration

UI

5-stat summary header (km · duration · ascent · L water · kcal), sun overlay card, timeline of milestones with dark-window highlighting, nearby-waypoint list. Empty state when no candidates fall in the corridor prompts you to widen it or add more waypoints.

Under the hood

Pure Python — no external routing engine. Walks the user's existing waypoint chain and reuses the simplified NOAA sun algorithm already in tasks.py. Same water/calorie reference numbers as the Kit Builder (3 L/person/day, 3500 kcal/person/day).

Tests

861 passed (was 854) — 7 new tests for distance math, pace scaling, corridor search, and error paths.

NOMAD Field Desk v7.3.0 — Interactive Kit Builder

13 Apr 02:26

Choose a tag to compare

Kit Builder — personalized kits from mission parameters

The 6 static inventory templates (72hr kit, family 30-day, etc) were a great starting point, but a family of 5 in Phoenix needs different water than a solo hiker in Seattle. The Kit Builder generates a personalized list.

Wizard inputs

  • Mission — bug-out / shelter-in-place / vehicle / medical bag
  • Climate — temperate / cold / hot / tropical
  • People — 1 to 50
  • Duration — 1 to 2160 hours (up to 90 days)
  • Mobility — on foot / vehicle / mixed

Output

A tailored item list with:

  • Water scaled by USACE TB MED 507 climate tables (2–6 L/person/day)
  • Food scaled by calorie budget (2200–3500 kcal/person/day by mission)
  • Climate-specific: sleeping bags + base layers for cold; sun hats + DEET for hot/tropical
  • Mission-specific: tarps + topo maps for bug-out; comprehensive IFAK+ for medical
  • Weight estimates for every item

Each item carries a reason field (e.g. "6 L/person/day × 4 people × 3 days (hot climate, bug out)") so you can see why each item is recommended. No black-box LLM — the engine is explainable Python rules.

Inventory-aware

Every recommended item is cross-referenced against your existing inventory and tagged:

  • Have — you own at least the recommended quantity
  • Partial — you own some, but less than recommended
  • Gap — you don't have it

One click commits the gaps to the shopping list.

UI

Modal wizard in Preparedness → Inventory. Two-step layout (inputs → results). Results page: 5-stat summary (items, weight, gap/partial/have counts), filter checkboxes, grid with status badges. Mobile-responsive.

Tests

854 passed (was 843) — 11 new tests covering rule scaling, inventory matching, and input validation.

NOMAD Field Desk v7.2.0 — Location-aware Situation Room

12 Apr 22:14

Choose a tag to compare

Near You — location-aware Situation Room

The global Situation Room can now filter threats by distance from your home.

Proximity Board

A new top-of-dashboard card shows a 4-bucket ring of active events:

  • < 50 km immediate
  • < 200 km regional
  • < 500 km default situational awareness
  • < 2000 km wide-area

Below the rings: a nearest-first list of active events with distance + compass bearing (e.g. "23 km NE"). Event types covered: seismic, severe weather, fires, disease outbreaks, volcanic activity, armed conflict, OREF sirens.

New Settings row

Home Location — latitude, longitude, radius selector, plus a Detect button that uses the browser geolocation API. Coordinates are reused by existing weather and sunrise/sunset features.

Empty / error states

  • No coordinates set → board shows a prompt card with a button to open Settings (not an empty grid).
  • Coordinates set, no threats within radius → green "All clear within X km" message.
  • Fetch failure → "Proximity data unavailable" fallback.

Under the hood

  • GET /api/sitroom/proximity aggregates from the existing sitroom_events table — no new background fetcher needed.
  • Haversine + bearing helpers in situation_room.py, unit-tested against the published NYC→LA great-circle distance.
  • SETTINGS_WHITELIST extended with latitude, longitude, proximity_radius_km.

Tests

843 passed (was 839) — 4 new tests for the proximity endpoint.

NOMAD Field Desk v7.1.0 — Voice Copilot

12 Apr 21:56

Choose a tag to compare

Voice Copilot — hands-free AI conversation

The AI copilot dock can now hold a real spoken conversation.

Two modes

Single-shot mic (existing microphone button): dictate a question. The transcript becomes your question; the answer is now also spoken aloud via text-to-speech.

Hands-free mode (new speaker button): enters a continuous conversation loop — listen → ask → speak → listen. Perfect for medical triage when hands are busy, vehicle ops, or field work.

Exits on:

  • Saying "stop listening", "exit voice", "end session", or "goodbye"
  • Pressing Escape
  • Clicking the button again

Settings

New Voice Copilot row under Settings → Preferences:

  • Speak answers — toggle TTS on/off
  • Voice picker — choose from your OS's installed voices
  • Rate slider — 0.5× to 2×

Preferences persist in localStorage.

Accessibility

A visible state badge (Idle / Listening / Thinking / Speaking) and a live transcript strip sit inside the dock so users who can't hear the TTS still follow the exchange. Respects prefers-reduced-motion. The hands-free button auto-hides on browsers that lack SpeechRecognition or SpeechSynthesis (Linux WebKitGTK typically has no voices).

Under the hood

  • speech_out.js — queued TTS wrapper with interrupt + voice selection
  • voice_copilot.js — hands-free session loop layered on the Web Speech API
  • No backend changes — the existing /api/ai/quick-query drives everything

Tests

839 passed.