Skip to content

Architecture ​

Rangefront Waste is a modular monolith with separate web and mobile clients. It's sold as a one-off licence with source code, self-hosted or hosted by Rangefront Labs, and white-labelled per business.

Parts ​

FolderWhat it is
apps/backend-laravelLaravel 13 API (PHP 8.3 or newer). Tenancy, queues, reports, PDFs, billing, council reporting, sync endpoints
apps/web-aureliaAurelia 2 and Vite operations console, plus the Admin area and the public customer pages (pay, quote, tracking, portal)
apps/mobile-expoExpo SDK 56 offline-first field app. Read its AGENTS.md first
shared-contractsThe API envelope schema, the generated OpenAPI file and generated TypeScript types
infra/local-devDocker for Postgres/PostGIS, Redis, S3 (SeaweedFS) and Mailpit

npm workspaces cover the web and mobile apps. The Laravel app is driven with Composer, through root npm scripts.

Runtime ​

text
Aurelia console   -> Laravel API -> PostgreSQL/PostGIS (MySQL 8 also works)
Expo field app    -> Laravel API -> same database
Laravel queues    -> Redis, run by Horizon
Laravel broadcast -> Reverb (websockets)
Files, PDFs, photos -> S3-compatible object storage
Field data on the phone -> Expo SQLite and an outbox

The console and the API are served from the same address. APP_URL has to be that address, because pay links, tracking links, quote links and portal links are built from it and open pages of the console.

Tenancy ​

Tenancy is shared-schema:

  • Every operational table carries organization_id.
  • Requests under /api/v1 need an X-Organization-Id header. The ResolveOrganization middleware checks the signed-in user has an active membership.
  • That middleware is context, not the security model. Policies, the permission: middleware and service actions still check permissions, and every query is scoped by organization.
  • A user can belong to several organizations, each with its own role.
  • Public links (pay, quote, tracking, portal) use hashed random tokens, not ids.

Backend layout ​

text
app/Http/Controllers/Api/V1   one controller per resource or action
app/Http/Requests/Api/V1      Form Requests, one per mutation
app/Http/Middleware           ResolveOrganization, RequirePermission, DemoReadOnly
app/Policies                  TenantResourcePolicy maps resources to permissions
app/Services                  the business logic: workflows, billing, messaging, PDFs
app/Services/Ai, PhoneAgent   Claude gateway and the phone agent
app/Services/Messaging        email and SMS provider catalogue and senders
app/Services/Routing          route optimisation providers
app/Jobs                      queued work
app/Events                    broadcast events
app/Extensibility             hooks, plugins and themes
app/Support/PermissionCatalog the permission keys
routes/api.php                the API (plus api-team.php and extensibility.php)
routes/console.php            artisan commands and the schedule

Most simple resources go through TenantTableController, which scopes reads and writes by organization, paginates, soft-deletes and writes audit records. Workflow actions (job status, report issue, invoice send, payment) go through services so rules are in one place and every path audits.

Workflows to know:

  • Jobs move through JobWorkflowService. Invalid moves are refused. After a move the service fires customer messages, webhooks, time entries and report issue.
  • Compliance reports move draft, generated, issued, submitted (or superseded). PDFs are immutable once issued. Generic PATCH refuses workflow-owned fields.
  • Invoices are edited only while draft. Sending, paying, PDFs and accounting sync are workflow services.
  • Messages are dispatched from rules by CustomerMessageService, with a dedupe key so each goes once.

Queues and the scheduler ​

Jobs run on named queues: critical, reports, routing, uploads, notifications, council, imports, default, plus documents (PDFs), integrations and webhooks. See install.

The scheduler runs every minute and starts the commands listed in install.

Realtime ​

Reverb broadcasts organization-scoped events (job changes, notifications, team messages, monitoring) on private channels. The console listens through Laravel Echo. Channel access is checked against organization membership, and the team channel also needs team.read.

Files ​

Uploads go directly to object storage with a signed address. Object keys are scoped under organizations/{organization_id}/.... Each upload is hashed and scanned (a built-in check, or ClamAV) before it's marked ready. Generated PDFs are stored the same way with their hashes.

PDFs ​

PDFs come from HTML templates rendered through headless Chromium in production (PDF_RENDERER, PDF_CHROMIUM_BINARY), queued as render jobs with retries. Themes and tenant templates are Twig; see themes and document templates.

Field app ​

The Expo app keeps routes, jobs, forms, photos and time in SQLite and queues changes in an outbox. It pulls and pushes through /mobile/sync/pull and /mobile/sync/push, with conflict review on the device. Devices are enrolled and can be revoked from the console.

Extensibility ​

Hooks, plugins and themes let a developer change behaviour without editing core, so upgrades don't overwrite the work. See extending, themes and plugin fields.

Contracts ​

The OpenAPI file is generated from the Laravel routes (npm run contracts:openapi) and TypeScript types from it (npm run contracts:types, or both with npm run contracts:generate). Commit the result.

UI direction ​

The console follows the office app built for a single operator, without its branding: a deep petrol frame around one white work surface, compact page headers, flat ruled directory rows, right-hand record sheets, and a schedule where work stream is colour and dispatch state is shape. It stays white-label: the frame shows the business's trading name beside the Rangefront mark. Avoid generic SaaS hero layouts, nested cards, decorative shadows and blue-purple gradients. AGENTS.md lists the style vocabulary.

A$125 a month, or A$9,450 once with the source code. Contact us.