Consult. Engineer. Ship.
0%
Assembling the pieces

Nobody is audited against one rulebook. — A case study

A signup becomes a running LMS.

Before

Every customer was a project. Their branding, their domain, their course taxonomy and their payment arrangement were configuration decisions somebody made on their behalf, in code, before the customer could put a single video in. The platform could only grow as fast as the team could set customers up.

Now

A company provisions its own tenant and configures the things that used to be a build: identity, domain, theme, course structure, roles, and how it charges — whether that is selling courses publicly, issuing them internally at no cost, or both at once. What used to be a bespoke engagement is now a form the customer fills in themselves.

  • Tenant provisioned with its own domain, branding and roles
  • Course taxonomy defined by the customer, not by the platform
  • Payments connected per tenant: public sale, internal issue, or both
  • Identity wired to the company's own SSO

Raw material becomes something a regulation can point at.

Before

Compliance material arrives as whatever the company already had: hours of recorded sessions, PDF policies, scanned handbooks, intranet articles. It is uploaded, it sits there, and it is findable only by whoever remembers where they put it. At a hundred thousand items, that is the same as not having it.

Now

Everything entering the platform is normalised into a learning object before it is anything else. Video is transcoded and transcribed; documents are extracted and chunked; every item is tagged and indexed. Around 100,000 videos, documents and articles are searchable — not by filename, but by what is actually said inside them.

  • Video transcoded and transcribed; documents extracted and chunked
  • AI tagging proposes topics, and a person confirms them
  • Everything indexed for search, including the transcript
  • Around 100,000 videos, documents and articles, all searchable

A completion becomes evidence.

Before

The system knew a course was finished. Proving compliance meant exporting that, opening a separate requirements list, matching one against the other by hand, working out which duties were now overdue, and assembling the result into something an auditor would accept. That work was redone from scratch every audit, and it was only ever as right as the person doing it was tired.

Now

A learning object is mapped to the requirements it discharges — plural, because one module routinely satisfies duties in several regimes at once. Completing it settles every one of them, starts each requirement's own renewal clock, and writes an evidence record that is never edited afterwards. The audit pack is a query, not a project.

  • One learning object mapped to every requirement it discharges
  • A completion settles all of them at once
  • Each requirement renews on its own cadence, not a shared reminder
  • Evidence written append-only; the pack is exported, not reassembled

Most learning platforms model a course and a completion. Compliance does not work that way: a company answers to several regimes at once, each with its own requirement list, its own renewal clock and its own idea of what proof looks like — and one training module often discharges a duty in three of them. We built the platform companies use to stand up their own compliance LMS: their content, their courses, their pricing, their frameworks, on shared infrastructure, with around 100,000 videos, documents and articles searchable underneath it.

The product started from a mismatch. An off-the-shelf LMS knows that a person finished a course. An auditor does not ask that. An auditor asks whether every person holding a particular role currently satisfies a particular requirement, and asks to see the evidence — which means the completion has to be mapped to the requirement, the requirement to the framework, and the framework to the date it next falls due.

Because no LMS holds that mapping, companies rebuild it by hand. Completions are exported to a spreadsheet, matched against a requirements list somebody maintains privately, and reassembled into an evidence pack every time an audit comes round. The training platform and the thing the training exists to prove live in two different places, and only one of them is a system.

The second problem was that no two customers wanted the same LMS. Different material, different course structures, different branding and domains, different ways of charging — some selling courses to the public, some issuing them internally at no cost, some doing both. Building each one bespoke does not scale, and a single rigid product fits nobody.

So it was built as a platform rather than a product. A company arrives, provisions its own LMS, pipes in its material, defines its course structure, connects its payments, and maps what it teaches to what it is obliged to prove. The compliance graph underneath is the part that is genuinely shared; everything a customer sees above it is theirs.

The decisions

What was chosen, why it was chosen, and what it cost.

  1. One schema with a tenant key on every row, not a database per customer.

    A customer can carry a hundred thousand content items and thousands of learners. A database each multiplies every migration, every connection pool and every backup by the number of customers, and the platform's whole promise is that adding a customer costs nothing.

    The cost: Every query has to be tenant-scoped, and trusting callers to remember is how tenants leak into each other. Scoping is enforced one level down, in the data layer, so a query that forgets simply cannot be written.

  2. Search isolated per tenant, but not one cluster per tenant.

    Two customers use the same words to mean different things, so relevance has to be tuned per tenant rather than globally. And a search result that crosses a tenant boundary is not a bug, it is a disclosure.

    The cost: Large tenants get their own index; small ones share one behind a routing key. That is two shapes to operate instead of one, and a tenant that grows has to be migrated between them.

  3. Ingestion is asynchronous and each stage is idempotent.

    A multi-gigabyte upload cannot happen inside a request, and transcoding, transcription, extraction and indexing each fail for their own unrelated reasons. Stages that can be retried in isolation mean one failed transcript does not cost the whole upload.

    The cost: The system is eventually consistent, so an item exists before it is searchable. The interface has to say so honestly — a 'processing' state a user can see beats a file that is silently missing from results.

  4. Compliance modelled as a many-to-many graph, not a list of courses.

    This is the whole product. One module discharges duties in several frameworks; one duty can be satisfied by any of several modules; both sides change when a regime is updated. A course list cannot express that, which is precisely why the spreadsheet existed.

    The cost: It is harder to author and harder to explain than a course list, and it needed a mapping interface of its own before anybody could use it. Customers have to do real thinking up front to get the thing they wanted.

  5. Evidence is append-only. Records are superseded, never edited.

    An audit trail that can be quietly corrected is not an audit trail. If a completion was recorded and later found wrong, both facts matter, and the sequence of them matters most.

    The cost: Storage grows and nothing is ever reclaimed, and 'what is true right now' becomes a projection over history rather than a row you can read. Current state is derived and cached instead of stored.

  6. Two money flows, kept separate: the platform bills the company, the company bills its learners.

    They are genuinely different relationships. Platform subscription is ours; course sales are the customer's revenue and must land in the customer's account, under the customer's tax arrangements, without passing through us.

    The cost: Two billing models, two sets of webhooks and two failure modes to reason about — and a tenant whose own payment connection lapses must keep working internally while its public sales stop.

  7. Laravel for the core, Next.js for the surfaces.

    The core is policy, queues, scheduling and long-running jobs — work Laravel is direct about. The surfaces need server rendering and per-tenant theming at request time, which is the thing Next.js is for.

    The cost: Two runtimes to deploy and one contract between them that has to be versioned deliberately, because the surfaces and the core do not ship at the same moment.

  8. AI retrieves and tags. It does not decide whether a duty is discharged.

    Finding the right four minutes inside a hundred thousand items is exactly what a model is good at. Judging whether a legal obligation has been met is a liability, and a confident wrong answer there is worse than no answer.

    The cost: Less automation than a demo would promise. Mappings and confirmations stay human, and the product is slower to set up than one willing to guess on the customer's behalf.

Built on

Laravel
The compliance core, the ingestion queues and the scheduling behind renewals.
Next.js
The tenant console, the learner surfaces and the per-tenant theming.
TypeScript
The contract between the surfaces and the core, and everything in front of it.
Elasticsearch
Search across roughly 100,000 items, isolated per tenant.
AI
Transcription, topic tagging and retrieval — proposing, never deciding.
Stripe
Platform subscriptions, and each tenant's own course sales kept separate from them.
Object storage & transcoding
Where the video actually lives, and what makes it playable.

Talks to

  • SSO / identity providers
  • HR systems
  • Stripe
  • Email & SMS

What changed

  • A company provisions its own LMS instead of commissioning one
  • Around 100,000 videos, documents and articles, all searchable
  • One module can discharge duties across several frameworks at once
  • Renewals run on each requirement's own clock rather than a reminder somebody sets
  • An audit pack is exported rather than reassembled by hand
  • Evidence is append-only, so the record of what happened cannot be quietly rewritten
  • Tenants keep their own domain, branding, course structure and pricing
  • Search, content and learner data never cross a tenant boundary

If you are proving something in a spreadsheet, the spreadsheet is the system.

Platforms like this one start wherever the real work has quietly moved out of the software and into somebody's file. Describe what you currently have to reassemble by hand, and you leave the first conversation with a scope, a budget and a ship date.

Send MessageAll work

We use cookies for analytics, to understand how visitors use this site. Decline and we won’t load them.