← All projects

Field Operations Platform

The Sod Boys · In Progress

Overview

A landscaping and construction crew was coordinating schedules, equipment loadouts, material orders and check-ins through scattered group chats, spreadsheets and memory. I built a single system that holds that data, and two ways to use it from a phone: a web dashboard for managers and a WhatsApp assistant for crews. I wrote the backend, adapted the frontend, deploy it, and keep it running.

Highlights

  • 40+ database migrations, a TypeScript domain layer and a REST API behind a React dashboard
  • WhatsApp crew assistant that calls the same domain logic through an MCP server
  • Nightly backups with restores actually tested, health heartbeats and a disaster-recovery runbook

Diagrams

Architecture of the field operations platform: a web dashboard and a WhatsApp gateway reach a REST facade and an MCP server, which share one TypeScript domain layer backed by PostgreSQL on a cloud VM behind Cloudflare and Caddy
System architecture: two front doors, one domain layer.
How a WhatsApp request is checked and approved: gateway calls the MCP server, a signed capability grant is verified against the tool's tier, reads answer directly and gated writes wait for manager approval
How a WhatsApp request is checked, approved and recorded.
Deploy, backup and monitoring: builds on a laptop, code pulled on the server, nightly database dump to object storage with restore tests, a heartbeat and an external uptime check
Deploy, backup and monitoring.

Diagrams are simplified. Addresses, hostnames and account names are placeholders.

Architecture

  • Backend: a TypeScript/Node.js domain layer (crew, sites, equipment, inventory, timeclock, procurement, payroll) exposed two ways: a REST facade for the dashboard and an MCP server for AI agents. Both call the same domain functions, so business rules live in one place.
  • Data: PostgreSQL (with pgvector) in a container, schema managed by ordered migrations, with a nightly dump copied to cloud object storage.
  • Frontend: an open-source construction-ERP frontend, forked and pruned down to the eight modules the business uses, served as static files by Caddy.
  • Edge: Cloudflare in front, full-strict TLS with an origin certificate, Caddy as the reverse proxy on a small Oracle Cloud VM.
  • Crew interface: a WhatsApp gateway connected to the MCP server, so crews can check in, log photos, order materials and ask questions in plain language.

The WhatsApp agent and how it is kept safe

  • Every action is a typed tool that declares a capability tier: 0 for read-only lookups, 2 for crew submissions (time clock, receipts, mileage, check-outs), 3 for anything that commits money, schedule or inventory, and 4 for admin and financial actions.
  • The gateway holds one service credential limited to tier 2, attached by the gateway so the language model never sees it. I tested a tier-3 call with that credential against the real server and it was denied.
  • Credentials are signed capability grants tied to an identity, and revoking a grant is the safety valve if one is ever exposed.
  • Some crew actions, such as spend records and mileage claims, use confirm-before-execute: the submission becomes a pending confirmation that a manager approves or rejects before anything is committed.
  • The agent resolves who is writing by looking up the sender's phone number with a tool, and it only reports what a tool returns, not what it guesses.
  • A policy file states which functions may call external services, and the AI provider chain has fallbacks if one provider fails.

Security and reliability

  • Login rate limiting plus per-account lockout, and TOTP multi-factor authentication for admin accounts.
  • Dependency audit clean, and the git history scanned for committed secrets (none found).
  • Heartbeat health checks from the server and an independent external uptime monitor.
  • Nightly database backups, and a restore into a throwaway database to prove the backup holds real, coherent data.

A real outage, and what changed

A frontend build run on the 1 GB production VM exhausted memory and made the server unresponsive for over an hour. I recovered it under time pressure, then wrote a disaster-recovery runbook. Builds now happen on my own machine and are shipped as static files, and the backup restore test that was previously just a gap in the plan is now done for real.

Technologies

TypeScript, Node.js, PostgreSQL, React, Oracle Cloud, MCP

Source code