Merge pull request 'docs: propose ADR 0051 module package licensing' (#73) from docs/adr-0051-module-package-licensing into main
CI / verify (push) Successful in 1m6s
CI / infrastructure-smoke (push) Successful in 37s
CI / migration-smoke (push) Successful in 1m56s
CI / image-build (push) Successful in 12s
CI / deploy-smoke (same-origin) (push) Successful in 42s
CI / deploy-smoke (split) (push) Successful in 1m22s

Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/73
Reviewed-by: The_River <[email protected]>
This commit was merged in pull request #73.
This commit is contained in:
The_River
2026-10-07 23:29:19 +00:00
2 changed files with 208 additions and 0 deletions
@@ -0,0 +1,207 @@
# 0051: Module packages declare their own license and carry its text
- Status: Proposed
- Date: 2026-10-07
- Amends: [0049](0049-module-package-v1.md) — every Module Package v1 artifact gains an
authoritative package-level license declaration and a `LICENSE` payload. `packageFormat` stays 1.
- Related: [the repository license](../../../README.md#license),
[ADR 0048](0048-generic-image-and-installed-module-storage.md),
[ADR 0050](0050-module-database-abi-v1.md)
## Context
ADR 0049 makes a module release one immutable, self-contained package and already requires
`THIRD_PARTY_NOTICES` for code bundled from third-party dependencies. It deliberately says that no
package is a distributable release until license and notice collection is proven. The implemented
packager now generates those notices from the dependency code it actually bundles, but Module
Package v1 still does not declare the module's own license or carry the module's license text.
Those are different obligations. `THIRD_PARTY_NOTICES` excludes both the module itself and bundled
Ekklesia-owned packages. The source module's development `package.json` is not copied into the
package: the packager generates a minimal runtime `package.json` instead. Consequently, an archive
can preserve every third-party notice and still lose the authoritative declaration and text for the
work being distributed.
The repository and its workspace packages declare `AGPL-3.0-or-later`, and the repository carries
the GNU AGPL version 3 text in `LICENSE`. The canonical module template also declares
`AGPL-3.0-or-later` in its source `package.json`. Official Ekklesia modules use that license today,
but a packager cannot assume that every module has the platform's license. A module release must say
what license its own publisher actually declared and must carry the corresponding material.
The current `ekklesia.package.json` contract is strict: a reader refuses unknown fields. Adding a
required field therefore makes pre-change and post-change readers reject one another's artifacts.
That is intentional here, not a reason for `packageFormat: 2`. ADR 0049 has not admitted a
distributable v1 release without proven license handling; this decision completes that still-open v1
requirement before real modules are distributed. The packager and verifier must adopt the tightening
together, and strict readers fail closed during that cutover rather than silently misreading a
package.
## Decision
### The package declares the module's own license once
Every Module Package v1 `ekklesia.package.json` has a required top-level `license` field containing
the module's SPDX license expression:
```json
{
"packageFormat": 1,
"module": { "id": "example", "version": "0.1.0" },
"license": "AGPL-3.0-or-later"
}
```
`ekklesia.package.json.license` is the authoritative package-release declaration. The expression is
not repeated in `ekklesia.module.json` or in the generated runtime `package.json`. The module's
source `package.json` is a packaging input, not release metadata inside the finished artifact.
The source module must explicitly declare its license in its own project metadata. The canonical
packager reads the normal string `license` field from the source module's `package.json`, validates
it as a supported SPDX license expression, and preserves that declaration in
`ekklesia.package.json`. It refuses a missing, empty, `UNLICENSED`, legacy object-shaped, malformed,
or unsupported declaration. It never substitutes the Ekklesia platform's license and does not try
to decide whether a publisher chose an appropriate license.
For official Ekklesia modules today the declaration is `AGPL-3.0-or-later`, matching the repository
license. This is a fact about those modules, not a default the packager invents for other modules.
### The package carries the applicable license material
Every distributable Module Package v1 contains a regular file at this canonical path inside the
module directory:
```text
LICENSE
```
`LICENSE` contains the license material applicable to the module release. It is an ordinary payload
file under ADR 0049:
- it appears exactly once in the strict `files` list in `ekklesia.package.json`;
- its byte size and SHA-256 are recorded there;
- the archive SHA-256 covers it and the metadata that records it;
- it is immutable for that release and changes only by producing a different archive.
The license bytes must be available as an explicit source or build input. The canonical source is a
module-owned `LICENSE` file at the module source root. Tooling may also accept an explicitly named
license-text input when a workspace layout needs one, but it must place those bytes at the package's
canonical `LICENSE` path. It must not search ancestor directories and silently adopt a repository
license, generate project-specific license material, or download text from the network. The
in-repository template implementation may later add its own `LICENSE` input or explicitly select
the repository's AGPL text; this ADR does neither.
The packager validates that both inputs exist: the declared SPDX expression and the applicable
license text. It preserves the supplied text as deterministic package input. It is not a license
compatibility engine and does not offer legal advice.
### `THIRD_PARTY_NOTICES` remains a separate concern
`THIRD_PARTY_NOTICES` continues to describe third-party packages actually bundled into or included
with the module: their identities, versions, declared licenses, uses, and required license and notice
texts. It is not the module's own license file and cannot satisfy the `LICENSE` requirement.
The module itself is not added to `THIRD_PARTY_NOTICES`. A first-party module is not duplicated there
merely because it uses the same AGPL license as Ekklesia. `LICENSE` and `THIRD_PARTY_NOTICES` both
remain required because they describe different parts of the release.
### Bundled Ekklesia code
A module package may bundle Ekklesia-owned SDK, contracts, database adapter, UI, and related code.
`incorporates` continues to record the exact versions of those Ekklesia components. It is an
inventory and compatibility fact, not a second authoritative declaration of the module's license.
Today's official module packages and the Ekklesia components they incorporate are all
`AGPL-3.0-or-later`. Their package `LICENSE` therefore carries the applicable AGPL text once. The
package does not need one identical copy per incorporated Ekklesia component, and those components
do not move into `THIRD_PARTY_NOTICES` merely to obtain duplicate text.
This ADR does not decide every future combination of a community module's license with differently
licensed incorporated code. Publishers remain responsible for their choices and obligations. The
packager preserves declared information and required material; it does not judge broad license
compatibility.
### Packaging and verification fail closed
The implementation that follows this ADR must make the packager refuse a module when:
- its source project has no acceptable explicit SPDX license expression;
- its license-text input is missing or is not a regular file;
- producing the package would omit `LICENSE` from the strict payload.
The package verifier must refuse an artifact when:
- `ekklesia.package.json.license` is missing, malformed, or outside the expression syntax the
contract supports;
- the `LICENSE` payload is missing;
- `LICENSE` is absent from the exact `files` list; or
- its listed size or SHA-256 does not match its bytes, as with every other payload file.
This ADR does not implement those checks. The contracts, packager, verifier, fixtures, template,
and package proofs must change together in the implementation PR.
### Determinism and runtime metadata
`LICENSE` introduces no timestamp, generated host path, network lookup, or other environmental
input. The deterministic archive rules in ADR 0049 apply to it unchanged.
The generated runtime `package.json` remains minimal and is not authoritative for licensing. There
is no runtime need to add a `license` field there. `ekklesia.package.json` is the release metadata,
while the source module's `package.json` only supplies the publisher's declaration to the packager.
### Package format remains 1
`packageFormat` remains **1**. This is an additive tightening that completes ADR 0049's licensing
requirement before the first distributable Module Package v1 release. It does not change the archive
encoding, safety rules, target model, installation layout, or meaning of any existing field.
The strict metadata schema is preserved. A verifier that predates this decision refuses the new
unknown `license` field; an updated verifier refuses an old package that lacks it or lacks `LICENSE`.
Both outcomes are safe. There is no installed or published v1 release compatibility promise to
preserve because ADR 0049 explicitly withheld distributable-release status pending this work.
ADR 0050's database ABI decision is unaffected. Whether a package uses database ABI v1 or the legacy
database facade has no bearing on its license declaration or payload.
## Consequences
- A Module Package v1 release is self-describing about the module's own license and carries the
corresponding material without consulting its source repository or the network.
- Operators can audit the release declaration in `ekklesia.package.json`, the module's license in
`LICENSE`, and dependency obligations in `THIRD_PARTY_NOTICES` without conflating them.
- Module publishers must make both the SPDX declaration and license text explicit packaging inputs.
- The template and every package fixture must be updated before packaging succeeds, but only in the
later implementation PR.
- Existing development artifacts produced before this tightening are intentionally rejected and
rebuilt; none was a distributable v1 release under ADR 0049.
- Identical AGPL text is carried once for today's official module and incorporated Ekklesia code.
## Alternatives
- **Use only `THIRD_PARTY_NOTICES`.** Rejected: it describes third-party dependencies and explicitly
excludes the module itself and Ekklesia-owned incorporated packages.
- **Make the generated runtime `package.json` authoritative.** Rejected: it is deliberately minimal
runtime discovery metadata, and copying the declaration there would duplicate release metadata.
- **Copy the platform's license automatically.** Rejected: the platform cannot declare a module's
license for its publisher, and a community module may make a different declaration.
- **Search parent directories for `LICENSE`.** Rejected: the chosen file would depend on checkout
layout and could silently apply an unrelated repository's terms. An out-of-tree input must be
explicit.
- **Download standard license text while packaging.** Rejected: builds must remain offline,
deterministic, and independent of a mutable remote source.
- **Duplicate the AGPL text for every incorporated Ekklesia package.** Rejected: identical copies
add no information. `incorporates` records component identity and version; one applicable text is
sufficient for today's official packages.
- **Create `packageFormat: 2`.** Rejected: v1 has not crossed its own distributable-release boundary,
strict readers fail safely, and no archive or installation mechanism changes.
- **Make the packager decide license compatibility.** Rejected: preserving declarations and material
is mechanical; judging every present and future license combination is a broader legal and policy
problem.
## Deferred
- Implementing contract, packager, verifier, fixture, template, and package-proof changes.
- The exact set and version of SPDX expression syntax accepted by the implementation, beyond the
requirement that unsupported expressions fail closed.
- Broad policy for future mixed-license community modules and incorporated code.
- Container-image license distribution.
- Marketplace, installer, publisher verification, and package-signing policy.
+1
View File
@@ -59,3 +59,4 @@ ADRs capture durable choices that affect more than one package or phase.
- [0048: Ekklesia ships one generic image, and installed modules live outside it](0048-generic-image-and-installed-module-storage.md) — Accepted - [0048: Ekklesia ships one generic image, and installed modules live outside it](0048-generic-image-and-installed-module-storage.md) — Accepted
- [0049: A module release is one immutable, self-contained package: Ekklesia Module Package v1](0049-module-package-v1.md) — Accepted - [0049: A module release is one immutable, self-contained package: Ekklesia Module Package v1](0049-module-package-v1.md) — Accepted
- [0050: Module Database ABI v1: modules and Core exchange SQL, never ORM objects](0050-module-database-abi-v1.md) — Accepted - [0050: Module Database ABI v1: modules and Core exchange SQL, never ORM objects](0050-module-database-abi-v1.md) — Accepted
- [0051: Module packages declare their own license and carry its text](0051-module-package-license-material.md) — Proposed