Shipping software is easy. Distributing it securely is not.
Most teams start simple: you build a binary, upload it somewhere, and share a link. That works surprisingly well, until it doesn't. The moment you ship software to paying customers, especially in B2B environments, distribution stops being a file hosting problem and turns into an access control, security, and operations problem.
This post is about why that happens and why software distribution is often underestimated.
The naive setup (and why it's attractive)
For many teams, the first iteration looks familiar. A GitHub Release, maybe a private S3 bucket with presigned URLs, a simple download page, and links sent via email or dropped into documentation. And honestly, this is not wrong.
It is fast to set up, has almost no operational overhead, and works perfectly fine for open-source projects or early prototypes. You get versioning, hosting, and global availability almost for free.
The problem is not the approach itself. The problem is what happens when reality kicks in.
When things start breaking
Customers are not anonymous users
Once you have paying customers, you quickly realize that "anyone with the link" is not a valid access model. Suddenly you need answers to questions you never thought about before. Which customer is allowed to download which product? Do different customers have access to different versions? What happens when a contract ends or a trial expires?
At that point, distribution becomes a customer-scoped problem, not a public one. Every artifact needs context about who should have access and under what conditions.
CI/CD is the real consumer
In many setups, humans rarely download artifacts directly. Instead, downloads are triggered by CI pipelines pulling dependencies, update agents checking for new versions, or installers running unattended on customer infrastructure.
This changes everything. Automation requires long-lived credentials and non-interactive authentication. The blast radius when something leaks is much larger than a single user losing access. Links and shared secrets do not age well in this environment. What works for a human clicking a button once becomes a security liability when embedded in a script that runs thousands of times.
Links always leak
This part is uncomfortable, but important. Download links will be forwarded, pasted into support tickets, shared in
Slack channels, stored in CI logs, and bookmarked in browsers. If a link works, you should assume it will be shared, intentionally or not.
Security by obscurity only works until the first copy-paste. The question is not whether a link will leak, but what happens when it does.
Here's a concrete example I have seen more than once: a customer is on a time-limited trial, gets an installer link, and shares it internally.
Someone pastes it into a support ticket to speed up troubleshooting. A CI job logs it while building an update image. A week later the trial ends, but the link is still floating around in places you do not control. Now you need answers: who is still downloading, should they be allowed to, and can you cut off access without breaking everyone else?
"Just use presigned URLs" is not enough
Presigned URLs are often presented as the solution, and they do solve an important part of the problem. They protect your storage backend by ensuring nobody can access the underlying bucket directly.
What they do not provide is customer context, revocation, auditability, or authorization beyond time limits. A presigned URL answers "is this link still valid?" It does not answer "is this customer allowed to download this artifact?"
Storage can validate a signature. It cannot make distribution decisions like whether a release is actually published, whether a customer is entitled to that product, or whether access should be suspended right now.
The distinction matters. Presigned URLs solve storage security, not distribution security. They are a necessary building block, but not a complete solution.
The hidden requirements nobody lists upfront
Once you start looking closer, a surprisingly consistent set of requirements appears:
- Customer-scoped access control: different customers should see different artifacts.
- Product-level entitlements: model what each customer has purchased or is allowed to access.
- Release lifecycle boundaries: keep drafts private and make publishing explicit.
- Time-bound availability: trials, expiring contracts, and phased rollouts.
- Revocation and suspension: end entitlements or suspend customers without rotating credentials everywhere.
- Auditability: support cases and compliance questions need clear answers.
- Automation-first authentication: machines are usually your primary consumers.
- Minimal operational overhead: you still want this to be boring to run.
None of these are exotic. What is rare is treating them as first-class concepts instead of bolting them on later when the pain becomes unbearable.
Why this pushed me to rethink release distribution
I ran into these problems repeatedly while working on B2B and self-hosted software. Existing tools were either optimized for humans rather than automation, deeply UI-driven with APIs as an afterthought, tightly coupled to licensing or DRM systems I did not need, or simply hard to integrate cleanly into CI/CD workflows.
I was not looking to invent a new category. I just wanted a model where the implicit requirements become explicit, where access control, entitlements, and auditability are part of the design from day one, not features added under pressure.
That line of thinking eventually turned into an API-first, self-hosted release backend called Releasy. But the ideas themselves apply far beyond a single project.
Key takeaways
- Once you have customers, distribution becomes an access control problem.
- Once you have automation, it becomes an operations and security problem too.
- Presigned URLs help, but do not replace customer context, revocation, and auditability.
What's next
In the next post, I will look at what an API-first release platform actually looks like, and why optimizing for automation changes many design decisions.
This post is part of a series about building a self-hosted, API-first release platform. The project that grew out of this work is open source and called Releasy.
Top comments (0)