4 live 1 building on the bench: Pehra Pro, Snap Receipt last shipped

Projects

Pehra Pro

Status
Building
Platforms
Web · server
Since
August 2026
Stage
Phase 0
Tenancy
One deployment per tracking company
Stack
NATS JetStream · PostgreSQL · TimescaleDB · TypeScript · Hono · React · MapLibre
Last shipped

Who watches the company that watches the vehicles?

Usually a spreadsheet, a notebook, and a phone that never stops ringing.

Pehra Pro is an AI-native platform for fleet management and asset tracking, built for GPS tracking companies. It takes data from mixed trackers, gives each company's customers live fleet operations, and runs the business behind it: devices and SIMs, installations, support, and billing.

Pehra means "the watch", as in standing guard.

Where it stands: Phase 0

  • Working end to end today, on simulated trackers: Teltonika and GT06 devices connect to the gateway. Their positions flow through NATS JetStream, get attributed to the right vehicle, land in TimescaleDB and PostgreSQL, and show up on the live map. Behind that: history, trips and stops, alerts, geofences, sensors, inventory, billing, a customer portal, and share links.
  • Not yet: frames captured from real hardware (every test frame so far is built from the vendors' published layouts), the driver app, the AI layer (designed and specified, not built), outbound webhooks, device commands, and UDP and MQTT listeners.
A simulated fleet on Pehra Pro's live map, with one vehicle's details open.

The 2010 tracker

In 2010 I built a commercial GSM vehicle tracker from a PIC16F887 and a SIM900 module. Pehra Pro is the platform that devices like it report to.

The devices barely changed. Cheap trackers still log in with an IMEI. They still buffer positions through coverage gaps and flush them later. They still resend a frame when the acknowledgement is late, and their clocks still lie.

What changed is everything around them: mixed brands in one fleet, customers who expect a live map, and a business that has to keep its stock, support and billing straight.

Building 247 Track, a tracking company's app for its customers, showed me how much better that whole setup could be. That's why I started Pehra Pro. The two are separate products.

A PIC microcontroller in a 40-pin package, beside a SIM900D GSM module.
The parts the 2010 tracker was built around: a PIC microcontroller and a SIM900 GSM module.

Who's the customer?

The tracking company in the middle: a small team that installs and manages trackers for many customers, from one person with five vehicles to companies running hundreds.

Most fleet platforms are built for the fleet, and start with the map. Pehra Pro is built for that company, and starts with its stock, installations, support and billing.

What that business fights every day:

  • Mixed hardware. Teltonika, GT06-family clones and others, side by side in the same fleet, each with its own quirks.
  • Stock on paper. Devices and SIMs move between branches and vehicles. What replaced what, when and why lives in someone's notebook.
  • Support by phone. "Where's my vehicle?" "It's not updating." The support agent has seconds to find the customer.
  • Irregular payments. Cash, cheque, bank transfer, mobile wallet. Partial payments. Months of nothing, then a lump sum. The billing exists to answer one question: who owes what, since when, and what have they paid?

Who it's for

The buyer: one tracking company per deployment, with its own application, messaging and data. The scale it's designed for: hundreds to low thousands of vehicles, run by a small team.

WhoWhat they get
The team inside: admin, operations, technicians, support, billingOne back office: stock, installations, work orders, support, invoices
CustomersA web portal with their own vehicles, live and in history
DriversA mobile app (planned)
SubcontractorsPassword-protected share links to a group of vehicles, revocable in one tap
Partner systemsAn API, and signed webhooks (planned)
AI agentsThe same operations people use, through MCP, inside the same permissions (planned)

What does AI-native mean for a tracking platform?

Agents use the platform the way people do. They never sit between a tracker and the database.

Every capability in Pehra Pro is a typed domain operation, with permissions, an audit trail and, where it matters, a human approval. People reach those operations through the dashboard. Agents reach the same ones through MCP, and nothing else: no database, no NATS, no trackers, no credentials.

The agents

  • A copilot in the web and mobile apps. Ask "Why is Vehicle A late today?" and it pulls the vehicle's trips, stops, alerts, driver and job into one answer. A customer's copilot stops at the level the tracking company set for them: live status, history, or account.
  • Background agents that start on an event, a schedule or a deadline. They work on alerts that already fired: a tracker goes offline, and instead of five alerts the operations team gets one case, with the voltage history, the active job and the probable cause.
  • Claude and ChatGPT, connected over remote MCP with OAuth. Same tools, same limits. A user who loses access loses it there too, without reinstalling anything.
  • WhatsApp, because that's where the customers already are. A registered number asks a question and gets an answer, or a link to the right screen, within that number's access level.

The rules they work under

  • Rules say what happened. Agents say why. Speed over a limit is a line of code. "Is this pattern unusual?" is a question for an agent.
  • Autonomy is granted per action, not per agent. Opening an incident can be automatic. Immobilising an engine always waits for a person.
  • An agent can't give itself a tool, widen its vehicle scope or skip an approval. Everything it reads, a customer's WhatsApp message included, is data, not instructions.
  • Every run is recorded: what triggered it, what it looked at, which tools it called, what it decided, and how sure it was.
  • A kill switch stops any agent without stopping tracking or a single alert.

All of this is designed and specified. None of it is built yet. The core comes first, and it has to be complete without a model. That's the difference between AI-native and AI-dependent.

How do you take in thousands of unreliable devices without losing a point?

By assuming the worst about them. Clocks drift, coverage drops, frames get resent, and a gateway restart reconnects every device at once. Each of those has a mechanism below.

The deployables

  • Tracker gateway. The device edge, and the only place protocol code lives. It's separate because it holds thousands of long-lived TCP sockets: "The backend deploys often; the gateway must not restart with it, or every deploy causes a reconnect storm and a telemetry gap."
  • Fleet core. A modular monolith in TypeScript, one image started in different roles: api, worker and migrate today, delivery for webhooks later.
  • Map server. Its own vector tiles and geocoder, so vehicle coordinates never leave the deployment.
  • Fleet AI. The agent runtime and the MCP server, outside the core path. A shell today.

From device to screen

  1. Connect. A tracker opens a TCP connection. The first two bytes identify the protocol. The first frame must be a login with the device's IMEI.
  2. Claim. The gateway claims the device's session with a compare-and-set write in NATS KV, so exactly one gateway instance owns each device. Only then does it acknowledge.
  3. Publish. Each frame is acknowledged, then published to NATS JetStream as a canonical event. Event IDs are content digests, so a resent frame deduplicates. If NATS is down, events spool to disk.
  4. Attribute. A worker finds the vehicle the device was installed in at the moment the point was recorded and republishes it against that vehicle.
  5. Store. Positions go to TimescaleDB in batches of 500 events or one second. Each vehicle's current state is upserted in PostgreSQL, guarded so an old point can never overwrite a newer one.
  6. Show. The dashboard loads a snapshot, then a live WebSocket stream, coalesced per vehicle every 250 ms.
  7. React. Alert, geofence and trip engines each consume the vehicle stream. Their decisions are pure functions, applied under a per-vehicle lock, and they write only when state actually changes.

Storage

  • PostgreSQL for the business, with hand-written no-overlap constraints on every time-bounded association: ownership, installations, SIM assignments, trips.
  • TimescaleDB for telemetry, in its own database: hypertables in daily chunks, compressed after seven days, kept for twenty-four months.
  • NATS KV for state that can be rebuilt: sessions, leases, caches. Nothing else.
  • S3-compatible object storage for raw frames, attachments and exports.

Tenancy and security

  • One isolated deployment per tracking company. Customers are records inside it, and row visibility is enforced in the data layer, failing closed.
  • Passwordless sign-in codes that expire in five minutes and die after five wrong attempts.
  • Refresh tokens rotated by compare-and-set. Sensitive fields encrypted.
  • Customers never see an IMEI or any hardware identifier.
  • Every write is audited. Nothing is hard-deleted.

Decisions

Why a modular monolith, and not microservices? #

The first design had six services. The second had twelve deployables. Then I counted what each one cost: "Every additional deployable is a permanent tax on that team: another image to build, version, scan, monitor, and roll back." Six images bought no isolation that process roles don't already give, and they added a network hop to every dashboard read.

Now it's one codebase started in roles, plus the gateway, which earns its own image by holding sockets. Microservices solve an organisation problem. A small team doesn't have that one yet. The cost: the roles ship together, so every release has to be good for all of them.

Why is TimescaleDB its own database? #

Because "business data must come back in minutes and must never wait behind terabytes of position history." Telemetry writes shouldn't compete with the queries a support agent runs during a call, and the two need different retention. It's still PostgreSQL underneath: same SQL, same drivers, same migrations. The cost: no foreign keys between the two.

Why does the latest position live in PostgreSQL, not in the cache? #

The first spec kept it in NATS KV. The architecture review caught it: "A single latest-state row per vehicle is not a time series and is not disposable." It's relational now, upserted with a guard, and KV holds only what can be rebuilt.

Why attribute a point to a vehicle by device time? #

Because trackers flush old points late, sometimes after they've been moved to another vehicle. Looking up the installation at the point's own time means "a replacement takes effect on the next packet, with no cache, no invalidation flow, and it stays correct for buffered packets that predate the replacement." It works because PostgreSQL won't accept two overlapping installations.

Why acknowledge before publishing? #

Because "a duplicate costs less than a gap." A tracker that doesn't get its ack resends, and content-digest event IDs turn the resend into a no-op.

Should AI be in the ingestion path of a tracking platform? #

No. "Do not put AI in the telemetry ingestion path" is a rule in the spec. The core has to be complete and sellable without any AI at all. Positions don't need an opinion. They need to be correct.

AI sits on top, as its own deployable, behind one boundary: MCP. The cost: nothing in the core path learns. Every rule in it was written by a person and tested against frames.

Why does every document use the organisation's time zone? #

Because otherwise two people downloading the same report get different files, "and neither file would be wrong, which is worse than one of them being wrong."

Why run your own map tiles and geocoder? #

"Keeping vehicle coordinates out of URLs keeps them out of every log on the way." A third-party geocoder sees every address you look up. This one runs inside the deployment, and only the server calls it.

Why doesn't non-payment switch tracking off automatically? #

Suspending a customer is an explicit, audited decision made by an admin. Payments here arrive as cash, cheque, transfer or mobile wallet, often partial and often late. Cutting tracking on a schedule would punish a cash-flow pattern, not a bad customer.

The numbers

  • The first commit was the specification: 14,736 lines across six documents, and no code.
  • 186 commits between 30 August and 5 October 2026.
  • About 145,000 lines of source and 58,000 lines of tests, with about 3,180 test cases, and 43 golden frames across Teltonika and GT06.
  • 155 API operations across 18 modules, with 10 more modules specified.
  • 33,000 lines of specification, kept current as the code changes.

What's next

  1. Frames captured from real hardware, replayed through the conformance suite.
  2. Device commands, routed to whichever gateway instance owns the socket.
  3. Outbound webhooks, from the delivery role, with outbound network access locked down.
  4. The driver app, in Flutter, on the same maps engine.
  5. The AI layer, read-only first: the MCP server, the copilot, Claude and ChatGPT connections, and WhatsApp. Then the background agents.

What this project says about how I work

  • Specs first. The first commit was the specification, and the review that graded it came before any code.
  • The business model is the architecture. Stock, installations and payments are first-class, not bolted onto a map.
  • Hardware reality is written into the software, not discovered at the roadside.
  • AI-native, not AI-dependent. Agents get the same contracts, permissions and audit trail as people, and the core is complete without them.

Walkthrough: Pehra Pro runs as a dedicated deployment for each tracking company. Ask me for a walkthrough