Building an Offline-First Bushfire Response Platform With Hermes Agent A developer rebuilt Project Haven, an offline-first bushfire response platform, integrating Hermes Agent as the core AI engine for emergency guidance, scheduled fire-risk briefings, and recovery grant research. The platform, originally a 46-hour GovHack 2024 winner, now runs event-driven microservices with a fully offline-capable React PWA, using Hermes to power real-time chat grounded in Australian emergency protocols and autonomous web searches for government grants. The entire stack, including a local LLM, deploys with a single Docker command and requires no external inference API. This is a submission for the Hermes Agent Challenge: Build With Hermes Agent Project Haven is an AI-powered emergency response platform for bushfire preparedness — evacuation routing, tiered real-time alerts, government recovery grant discovery, and offline-first PWA support for when mobile networks go down mid-crisis. The platform was originally a 46-hour hackathon build GovHack 2024 — we won . This year I brought it back from the dead and rebuilt https://dev.to/ujja/from-govhack-win-to-something-that-actually-matters-2mmi it properly: event-driven microservices, contract-first OpenAPI specs, a prediction engine based on XGBoost weights from our historical bushfire notebooks, and a fully offline-capable React PWA. The one piece that was always a hollow mock was the AI Assistant — the in-app emergency guidance chat. It had a setTimeout pretending to think and a big switch statement of canned responses. Every time I looked at it I felt embarrassed. Hermes fixed that. Hermes Agent is now the live brain behind three things in Project Haven: In-app emergency guidance — the AI Assistant page calls Hermes via its OpenAI-compatible /v1/chat/completions API, grounded with a system prompt that constrains it to verified Australian emergency protocols and instructs it to escalate emergency situations to 000. Scheduled fire-risk briefings — Hermes runs a natural-language cronjob that fires every morning at 6am during fire season, calls the Bureau of Meteorology and NSW RFS feeds, synthesises a risk summary, and publishes it as an event into the alert pipeline. Recovery grant research — when a user marks themselves as "in recovery", Hermes autonomously searches for current government grant programs NDRA, state schemes, Services Australia , compares them against the user's declared situation, and adds matched recommendations to the recommendation service DB. 🔗 GitHub Repository: project-haven cp .env.example .env docker compose up --build That's it. One command spins up 6 microservices, an API gateway, PostgreSQL instances, RabbitMQ, and the React PWA at http://localhost:3000 . To trigger the full prediction → alert pipeline: curl -X POST http://localhost:8080/weather \ -H 'Content-Type: application/json' \ -d '{"lat":-33.87,"lng":151.21,"temperature":42,"windSpeed":80,"humidity":10,"season":"summer","vegetationDensity":0.9}' That simulates an extreme weather event near Sydney, runs it through the prediction engine, and fires a CRITICAL alert through the system within seconds. AI Assistant — Hermes-powered emergency guidance The AI Assistant page now sends messages to Hermes via the api-gateway /assistant/v1/chat/completions . Hermes has persistent memory across sessions via X-Hermes-Session-Key , so if a user opened the app two days ago and said "I'm in the Blue Mountains", Hermes still knows that when they say "the fire is getting closer" today. Scheduled briefings appearing in the alert feed Every morning during fire season, Hermes fetches live fire danger ratings from the NSW RFS API, synthesises a 3-sentence risk summary grounded in real data, and injects it as a feed.created event. The event propagates through RabbitMQ to the alert service and appears in-app within seconds. Recovery grant matching A user marks the "Recovery" scenario. Hermes is asked to research grants available for their postcode and situation. It uses its web search tool to check current Services Australia pages — bypassing the staleness problem of any static dataset — and returns structured results that get persisted back to the recommendations table. 🔗 GitHub Repository: project-haven The full stack — Haven microservices + Hermes Agent + a local LLM — runs entirely on your machine. No external inference API needed. Requirements: Docker Desktop 27+, 8 GB RAM minimum 16 GB recommended , macOS / Linux / WSL2 on Windows. Ollama, the model, all backend services, and the frontend are all managed by Docker Compose. No host-level installation needed beyond Docker itself. git clone https://github.com/ujja/project-haven.git cd project-haven cp .env.example .env docker compose up --build On first start, the ollama-init container pulls nous-hermes2 ~4.5 GB automatically. The api-gateway waits for the pull to complete before starting. Subsequent starts are fast — the model is cached in a Docker volume. Progress is streamed to the compose log. Once you see api-gateway | api-gateway listening on port 8080 , everything is ready. Simulate an extreme weather event near Sydney → triggers prediction → alert curl -X POST http://localhost:8080/weather \ -H 'Content-Type: application/json' \ -d '{"lat":-33.87,"lng":151.21,"temperature":42,"windSpeed":80,"humidity":10,"season":"summer","vegetationDensity":0.9}' Ask the AI assistant directly curl -X POST http://localhost:8080/assistant/v1/chat/completions \ -H 'Content-Type: application/json' \ -d '{"model":"nous-hermes2","messages": {"role":"user","content":"There is a bushfire near me. What do I do right now?"} }' Search live recovery grants curl -X POST http://localhost:8080/recommendations/research \ -H 'Content-Type: application/json' \ -d '{"postcode":"2750","situation":"home destroyed by bushfire"}' React PWA port 3000 ↓ API Gateway port 8080 ├── /assistant/ → Ollama /v1/chat/completions nous-hermes2 ├── /recommendations/research → Ollama structured JSON └── node-cron @ 6am → Ollama → feed-service ↓ Ollama container port 11434, haven-net ↓ nous-hermes2 cached in ollama-data volume Everything runs inside Docker Compose. ollama-init pulls the model once on first start; the ollama-data volume persists it across restarts. | Model | Size | Best for | |---|---|---| nous-hermes2 | ~4.5 GB | Default — strong instruction following, good JSON | gemma2:9b | ~5.4 GB | Superior JSON adherence | gemma2:2b | ~1.6 GB | Low-RAM machines, faster responses | llama3:latest | ~4.7 GB | General-purpose alternative | Set HERMES MODEL in .env to swap. Also update the ollama-init entrypoint in docker-compose.yml to pull the new model. Avoid 70B-class models unless you have GPU hardware with 40+ GB VRAM. First start is slow: The ollama-init container has to download the nous-hermes2 model ~4.5 GB . Subsequent starts skip this. Model too slow on CPU: Edit .env and set HERMES MODEL=gemma2:2b 1.6 GB, much faster . Also update the ollama-init entrypoint in docker-compose.yml to pull the new model. WSL2 networking problems: Volume mounts and bridge networking have edge cases — increasing Docker's memory allocation in Docker Desktop settings usually resolves them. Linux GPU acceleration: Uncomment the deploy block in the ollama service in docker-compose.yml and ensure nvidia-container-toolkit is installed. | Layer | Technology | |---|---| AI backbone | Nous Hermes 2 via Ollama OpenAI-compatible, locally hosted | Frontend | React 18, TypeScript, Vite, Workbox PWA, Leaflet | Backend | Node.js 20, Express, TypeScript — 6 microservices + API gateway | Messaging | RabbitMQ event-driven: weather → prediction → alert pipeline | Databases | PostgreSQL per-service | ML | XGBoost Python notebooks → TypeScript heuristic engine | Data | Digital Atlas / Geoscience Australia ArcGIS REST APIs | Infra | Docker Compose, multi-stage builds, shared @haven/shared npm package | Ollama exposes POST /v1/chat/completions exactly like the OpenAI SDK expects. It runs as a Docker Compose service ollama on the internal haven-net network. The api-gateway proxies /assistant/ directly to http://ollama:11434 . No separate agent container, no extra port, no API key management between services. In AIAssistant.tsx , the getAIResponseHermes function — previously a setTimeout + switch statement — became a real API call: async function getAIResponseHermes userMessage: string : Promise