AmanERPAmanERP
Blog पर वापस
OPEN SOURCE LICENSING

The AGPL Trap That Forced an Identity Provider Migration

Running an unmodified AGPL binary can be safe. Importing its Go module is a different risk. That distinction cost me six weeks.

Niraj Kumar2026-06-175 min read
A horizontal timeline with four marks — a license flipping upstream, the dependency adopted, the audit that catches it, and a wide migration span — showing the cost of the unasked license question.

Six weeks. Roughly 40-50 files. That was the price of treating "open source" as enough, and of not asking the one question that matters for copyleft:

How does this dependency enter the product?

The Audit That Found the License Problem

I adopted ZITADEL as the identity provider in December 2025. The ADR recorded the properties I cared about then: self-hosted, open source, modern, tenant-aware. It did not force the license-family x integration-mode question.

By the February 2026 vendor-coupling audit, that omission mattered. ZITADEL v3 had moved from Apache 2.0 to AGPL 3.0, and my codebase had accumulated 15+ files with hardcoded ZITADEL claim names, a 760-line setup script, and database columns named zitadel_org_id cascading through roughly 55 files via SQLC code generation.

I had not merely chosen an identity provider. I had shaped schema, token claims, generated code, and operational scripts around it.

That left one question I could not hand-wave inside a closed-source SaaS: was this dependency safe in the way I was using it?

The Trap Most Engineers Walk Into

The usual AGPL conversation starts with the network-use clause. If you modify an AGPL program and let users interact with it over a network, AGPL section 13 says you must offer the source for that modified version. That is the SaaS loophole AGPL was written to close.

The usual response is equally familiar: "We are just running it behind a network boundary. We are not modifying it. We are fine."

That sentence is half right.

AGPL section 13 is about modified versions. Running an unmodified AGPL program as a separate service is not the same thing as publishing your proprietary code. The license text itself separates mere execution from conveying a copy.

But that is not the trap that bit me.

The trap is linking.

Go Static-Links. That Changes Everything.

GPL/AGPL section 0 defines "modify" to include creating a work based on the program. The FSF FAQ treats static or dynamic linking as a combined-work case.

Go, by default, builds imported modules into one binary. So a single import of an AGPL Go module is not the same risk as calling an unmodified service over HTTP. It moves you into the combined-work case: the one lawyers and conservative license policies treat most seriously.

JavaScript bundles can create the same shape when they inline dependency code.

That is why the license name alone never answers the adoption question. The answer lives in the integration mode.

The Two-Axis Rule

I formalized this as ADR-086 after the ZITADEL migration because the old rule - "AGPL/GPL = forbidden" - was both too broad and too vague. It blocked genuinely safe internal tooling, while still failing to name the product risk precisely.

The correct rule has two axes: license family x integration mode.

  • Mode A: internal tooling only, run as a standalone binary, never shipped, never linked. AGPL/GPL verdict: allowed with documented allowlist.
  • Mode B: arms-length service, separate process, product talks to it over a network API. AGPL/GPL verdict: requires IP-counsel review.
  • Mode C: Go module import, npm bundle, same binary. AGPL/GPL verdict: hard blocked.
  • Mode D: shipped to customers, or modified and network-exposed to non-employees. AGPL/GPL verdict: hard blocked.

The mode the standard defense usually describes - running an unmodified service behind a network boundary - is Mode B. That boundary may be defensible, but it is still the wrong place for a pre-revenue closed-source SaaS to take heroic license risk when permissive alternatives exist.

Google's AGPL policy is blunt for the same reason: the modification and linking distinctions are fact-specific, easy for engineers to get wrong, and costly when they are wrong.

What We Did

I replaced ZITADEL with Logto OSS (MPL-2.0). MPL-2.0 is file-level copyleft: if I modify MPL files, those changes have sharing obligations, but the license does not spread across my whole proprietary binary. No AGPL network-use clause. No Go-linking trap for the product.

The migration cost six weeks and touched roughly 40-50 files. That was not the price of being careful. It was the price of being careful late, after identity had leaked into schema, scripts, token claims, and generated code.

Where the rule gets too blunt

The opposite failure is real too. A flat "AGPL is forbidden" rule reaches the right answer for linked product code, then keeps going until it blocks safe tooling. I hit that next: the old rule vetoed a standalone internal CLI that never shipped, never linked, and never entered the product runtime.

That was not risk management. That was a velocity tax.

Permissive licenses are safe everywhere. AGPL linked or bundled into product code is blocked. AGPL run as an unshipped, unlinked internal tool can be allowed with a documented allowlist. Scope the alarm to the quadrant that earns it.

The Rule Worth Keeping

The danger is not the license family. The danger is the integration mode.

Permissive licenses are safe in every cell. AGPL linked or bundled into your product binary is blocked, even if you never modify it. AGPL as an internal-only CLI you invoke as a separate process can be allowed with documentation. The scanner catches the linked case automatically; judgment catches the rest.

A dependency review that asks only "what license?" and not "how will this be integrated?" is incomplete. The rule is worth writing before you need it. Mine arrived after the migration.

The question that decides the product case is simple: does this ever touch go.mod or package.json for production use?

If yes, the answer to AGPL is no.

About AmanERP

AmanERP is an AI-native ERP for SMBs, built in public, six-week migrations included. Aman means peace: calm software stands on dependencies you can live with. www.amanerp.com