← Back to all notes

Distribution Platform — how it came to be

Published:

A central licensing and distribution server for my own software products — born from the frustration of cobbling together licensing, delivery and customer management from scratch for every product.

Why at all

At some point I had more than one product running as a Docker container on customer servers. And every time the same questions: which customer gets which version, which modules are unlocked, how does the image even get onto their server? I hacked that together per product at first — and regretted it every time.

So I wanted one place that knows it centrally: customers, products, modules, licenses. The customer instance asks "what am I allowed to do?" at runtime and gets a complete answer back. No customer self-service, no frills — an admin tool that does the bookkeeping for my own deliveries.

How it came to be

I didn't jump straight into code but started with a concept and a set of Architecture Decision Records. Sounds like overhead, but it kept the scope off my back: first the license server and the bare CRUD for customers/products/modules/licenses, then everything else layer by layer.

  • A small CRM, because I wanted notes and reminders per customer.
  • A link to my support ticketing, so tickets land with the right customer.
  • A read-only statistics dashboard showing which versions and modules are actually running out there.
  • An optional customer portal with magic-link login — read-only, single-use, rate-limited.
  • An onboarding mailer that sends out credentials.

The centerpiece is the link to my container registry: creating a customer automatically provisions a robot account that may pull exactly their images — nothing more.

What went well

  • The clean layering (API → service → repository → domain) paid off the moment the second and third integration arrived.
  • Optional services run in "disabled mode" when you don't configure them. So the thing boots even without registry, ticketing and SMTP.
  • Soft-delete and a consistent audit log everywhere — with licenses you really want to know who changed what and when.
  • Real Postgres containers in the integration tests instead of mocks. Catches the bugs that matter.

What didn't go so well

  • The scope grows almost by itself: CRM, support, portal, mailer — each sensible on its own, a lot together. Without the ADRs as a brake it would have sprawled.
  • The registry integration was fiddlier than expected. Robot-account lifecycle, the report from the vulnerability scanner, null-tolerant queries — lots of small edges.
  • Security hardening is never "done": ripped out an outdated crypto library, slimmed the images, armed the scan gate. That stays ongoing work.