Distribution Platform — how it came to be
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.