From 1c7755d32035f563d8d0a282fa516db917908cde Mon Sep 17 00:00:00 2001 From: ekklesia Date: Wed, 7 Oct 2026 18:21:51 -0500 Subject: [PATCH] docs: propose ADR 0051 module package licensing --- .../0051-module-package-license-material.md | 207 ++++++++++++++++++ docs/architecture/decisions/README.md | 1 + 2 files changed, 208 insertions(+) create mode 100644 docs/architecture/decisions/0051-module-package-license-material.md diff --git a/docs/architecture/decisions/0051-module-package-license-material.md b/docs/architecture/decisions/0051-module-package-license-material.md new file mode 100644 index 0000000..b120519 --- /dev/null +++ b/docs/architecture/decisions/0051-module-package-license-material.md @@ -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. diff --git a/docs/architecture/decisions/README.md b/docs/architecture/decisions/README.md index bccc4e8..8d846cd 100644 --- a/docs/architecture/decisions/README.md +++ b/docs/architecture/decisions/README.md @@ -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 - [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 +- [0051: Module packages declare their own license and carry its text](0051-module-package-license-material.md) — Proposed -- 2.54.0