All workIn production

Case file

SMV4

The system a cross-border freight forwarder runs on, rebuilt from the ground up while the old one kept shipping.

Client
Siam Max Co., Ltd.
My role
Sole developer, architecture to operations
Period
2021 – present
TypeScriptNestJS 11MongoDBReact 19Tauri 2RustExpo / React NativeCloudflare R2nginxsystemd

In one paragraphFor engineersFor hiring managersThe short version

Siam Max's shipping, billing, accounting, HR, and payroll run on software I designed, built, and operate alone. It replaced a first-generation system I also built, without stopping daily operations, and it has been in production since.One NestJS API with about 35 domain modules behind a Tauri desktop app, two web portals, and two Expo mobile apps. A full accounting engine with GL auto-posting, FX revaluation, and Thai tax reports, a dual-country ledger dimension, one carrier abstraction over DHL, Thailand Post, and DMS, all self-hosted on nginx and systemd.Siam Max's shipping, billing, accounting, HR, and payroll run on software I designed, built, and operate alone. It replaced a first-generation system I also built, without stopping daily operations, and it has been in production since.A shipping company working between Thailand and Myanmar needed one system for everything from waybills to payroll. I built it. Then I built it again, properly.

Choose a lens in the header to reorder this page for your reading.Engineer lens: the engineering story comes before the outcome.Hiring lens: the outcome comes right after the problem; the engineering story follows.Curious lens: the story first, the engineering detail last.

Situation

The problem

Siam Max is a freight forwarder moving cargo between Thailand and Myanmar. When I joined in December 2021, I built SMV3: an Express and React application that became the company's daily operating system. Shipment entry, carrier rating, waybills, agent and sales billing, invoicing, payments, and double-entry accounting all ran through it, in three languages, for admin, accounting, agent, and sales roles. It talked to DHL's API for shipment creation and tracking, priced shipments across DHL, Thailand Post, DMS, and ESS with per-agent rate cards, and reconciled DHL invoices every night.

It worked, and it kept working for years. But it had grown the way first systems do: one JavaScript codebase, features added in the order the business asked for them, and business rules living in places you had to remember. When the company wanted full accounting with Thai tax reporting, HR and payroll, biometric attendance, and a second country's books, patching the old system would have made it more fragile. It needed a foundation that could take the weight.

Delivery

What I built

SMV4 is a single typed API serving every client the company uses.

  • API. NestJS 11 and TypeScript, about 35 domain modules. JWT with rotating refresh tokens and TOTP two-factor authentication.
  • Desktop app for staff. Tauri 2 with a React 19 front end and a Rust side. Fingerprint attendance from a biometric reader, and signed auto-updates so fixes reach every workstation without a visit.
  • Web portals. One for administrators, one for agents.
  • Mobile. Two Expo (React Native) apps.
  • Accounting. General-ledger auto-posting from operational documents, bank reconciliation, FX revaluation, and Thai CIT, VAT, and withholding-tax reports.
  • Dual-country ledger. A Thailand / Myanmar dimension across the books, so each side reports on its own and together.
  • HR and payroll.
  • Carriers. One abstraction over DHL, Thailand Post, and DMS.
  • Operations. Self-hosted on Linux: nginx at the edge, systemd services, Cloudflare R2 for files, versioned deploy configs.
35
API domain modules
5
client apps on one API
Under the hood

The engineering story

One API, many clients. Every client, from the desktop app to the agent portal to the mobile apps, talks to the same NestJS API. Each business domain is its own module, so shipping, accounting, HR, and payroll have their own boundaries instead of sharing one tangle of routes. Auth is JWT with rotating refresh tokens and TOTP two-factor, the same for every client.

Why a desktop app. Staff work at fixed stations with hardware attached, starting with the fingerprint reader used for attendance. Tauri gave me a small native shell with a Rust side for the hardware and a React UI that shares its thinking with the web portals. Signed auto-updates mean I ship a fix once and every workstation picks it up.

Money is posted, not typed. Operational documents create journal entries automatically. An invoice posts revenue and receivables, a payment clears them, and revaluation adjusts foreign-currency balances. Bank reconciliation matches the ledger against statements, and the Thai tax reports are derived from the ledger rather than maintained by hand.

Two countries, one set of books. The ledger carries a country dimension, so the Thai and Myanmar sides of the business can be reported separately and consolidated without running two systems.

Carriers behind one interface. DHL, Thailand Post, and DMS sit behind one carrier abstraction, so rating and shipment creation do not care which carrier is on the other end, and adding one does not touch the rest.

Running it. The whole thing is self-hosted: nginx at the edge, each service under systemd, files in Cloudflare R2, and the deploy configuration versioned alongside the code.

Impact

What it changed

Siam Max's operations run on SMV4 today. SMV3 ran the business for years before it; the replacement happened while cargo kept moving, and the same staff who used the old system use the new one.

For the business, the change is that shipping, billing, accounting, tax reporting, HR, and payroll are one system with one set of numbers, instead of several tools and a lot of copying between them. For me, it is a system I can keep extending safely, because the boundaries are in the code and not in my memory.