225 Commits
Author SHA1 Message Date
The_River 4919db6168 Merge pull request 'chore(release): prepare 0.2.1' (#85) from chore/release-0.2.1 into main
CI / verify (push) Successful in 1m8s
CI / infrastructure-smoke (push) Successful in 37s
CI / migration-smoke (push) Successful in 1m54s
CI / package-release (push) Successful in 37s
CI / image-build (push) Successful in 13s
CI / deploy-smoke (same-origin) (push) Successful in 49s
CI / deploy-smoke (split) (push) Successful in 1m21s
CI / package-publication (push) Successful in 52s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/85
Reviewed-by: The_River <[email protected]>
v0.2.1
2026-10-09 03:00:59 +00:00
ekklesiaandClaude Opus 5.5 6e76293620 chore(release): prepare 0.2.1
CI / verify (pull_request) Successful in 1m8s
CI / infrastructure-smoke (pull_request) Successful in 38s
CI / migration-smoke (pull_request) Successful in 1m57s
CI / package-release (pull_request) Successful in 36s
CI / image-build (pull_request) Successful in 31s
CI / deploy-smoke (same-origin) (pull_request) Successful in 55s
CI / deploy-smoke (split) (pull_request) Successful in 1m30s
CI / package-publication (pull_request) Successful in 52s
Every platform workspace package and every public @ekklesia/* package declares 0.2.1, and the deploy
README's image build example names it. templates/module and the fixture modules keep their own
versions.

0.2.1, a patch: since v0.2.0 (1788d17), main has one change, #84. It adds the reviewed supplement for
@img/[email protected] and moves the packager's native proof to sharp 0.35.5. That is new
packager data and test-only dependencies. No SDK, manifest, database ABI, packageFormat, or package
API changed.

The standalone fixture consumes exactly this release: its @ekklesia ranges become ^0.2.1 and its
module-pack pin 0.2.1 (docs/development/releases.md, step 1). Module compatibility ranges stay ^0.2.0,
as the release guide says a patch version requires. The template, the standalone fixture, the native
proof module, and the test fixtures modelling the current platform are therefore unchanged. A module
built for 0.2.0 remains compatible with 0.2.1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 21:45:13 -05:00
The_River 3b54a7f023 Merge pull request 'feat: reviewed libvips 1.3.4 supplement, and the native proof on sharp 0.35.5' (#84) from feat/libvips-1.3.4-supplement into main
CI / verify (push) Successful in 1m7s
CI / infrastructure-smoke (push) Successful in 39s
CI / migration-smoke (push) Successful in 1m58s
CI / package-release (push) Successful in 42s
CI / image-build (push) Successful in 12s
CI / deploy-smoke (same-origin) (push) Successful in 52s
CI / deploy-smoke (split) (push) Successful in 1m26s
CI / package-publication (push) Successful in 52s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/84
Reviewed-by: The_River <[email protected]>
2026-10-09 02:43:21 +00:00
ekklesiaandClaude Opus 5.5 afaf3a853c feat: reviewed libvips 1.3.4 supplement, and the native proof on sharp 0.35.5
CI / verify (pull_request) Successful in 1m4s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / package-release (pull_request) Successful in 34s
CI / image-build (pull_request) Successful in 30s
CI / deploy-smoke (same-origin) (pull_request) Successful in 47s
CI / deploy-smoke (split) (pull_request) Successful in 1m28s
CI / package-publication (pull_request) Successful in 50s
sharp 0.35.4 carries a production-relevant librsvg advisory (GHSA-wq5f-xc86-pv6w). Its fix, sharp
0.35.5, loads @img/sharp-libvips-linux-x64 1.3.4, which ships no licence material either. This adds
the reviewed supplement for that exact package (ADR 0052) and moves the packager's native proof to
sharp 0.35.5, so a later 0.2.1 release can package it on linux-x64-glibc. Nothing in module-pack's
code changes: supplement selection is already by exact name, version, and content digest.

- Supplement: license-supplements/@img/sharp-libvips-linux-x64/1.3.4/
  - content digest ekklesia-content-v1:sha256:2927fe11493461fa34847191d0b9b1c6441007c3e99f306e5708cb07fb0a5e59;
  - 190 components and 146 hashed texts;
  - assembled with the same method as 1.3.3's, from sharp-libvips v1.3.4's own inputs. That method
    reproduces the 1.3.3 supplement byte for byte.
  - PROVENANCE.md records:
    - the 27 upstream archives and five build-time patches, by sha256;
    - the librsvg 2.63.2 crate reproduction (cargo 1.99.0; Cargo.lock 5723e14c…);
    - rustc 75a75c3e…;
    - every change from 1.3.3.
  - Notable: sharp-libvips v1.3.4 now states cairo is used under MPL 1.1, not MPL 2.0. cairo ships
    that text itself, so the standard MPL 2.0 text is no longer added for cairo; selectors still
    carries it.
  - No legal conclusion: the LGPL and MPL source-availability determination stays open, as for 1.3.3.
- The 1.3.3 supplement is unchanged, and still proven: module-pack keeps sharp 0.35.4 as a test-only
  devDependency alias (`sharp-0.35.4`). Each supplement is pinned against its real installed
  libvips, and each matches only its own.
- Native proof fixture: sharp 0.35.4 → 0.35.5. The closure has the same shape: sharp, @img/colour,
  detect-libc, semver, and @img/sharp-linux-x64 0.35.5 with @img/sharp-libvips-linux-x64 1.3.4.
  The runtime read from the binaries is unchanged: Node-API 9, glibc >= 2.28, x86-64.
- Docs: the module guide's note on available supplements.

No version bump, no publication, no tag.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 21:34:35 -05:00
The_River 1788d17f0a Merge pull request 'fix: hand npm the release artifact's absolute path, so publishing finds it' (#83) from fix/publish-absolute-artifact-path into main
CI / verify (push) Successful in 1m6s
CI / infrastructure-smoke (push) Successful in 36s
CI / migration-smoke (push) Successful in 1m56s
CI / package-release (push) Successful in 34s
CI / image-build (push) Successful in 11s
CI / deploy-smoke (same-origin) (push) Successful in 49s
CI / deploy-smoke (split) (push) Successful in 1m22s
CI / package-publication (push) Successful in 47s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/83
Reviewed-by: The_River <[email protected]>
v0.2.0
2026-10-08 23:22:00 +00:00
ekklesiaandClaude Opus 5.5 1caffc5f26 fix: hand npm the release artifact's absolute path, so publishing finds it
CI / verify (pull_request) Successful in 1m5s
CI / infrastructure-smoke (pull_request) Successful in 37s
CI / migration-smoke (pull_request) Successful in 1m52s
CI / package-release (pull_request) Successful in 34s
CI / image-build (pull_request) Successful in 24s
CI / deploy-smoke (same-origin) (pull_request) Successful in 50s
CI / deploy-smoke (split) (pull_request) Successful in 1m30s
CI / package-publication (pull_request) Successful in 49s
The first real 0.2.0 publish failed on its first package with ENOENT:
"Publishing ekklesia-contracts-0.2.0.tgz failed: npm error code ENOENT".

The publish workflow passes a relative release path,
--release "$(find package-train …)", which is
package-train/ekklesia-packages-0.2.0. The tool validated the artifacts
relative to the job's working directory, but ran npm in its own
temporary home (cwd /tmp/ekklesia-publish-XXXXXX) with the same relative
path, package-train/ekklesia-packages-0.2.0/ekklesia-contracts-0.2.0.tgz,
which there names nothing. The disposable-registry suite always passed
an absolute path, so it never saw this.

- loadReleaseTrain resolves the release directory once; every artifact
  path is absolute from validation on, and npm is given only that exact
  file's absolute path (anything else is refused at the npm boundary).
- Just before each upload the file is checked against the validated
  bytes once more; the tool logs the absolute path it publishes, and a
  failure names it with npm's path/syscall lines (npm's temporary home
  masked, the token never shown).
- Regression tests: unit tests with a recording npm (relative nested
  release from another cwd; npm's argv, cwd, and the file's existence; a
  changed artifact refused before npm runs), which fail on the previous
  tool; and a disposable-Gitea test that runs the CLI exactly as the
  workflow does, from the Actions artifact layout, round-tripping all
  nine packages byte for byte.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:51:22 -05:00
The_River debb8df3ce Merge pull request 'chore(release): prepare 0.2.0' (#82) from chore/release-0.2.0 into main
CI / verify (push) Successful in 1m6s
CI / infrastructure-smoke (push) Successful in 36s
CI / migration-smoke (push) Successful in 1m55s
CI / package-release (push) Successful in 35s
CI / image-build (push) Successful in 11s
CI / deploy-smoke (same-origin) (push) Successful in 49s
CI / deploy-smoke (split) (push) Successful in 1m22s
CI / package-publication (push) Successful in 41s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/82
Reviewed-by: The_River <[email protected]>
2026-10-08 22:36:19 +00:00
ekklesiaandClaude Opus 5.5 98e59d4ba0 chore(release): prepare 0.2.0
CI / verify (pull_request) Successful in 1m5s
CI / infrastructure-smoke (pull_request) Successful in 35s
CI / migration-smoke (pull_request) Successful in 1m52s
CI / package-release (pull_request) Successful in 35s
CI / image-build (pull_request) Successful in 31s
CI / deploy-smoke (same-origin) (pull_request) Successful in 45s
CI / deploy-smoke (split) (pull_request) Successful in 1m30s
CI / package-publication (pull_request) Successful in 44s
Every platform workspace package and every public @ekklesia/* package
declares 0.2.0, and the deploy README's image build example names it.
templates/module keeps its own version.

0.2.0, not 0.1.3: before 1.0 a minor version is the breaking one
(docs/development/releases.md, ADR 0054), and since 0.1.2 main has
broken modules, module authors, and operators:
- #64 (ADR 0048): one generic image; installed modules live outside it;
- #66, #72 (ADR 0049, 0050): modules install as Module Package v1, and
  move to the Module Database ABI;
- #71: the manifest v8 cutover; v7 manifests are refused;
- #79 (ADR 0053): the curated SDK drops defineModuleSchema and the
  legacy ModuleDatabase type;
plus #67 (AGPL), #74, #77 (licensing, native targets), and the public
package train (#80, #81). It is also the first package train to publish:
0.1.2 never will be.

Because 0.2.0 is outside ^0.1.0, the same change moves to ^0.2.0:
- the standalone fixture's @ekklesia ranges and its exact module-pack pin;
- the compatibility ranges of the modules built for this release: the
  template, the standalone fixture, and the native proof module;
- the test fixtures that model a module of the current release, or the
  current platform's versions;
- the module-development guide's examples, and the release guide, which
  now names what a minor bump moves.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 17:26:13 -05:00
The_River 22404ea2d4 Merge pull request 'feat: publish the exact @ekklesia package train to the npm registry (ADR 0053)' (#81) from feat/package-train-publication into main
CI / verify (push) Successful in 1m1s
CI / infrastructure-smoke (push) Successful in 36s
CI / migration-smoke (push) Successful in 1m53s
CI / package-release (push) Successful in 32s
CI / image-build (push) Successful in 11s
CI / deploy-smoke (same-origin) (push) Successful in 42s
CI / deploy-smoke (split) (push) Successful in 1m22s
CI / package-publication (push) Successful in 42s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/81
Reviewed-by: The_River <[email protected]>
2026-10-08 16:31:57 +00:00
ekklesiaandClaude Opus 5.5 486356a01e docs: ADR 0054, Ekklesia's module-development packages are publicly readable before launch
CI / verify (pull_request) Successful in 1m4s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m46s
CI / package-release (pull_request) Successful in 33s
CI / image-build (pull_request) Successful in 22s
CI / deploy-smoke (same-origin) (pull_request) Successful in 48s
CI / deploy-smoke (split) (pull_request) Successful in 1m27s
CI / package-publication (pull_request) Successful in 43s
ADR 0053 was accepted assuming a private package phase before launch. The
project owner has decided otherwise: Ekklesia is not secret, its source is
already public, and only publication must be restricted.

ADR 0054 supersedes ADR 0053 only where it describes that phase as
private or requires authenticated package reads. Packages may be read
anonymously, which is neither a launch nor an API stability promise
beyond pre-1.0 SemVer; publication stays authenticated and restricted;
published versions stay immutable; the River_of_Glory_Church Gitea
namespace is an acceptable host; a public launch remains a separate
decision. Every other ADR 0053 decision is unchanged.

ADR 0053 gets only a cross-reference line. The ADR index, the release and
module-development guides, the README status, and the publish workflow's
comment now point at ADR 0054.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 10:24:17 -05:00
ekklesiaandClaude Opus 5.5 1f30c86d4f fix: hand the proven release train on from a directory the artifact actions upload
CI / verify (pull_request) Successful in 1m4s
CI / infrastructure-smoke (pull_request) Successful in 38s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / package-release (pull_request) Successful in 32s
CI / image-build (pull_request) Successful in 22s
CI / deploy-smoke (same-origin) (pull_request) Successful in 49s
CI / deploy-smoke (split) (pull_request) Successful in 1m39s
CI / package-publication (pull_request) Successful in 59s
The CI package-release gate passed, but the upload found no files: the
artifact actions leave hidden files out, and .package-release starts
with a dot. Keep the release in package-train instead, in both
workflows, and test that no artifact path is hidden.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 09:56:06 -05:00
ekklesiaandClaude Opus 5.5 d3c62e63bf feat: publish the exact @ekklesia package train to the npm registry (ADR 0053)
CI / verify (pull_request) Successful in 1m1s
CI / infrastructure-smoke (pull_request) Successful in 35s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / package-release (pull_request) Failing after 43s
CI / package-publication (pull_request) Skipped
CI / image-build (pull_request) Successful in 29s
CI / deploy-smoke (split) (pull_request) Canceled after 0s
CI / deploy-smoke (same-origin) (pull_request) Canceled after 38s
Publishes one package release train to the Gitea npm registry under
River_of_Glory_Church, whose packages are publicly readable on purpose:
exactly the .tgz files the package-release gate proved, and nothing else.
Nothing is published by this change.

- scripts/publish-package-train.ts validates the whole local train before
  any request (gate manifest hash, version, revision, unmodified source,
  the nine packages, every artifact's size, SHA-256, integrity,
  SHA256SUMS line, and inner package.json), then publishes in dependency
  order with `npm publish <file.tgz>` under a staging tag. Each package
  is reconciled first (absent: publish; same bytes: accept; other bytes:
  hard stop, version burned), then read back: metadata (name, version,
  integrity, dependency maps, normalized bin, tarball on the registry)
  and the tarball itself, byte for byte, also anonymously. `latest` moves
  only after all nine verify; reruns converge and never delete anything.
  It writes a publication record. The token comes from
  EKKLESIA_PACKAGES_PUBLISH_TOKEN only, through a temporary npm config
  that refers to the variable.
- scripts/test-package-publication.sh proves it against a disposable
  Gitea 1.27.2 with public owners, a publisher team and an outsider:
  anonymous reads, refused unauthorized publishing, byte-identical round
  trips, the registry's own 409, partial-train recovery, the burned
  version hard stop, and dist-tag retry convergence.
- CI: package-release keeps its proven release and hands it on as an
  artifact with its manifest hash; the new package-publication job
  publishes exactly that artifact to the disposable Gitea. Artifact
  actions v3 (node20): v4 refuses any server other than github.com.
- .github/workflows/publish-packages.yml publishes to the real registry
  only by manual dispatch on main, after its own gate run, with the
  secret in the publish step alone.
- docs: the public-read model, the workflow, the record, credentials,
  immutability policy, provider moves, and the first-publication steps.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 09:49:27 -05:00
The_River 57df2e15f2 Merge pull request 'feat: the public @ekklesia package release train, built, reproduced, and proven (ADR 0053)' (#80) from feat/public-package-release-train into main
CI / verify (push) Successful in 1m5s
CI / infrastructure-smoke (push) Successful in 37s
CI / migration-smoke (push) Successful in 1m54s
CI / package-release (push) Successful in 32s
CI / image-build (push) Successful in 10s
CI / deploy-smoke (same-origin) (push) Successful in 49s
CI / deploy-smoke (split) (push) Successful in 1m22s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/80
Reviewed-by: The_River <[email protected]>
2026-10-08 13:53:41 +00:00
ekklesiaandClaude Opus 5.5 4f1b1d68c7 fix: keep pnpm's package store out of the release builder's clean-source check
CI / verify (pull_request) Successful in 1m4s
CI / infrastructure-smoke (pull_request) Successful in 37s
CI / migration-smoke (pull_request) Successful in 1m54s
CI / package-release (pull_request) Successful in 32s
CI / image-build (pull_request) Successful in 30s
CI / deploy-smoke (same-origin) (pull_request) Successful in 50s
CI / deploy-smoke (split) (pull_request) Successful in 1m32s
The package-release job failed its clean-tree assertion in CI. Reproduced
as root in a container with a bind-mounted checkout, as the Gitea runner
does: after checkout the tree is clean; `pnpm install --frozen-lockfile`
then creates `.pnpm-store/` (17,272 untracked files) at the checkout's
root, because pnpm keeps its store on the node_modules file system and
the checkout is its own mount; `pnpm build` changes nothing. No tracked
file is ever modified.

- `.pnpm-store/` is ignored, like node_modules: a package cache, never
  source. The clean-commit requirement is unchanged for every tracked
  file and every untracked file Git does not ignore.
- The clean-tree failure now lists `git status --short` and
  `git diff --stat`.
- Nothing is built before the clean-source assertion: the gate no longer
  runs a global `pnpm build` first. The builder asserts a clean tree,
  builds, then fails if its build changed the tree, before packing. Core's
  verifier is built after both releases, for the consumer proof only.
- Every secondary offline install (the second worktree, the consumer
  module) uses the workspace install's own store explicitly, since pnpm
  picks a store per file system and those directories are on another.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 08:43:19 -05:00
ekklesiaandClaude Opus 5.5 9debe4e891 feat: the public @ekklesia package release train, built, reproduced, and proven (ADR 0053)
CI / verify (pull_request) Successful in 1m2s
CI / infrastructure-smoke (pull_request) Successful in 35s
CI / migration-smoke (pull_request) Successful in 1m52s
CI / package-release (pull_request) Failing after 16s
CI / image-build (pull_request) Successful in 29s
CI / deploy-smoke (same-origin) (pull_request) Successful in 44s
CI / deploy-smoke (split) (pull_request) Successful in 1m29s
Produces the exact public package tarballs of a platform release, validates
them, and proves a standalone module consumes those artifacts end to end.
Nothing is published and no credential is configured.

- scripts/package-release.ts (pnpm packages:release) is the canonical
  release builder: from a clean commit it rebuilds the nine public
  packages from clean output, packs each, inspects every tarball, checks
  the release set and coordinated versions, and only then writes
  ekklesia-packages-<version>/ with the tarballs, SHA256SUMS, and a
  provider-neutral manifest (ekklesia-package-train.json: version,
  revision, and each package's file, size, SHA-256, npm integrity).
- Packing uses a pack-only pnpm beforePacking hook (passed with
  --config.pnpmfile, never the workspace's install settings). pnpm inserts
  rewritten workspace: dependencies in async completion order, so two
  checkouts of one commit packed module-pack's package.json with keys in a
  different order; the hook restores the source key order (and the
  inspection enforces it). It also drops devDependencies, which named
  private packages outside the train.
- scripts/test-package-release.sh (pnpm test:package-release, the CI
  package-release gate) builds the train twice, the second time from a
  separate worktree, requires byte-identical release directories, then
  runs the standalone consumer proof on the release: manifest checked,
  offline install from the tarballs only, lockfile integrity equal to the
  manifest's, frozen-lockfile reinstall, no Drizzle, one React, contracts
  only transitive, every @ekklesia package and module-pack at the release
  version, skipLibCheck: false, UI build, pack, and Core verification.
- docs/development/releases.md documents the release order, the train,
  the artifacts, and the reproducibility findings; registry publication
  remains unimplemented.
- ADR 0053 is Accepted: everything it decides that does not need a
  registry is now proven, and registry provisioning is on its own
  Deferred list.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 08:02:41 -05:00
The_River 7ea2d7883f Merge pull request 'feat: standalone module-build readiness for the public @ekklesia packages (ADR 0053)' (#79) from feat/standalone-module-build into main
CI / verify (push) Successful in 1m5s
CI / infrastructure-smoke (push) Successful in 36s
CI / migration-smoke (push) Successful in 1m54s
CI / standalone-module (push) Successful in 27s
CI / image-build (push) Successful in 11s
CI / deploy-smoke (same-origin) (push) Successful in 43s
CI / deploy-smoke (split) (push) Successful in 1m21s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/79
Reviewed-by: The_River <[email protected]>
2026-10-08 12:48:07 +00:00
ekklesiaandClaude Opus 5.5 aaa7ebfd84 fix: keep Drizzle out of every published declaration (ADR 0053)
CI / verify (pull_request) Successful in 1m2s
CI / infrastructure-smoke (pull_request) Successful in 37s
CI / migration-smoke (pull_request) Successful in 1m52s
CI / standalone-module (pull_request) Successful in 26s
CI / image-build (pull_request) Successful in 28s
CI / deploy-smoke (same-origin) (pull_request) Successful in 43s
CI / deploy-smoke (split) (pull_request) Successful in 1m30s
@ekklesia/contracts still emitted a type-only import of
drizzle-orm/node-postgres for the legacy ModuleDatabase facade, so a
standalone module type-checked only because skipLibCheck hid it.

- ModuleDatabase, LegacyModuleRuntimeServices and
  AnyModuleRuntimeServices move to core/src/modules/database.ts, beside
  the facade Core builds; the facade itself is unchanged.
- contracts' runtime, route, and services generics no longer name an
  ORM: the database type is a plain parameter defaulting to the ABI v1
  executor, and the legacy definition's database is opaque until Core
  specializes it. Core and apps/api name the legacy types from Core.
- contracts drops its drizzle-orm optional peer and devDependency;
  Drizzle stays public only through @ekklesia/module-database/drizzle.
- The facade's type-level test moves from contracts to Core.
- Regression proof: the standalone fixture type-checks with
  skipLibCheck: false against the packed tarballs, with no drizzle-orm
  installed (the smoke asserts both). Against the previous contracts
  tarball the same check fails with TS2307 on drizzle-orm/node-postgres.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 07:40:43 -05:00
ekklesiaandClaude Opus 5.5 89d5d0f2da feat: standalone module-build readiness for the public @ekklesia packages (ADR 0053)
CI / verify (pull_request) Successful in 1m2s
CI / infrastructure-smoke (pull_request) Successful in 28s
CI / migration-smoke (pull_request) Successful in 1m52s
CI / standalone-module (pull_request) Successful in 26s
CI / image-build (pull_request) Successful in 30s
CI / deploy-smoke (same-origin) (pull_request) Successful in 50s
CI / deploy-smoke (split) (pull_request) Successful in 1m42s
Makes the public module-development package set usable from a module
repository outside the monorepo. Nothing is published.

- Curated @ekklesia/module-sdk: an explicit author API, no contracts
  barrel, no identity/authorization internals, no Drizzle and no legacy
  ModuleDatabase type. defineModuleRuntime returns the SDK's own named
  interfaces so a module's emitted declarations never reach into
  @ekklesia/contracts. defineModuleSchema is gone; createModuleTable and
  defineModuleSchemaMetadata live in @ekklesia/module-database.
- Drizzle: no longer a dependency of module-sdk or contracts. Core keeps
  the legacy Drizzle schema validator (moved to core/src/modules) and
  its legacy runtime support; drizzle-orm is an optional peer only.
- React is a peer (plus devDependency) of @ekklesia/ui; the boundary
  checker allows exactly that consumer-selected peer.
- @ekklesia/typescript-config is publishable; module-ui-host is private.
  Every public package carries "ekklesia.distribution":
  "module-development" and no monorepo-root prepack.
- module-pack reads the Node.js range from the module's own engines.node
  and recognizes Ekklesia packages by name plus that marker, so
  registry-installed packages are incorporated, SDK-compatibility
  checked, and never credited as third-party.
- scripts/pack-public-packages.ts (pnpm packages:pack) rebuilds, packs
  and inspects the nine public tarballs; scripts/test-standalone-module.sh
  (pnpm test:standalone-module, new CI job) installs them into a fixture
  module outside the repository and proves it typechecks, builds,
  packages, and verifies.
- Public-package boundary tests and module-development docs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 07:28:54 -05:00
The_River 9426c83109 Merge pull request 'docs: propose ADR 0053, standalone module builds from Ekklesia packages over the npm registry protocol' (#78) from docs/adr-0053-module-package-distribution into main
CI / verify (push) Successful in 1m6s
CI / infrastructure-smoke (push) Successful in 28s
CI / migration-smoke (push) Successful in 1m54s
CI / image-build (push) Successful in 11s
CI / deploy-smoke (same-origin) (push) Successful in 43s
CI / deploy-smoke (split) (push) Successful in 1m23s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/78
Reviewed-by: The_River <[email protected]>
2026-10-08 02:10:19 +00:00
ekklesiaandClaude Opus 5.5 47e64c6f39 docs: propose ADR 0053, standalone module builds from Ekklesia packages over the npm registry protocol
CI / verify (pull_request) Successful in 1m9s
CI / infrastructure-smoke (pull_request) Successful in 37s
CI / migration-smoke (pull_request) Successful in 1m56s
CI / image-build (pull_request) Successful in 24s
CI / deploy-smoke (same-origin) (pull_request) Successful in 43s
CI / deploy-smoke (split) (pull_request) Successful in 1m38s
Defines the external module-development and package-distribution contract
that ADR 0049 deferred.

- Protocol, not provider: packages are distributed over the standard npm
  registry protocol. Gitea may host the private phase. Host-neutral
  lockfiles and an Ekklesia proxy are not required; a registry move may need
  one configuration change and a lockfile regeneration, while integrity hashes
  keep verifying content.
- Public set:
  - Author API: module-sdk, module-database, module-ui-sdk, design-tokens.
  - Tooling: module-ui-build, module-pack, typescript-config.
  - ui as a pre-1.0 component API.
  - contracts only as a transitive implementation dependency.
  - Core, apps, module-ui-host, and module-template stay unpublished.
- Curated SDK before first publication: no full contracts barrel, no forced
  Drizzle at the SDK root, and no legacy Drizzle database contract in the
  standalone surface. Drizzle support lives in module-database's adapter.
- Dependency rules: React/ReactDOM are peers of the public UI packages
  (bundling per ADR 0023 is unchanged); Drizzle is an optional peer only
  where the adapter needs it; modules put runtime packages in dependencies
  and tooling/config in devDependencies.
- Versioning: one coordinated 0.x release version equal to the platform
  version, with no mixed trains. Ranges go in source and exact artifacts in
  lockfiles; module-pack is pinned exactly; independent versions deferred.
- Standalone packaging: no monorepo checkout. The module declares its own
  Node range (engines.node). The packager recognizes Ekklesia packages by
  identity and metadata, not path. No monorepo-root commands at install time.
- Supply chain: immutable versions, frozen-lockfile integrity, and a
  pre-publication smoke that installs the packed tarballs into a clean
  standalone fixture and proves build, package, and verify. Signing deferred.
- Private phase: no committed secrets; credentials come from the
  environment; verify actual read access before the first publication.

Status Proposed. ADR 0047 (still proposed) must be revised against this. No
code, registry, credential, or Signage changes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 20:46:32 -05:00
The_River 5803757153 Merge pull request 'feat: native linux-x64-glibc module packages and reviewed license supplements (ADR 0049, ADR 0052)' (#77) from feat/native-targets-license-supplements into main
CI / verify (push) Successful in 1m13s
CI / infrastructure-smoke (push) Successful in 37s
CI / migration-smoke (push) Successful in 1m58s
CI / image-build (push) Successful in 12s
CI / deploy-smoke (same-origin) (push) Successful in 42s
CI / deploy-smoke (split) (push) Successful in 1m23s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/77
Reviewed-by: The_River <[email protected]>
2026-10-08 01:29:37 +00:00
ekklesiaandClaude Opus 5.5 44d385cd7f feat: native linux-x64-glibc module packages and reviewed license supplements (ADR 0049, ADR 0052)
CI / verify (pull_request) Successful in 1m6s
CI / infrastructure-smoke (pull_request) Successful in 27s
CI / migration-smoke (pull_request) Successful in 1m54s
CI / image-build (pull_request) Successful in 29s
CI / deploy-smoke (same-origin) (pull_request) Successful in 43s
CI / deploy-smoke (split) (pull_request) Successful in 1m30s
Implements ADR 0049's deferred native target and ADR 0052's reviewed
license supplements. The proof case is a real module using sharp.
packageFormat stays 1, and `any` is unchanged.

Packager (@ekklesia/module-pack):
- --target linux-x64-glibc. Native or platform-specific packages, found by
  the same generic rule that refuses them in `any`, stay outside the bundle.
- Each native package's production closure is walked through its own
  dependencies, installed optionalDependencies that match the target, and
  required peers. It is copied as real files flat under node_modules/<name>,
  with no links, dev dependencies, declarations, source maps, or other
  platforms' packages. Missing or foreign-platform required dependencies,
  version conflicts, links, foreign machines, and Node.js range conflicts
  fail closed.
- runtime.nodeApi, nodeAbi, and libcVersion are read from the binaries by a
  small ELF reader: the addon's node_api_module_get_api_version_v1 and the
  newest GLIBC_ symbol version needed. No shelling out. A native target with
  no addon is refused.
- ADR 0052 supplements: package-provided material stays primary. An exact
  match on name, version, and ekklesia-content-v1 digest (sorted normalized
  paths with each file's size and SHA-256; no metadata) adds labelled
  material after the package's own. Any mismatch, a declared-licence
  conflict, or a malformed or unreviewed supplement file fails. Missing
  material with no supplement still fails.

Supplement for @img/[email protected]:
- 184 components and 143 unique texts, assembled from the exact upstream
  sources sharp-libvips v1.3.3 builds linux-x64 from: 27 C/C++ libraries,
  glib's gvdb, librsvg's Rust crates (reproduced with cargo tree), the Rust
  standard library at the rustc commit recorded in the binary, plus the
  LGPL-3.0, GPL-3.0, and MPL-2.0 texts. Each source is recorded with its
  SHA-256 in PROVENANCE.md.
- It pins digest ekklesia-content-v1:sha256:50647b0b…, which is identical
  from the installed copy and from the registry tarball.
- LGPL/MPL source-availability determinations stay open, as ADR 0052
  states. Nothing claims they are met.

Verifier (Core):
- linux-x64-glibc is accepted only on a host whose platform, arch, glibc
  family and version, and Node-API version (from Node itself) match.
- Every ELF file must be x86-64; every .node must be an ELF object.
- `any` packages now also refuse node_modules/ and ELF files.
- Contract: native metadata must state nodeApi or nodeAbi, and Linux targets
  libcVersion (ADR 0049).

Proof:
- A checked-in AGPL native fixture packs byte-identically from unrelated
  copies (8.86 MB) and verifies with Core's verifier.
- Extracted elsewhere, it runs a sharp resize and PNG round trip in an
  empty-environment process. Every resolution stays in the package, and
  only the package's addon and libvips plus host system libraries load.

Signage is untouched.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 20:16:53 -05:00
The_River f53506b3f0 Merge pull request 'docs: propose ADR 0052, reviewed license supplements for redistributed dependencies' (#76) from docs/adr-0052-license-supplements into main
CI / verify (push) Successful in 1m5s
CI / infrastructure-smoke (push) Successful in 36s
CI / migration-smoke (push) Successful in 1m53s
CI / image-build (push) Successful in 12s
CI / deploy-smoke (same-origin) (push) Successful in 41s
CI / deploy-smoke (split) (push) Successful in 1m23s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/76
Reviewed-by: The_River <[email protected]>
2026-10-08 00:47:12 +00:00
ekklesiaandClaude Opus 5.5 951453c735 docs: ADR 0052, what supplement review is, and what the match digest covers
CI / verify (pull_request) Successful in 1m2s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / image-build (pull_request) Successful in 23s
CI / deploy-smoke (same-origin) (pull_request) Successful in 43s
CI / deploy-smoke (split) (pull_request) Successful in 1m31s
Two wording clarifications; no decision changes.

- Repository review of a supplement is project and code review of a build
  input. It is not legal review, and not proof that redistribution
  obligations are satisfied.
- The exact-match digest is deterministic over normalized redistributed
  relative paths and file bytes. It excludes host filesystem metadata,
  timestamps, ownership, permissions, and extraction or listing order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 19:39:52 -05:00
ekklesiaandClaude Opus 5.5 99f145a80c docs: propose ADR 0052, reviewed license supplements for redistributed dependencies
CI / verify (pull_request) Successful in 1m0s
CI / infrastructure-smoke (pull_request) Successful in 34s
CI / image-build (pull_request) Canceled after 0s
CI / deploy-smoke (same-origin) (pull_request) Canceled after 0s
CI / deploy-smoke (split) (pull_request) Canceled after 0s
CI / migration-smoke (pull_request) Canceled after 1m10s
When a redistributed dependency's own package omits licence material that
redistribution requires, the canonical packager may use a checked-in,
reviewed supplement. The motivating case is
@img/[email protected]: LGPL-3.0-or-later, and its shared
library contains about 29 libraries, but it ships no licence text or
notice for any of them.

ADR 0052 decides:
- package-provided material stays the preferred source and is never
  replaced;
- a supplement matches exact package name, version, and a packager-computed
  content integrity; any mismatch fails closed, with no fallback;
- supplements are immutable, reviewed build inputs with no network access
  during packaging;
- they may carry licence texts, copyright and attribution notices,
  provenance, corresponding-source information, and other accompanying
  material;
- merging is deterministic, and supplement material is labelled;
- a supplement never overrides a dependency's declared licence; a
  disagreement fails packaging;
- the mechanism is generic, with no package-specific code;
- missing material with no exact supplement still fails packaging;
- packageFormat stays 1.

It extends ADR 0049 only in the collection rule that ADR delegated to the
implementation. Legal and source-obligation determinations for LGPL, MPL,
and similar binaries are explicitly deferred: no written offer, URL, or
mirror is asserted to satisfy any licence.

Status Proposed. No implementation, no libvips supplement, and no Signage
change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 19:37:11 -05:00
The_River 104a5145c8 Merge pull request 'test: give Core's import-safety tests a timeout that holds under full verify load' (#75) from test/import-safety-timeouts into main
CI / verify (push) Successful in 1m3s
CI / infrastructure-smoke (push) Successful in 28s
CI / migration-smoke (push) Successful in 1m48s
CI / image-build (push) Successful in 12s
CI / deploy-smoke (same-origin) (push) Successful in 48s
CI / deploy-smoke (split) (push) Successful in 1m22s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/75
Reviewed-by: The_River <[email protected]>
2026-10-08 00:18:06 +00:00
ekklesiaandClaude Opus 5.5 796ad4f16e test: give Core's import-safety tests a timeout that holds under full verify load
CI / verify (pull_request) Successful in 1m5s
CI / infrastructure-smoke (pull_request) Successful in 37s
CI / migration-smoke (pull_request) Successful in 1m55s
CI / image-build (pull_request) Successful in 24s
CI / deploy-smoke (same-origin) (pull_request) Successful in 49s
CI / deploy-smoke (split) (pull_request) Successful in 1m31s
Four Core tests prove that importing a module has no side effects. Each one
does a cold dynamic import() of a large Core source graph inside its test
body:
- doctor-import
- module-http-import
- cli-bootstrap-platform-owner
- cli-create-initial-owner (its first test)

Vitest transforms and evaluates that graph on demand, so the CPU-bound cost
counts against the default 5 s per-test timeout. Ordinary test files import
at the top level, outside any test timeout. There is no subprocess, shared
resource, or test bug.

Measured on six cores:
- alone: 0.7–1.2 s;
- alongside five other packages' suites, as in a full pnpm verify:
  3.0–5.4 s (doctor-import reached 5,400 ms).

A shared IMPORT_SAFETY_TEST_TIMEOUT_MS of 30 s (core/tests/support/
import-safety.ts) now applies to exactly these four tests. That is about five
times the slowest time measured under load, the same as the repository's
integration-test timeout, and still bounded, so an import that genuinely
hangs fails. Assertions, coverage, the global Vitest timeout, parallelism,
and production code are unchanged; there are no retries.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 19:08:30 -05:00
The_River b1e3244c1d Merge pull request 'feat: Module Package v1 carries the module's own license (ADR 0051)' (#74) from feat/module-package-v1-licensing into main
CI / verify (push) Successful in 1m6s
CI / infrastructure-smoke (push) Successful in 38s
CI / migration-smoke (push) Successful in 2m7s
CI / image-build (push) Successful in 15s
CI / deploy-smoke (same-origin) (push) Successful in 56s
CI / deploy-smoke (split) (push) Successful in 1m23s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/74
Reviewed-by: The_River <[email protected]>
2026-10-08 00:03:01 +00:00
ekklesiaandClaude Opus 5.5 85fae9e07e feat: Module Package v1 carries the module's own license (ADR 0051)
CI / verify (pull_request) Successful in 58s
CI / infrastructure-smoke (pull_request) Successful in 35s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / image-build (pull_request) Successful in 30s
CI / deploy-smoke (same-origin) (pull_request) Successful in 50s
CI / deploy-smoke (split) (pull_request) Successful in 1m32s
Every Module Package v1 now declares the module's license and carries its
text. packageFormat stays 1; pre-ADR-0051 metadata fails closed.

- Contracts: ekklesia.package.json requires an SPDX `license` expression,
  validated with spdx-expression-parse, with UNLICENSED and non-expressions
  refused. The `files` list must hold exactly one non-empty root LICENSE.
  The package contracts move to the @ekklesia/contracts/module-package
  subpath, so the SPDX parser and its data (spdx-exceptions and
  spdx-license-ids ship no licence files) never reach a module bundle
  through the SDK's re-export of the contracts root.
- Packager: reads the source package.json's string SPDX declaration and
  never invents one. It requires an explicit --license-file (a non-empty
  regular file, no symlink, no parent search, no download) and copies its
  bytes unchanged to LICENSE, listed with size and SHA-256 and covered by
  the archive digest. THIRD_PARTY_NOTICES stays third-party only, and
  bundled Ekklesia code stays in `incorporates`.
- Verifier: rejects missing or invalid license metadata, and a missing,
  unlisted, duplicated, or corrupted LICENSE, through the schema and the
  existing strict inventory.
- Template: packaged as AGPL-3.0-or-later with the repository's canonical
  LICENSE, passed explicitly (Dockerfile, smoke, docs). Packing it twice
  gives byte-identical archives.

Started by Codex; reviewed and completed in Claude Code (empty license-text
refusal, schema non-empty LICENSE, symlink and empty-file tests).

ADR 0051 is Accepted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 18:51:03 -05:00
The_River 30f61094e6 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]>
2026-10-07 23:29:19 +00:00
ekklesia 1c7755d320 docs: propose ADR 0051 module package licensing
CI / verify (pull_request) Successful in 1m5s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m54s
CI / image-build (pull_request) Successful in 23s
CI / deploy-smoke (same-origin) (pull_request) Successful in 41s
CI / deploy-smoke (split) (pull_request) Successful in 1m29s
2026-10-07 18:21:51 -05:00
The_River 42acdeb9e8 Merge pull request 'feat: transition module packages and template to database ABI v1' (#72) from feat/module-package-abi-v1-transition into main
CI / verify (push) Successful in 59s
CI / infrastructure-smoke (push) Successful in 36s
CI / migration-smoke (push) Successful in 1m53s
CI / image-build (push) Successful in 12s
CI / deploy-smoke (same-origin) (push) Successful in 49s
CI / deploy-smoke (split) (push) Successful in 1m21s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/72
Reviewed-by: The_River <[email protected]>
2026-10-07 23:14:49 +00:00
ekklesia 70854d234c feat: transition module packages to database ABI v1
CI / verify (pull_request) Successful in 1m1s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m54s
CI / image-build (pull_request) Successful in 31s
CI / deploy-smoke (same-origin) (pull_request) Successful in 52s
CI / deploy-smoke (split) (pull_request) Successful in 1m32s
2026-10-07 18:00:04 -05:00
The_River 20ad1ae208 Merge pull request 'feat: deliver module database ABI through manifest v8' (#71) from feat/manifest-v8-database-abi into main
CI / verify (push) Successful in 1m4s
CI / infrastructure-smoke (push) Successful in 38s
CI / migration-smoke (push) Successful in 1m47s
CI / image-build (push) Successful in 12s
CI / deploy-smoke (same-origin) (push) Successful in 49s
CI / deploy-smoke (split) (push) Successful in 1m24s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/71
Reviewed-by: The_River <[email protected]>
2026-10-07 22:45:52 +00:00
ekklesia 49b4d4165d feat: deliver module database ABI by manifest
CI / verify (pull_request) Successful in 1m0s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / image-build (pull_request) Successful in 23s
CI / deploy-smoke (same-origin) (pull_request) Successful in 50s
CI / deploy-smoke (split) (pull_request) Successful in 1m30s
2026-10-07 17:29:32 -05:00
The_River a1cdcb3ad1 Merge pull request 'feat: add module database ABI v1 adapters' (#70) from feat/module-database-abi-adapters into main
CI / verify (push) Successful in 1m4s
CI / infrastructure-smoke (push) Successful in 37s
CI / migration-smoke (push) Successful in 1m49s
CI / image-build (push) Successful in 11s
CI / deploy-smoke (same-origin) (push) Successful in 43s
CI / deploy-smoke (split) (push) Successful in 1m23s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/70
Reviewed-by: The_River <[email protected]>
2026-10-07 22:00:06 +00:00
ekklesia 859675c3c7 feat: add module database ABI v1 adapters
CI / verify (pull_request) Successful in 58s
CI / infrastructure-smoke (pull_request) Successful in 35s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / image-build (pull_request) Successful in 29s
CI / deploy-smoke (same-origin) (pull_request) Successful in 49s
CI / deploy-smoke (split) (pull_request) Successful in 1m31s
2026-10-07 16:44:28 -05:00
The_River 4a3958c616 Merge pull request 'feat: Core's Module Database ABI v1 executor, proven against PostgreSQL (ADR 0050)' (#69) from feat/module-database-abi-executor into main
CI / verify (push) Successful in 56s
CI / infrastructure-smoke (push) Successful in 35s
CI / migration-smoke (push) Successful in 1m48s
CI / image-build (push) Successful in 12s
CI / deploy-smoke (same-origin) (push) Successful in 41s
CI / deploy-smoke (split) (push) Successful in 1m22s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/69
Reviewed-by: The_River <[email protected]>
2026-10-07 21:25:15 +00:00
ekklesiaandClaude Opus 5.5 317a4ce410 fix: ADR 0050 callback-error precedence, and the honest limits of transaction control
CI / verify (pull_request) Successful in 1m4s
CI / infrastructure-smoke (pull_request) Successful in 38s
CI / migration-smoke (pull_request) Successful in 1m54s
CI / image-build (pull_request) Successful in 22s
CI / deploy-smoke (same-origin) (pull_request) Successful in 51s
CI / deploy-smoke (split) (pull_request) Successful in 1m37s
Narrow contract and implementation adjustments from review. No SDK, manifest,
packaging, or Signage work.

Callback-error precedence:
- A transaction or savepoint callback that rejects keeps its own error, even
  while sibling work it started is still settling (one failed half of a
  Promise.all). Core drains that work, rolls back, and never commits or
  releases.
- A callback that resolves with work outstanding (a forgotten await) still
  fails with MODULE_DATABASE_SCOPE_WORK_OUTSTANDING and rolls back.

Transaction-control SQL:
- ADR 0050 forbids it through query() as a conformance rule for trusted code,
  not a security guarantee. Core does not parse SQL, so PostgreSQL has run
  the statement before Core sees it. Core detects the violation from the
  command tag or the transaction status, and claims nothing it cannot do: a
  module's own COMMIT has committed.
- After a violation, the transaction's scopes refuse every later statement
  without sending it (it would otherwise autocommit). Core never commits the
  transaction itself, and the connection is destroyed rather than reused,
  for top-level violations too.
- The commit and rollback guarantees apply to conforming use.

Duplicate object-mode columns: documented as found only after execution, so a
mutation can have taken effect; atomicity needs an ABI transaction.

Tests prove each of these against PostgreSQL: Promise.all failures in a
transaction and a savepoint keep 23505; a forgotten await still rolls back; a
module COMMIT's prior work is committed while nothing after it runs; a backend
PID changes after every violation; duplicate RETURNING columns persist at the
top level and roll back in a transaction.

ADR 0050 stays Accepted: the executor conforms to the revised text.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 16:12:01 -05:00
ekklesiaandClaude Opus 5.5 6db50516b7 feat: Core's Module Database ABI v1 executor, proven against PostgreSQL (ADR 0050)
CI / verify (pull_request) Successful in 59s
CI / infrastructure-smoke (pull_request) Successful in 27s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / image-build (pull_request) Successful in 23s
CI / deploy-smoke (same-origin) (pull_request) Successful in 41s
CI / deploy-smoke (split) (pull_request) Successful in 1m29s
Implements the first ADR 0050 slice: Core's ORM-neutral executor and its
PostgreSQL evidence. Nothing is wired to modules yet; manifest v8, SDK
adapters, schema metadata, packaging, and Signage are later slices. The
legacy ModuleDatabase facade is unchanged.

Contracts (@ekklesia/contracts, no imports at all):
- ABI version 1; query { text, values, rowMode } returning
  { command, rowCount, rows, fields: [{ name, typeOid }] };
- executor, transaction, and savepoint types with exactly the ABI's
  members; parameter and result value types;
- ModuleDatabaseError { code, sqlState? }: eight stable codes, and a
  SQLSTATE only when PostgreSQL supplied one; static messages, no cause,
  frozen; isModuleDatabaseError recognises it by shape.

Core (createModuleDatabaseExecutor over the API's existing pool):
- one statement per query, enforced by forcing the extended query
  protocol so PostgreSQL itself refuses a second statement (42601);
  no SQL parsing;
- parameters: null, boolean, number, bigint, string, Uint8Array, and
  rectangular arrays of them; everything else refused before sending;
- a Core-owned decoding table that never consults pg's global parser
  registry: int8, numeric, temporal, and JSON as exact text; bytea as a
  plain Uint8Array; built-in arrays element by element;
- every operation pins TimeZone, DateStyle, IntervalStyle, bytea_output,
  and extra_float_digits, then resets with DISCARD ALL before returning
  the connection, or destroys it when it cannot be reset cleanly;
- callback-scoped transactions with explicit isolation and access mode;
  a COMMIT that PostgreSQL answers with ROLLBACK is reported as failed;
- savepoints named by Core; only the innermost scope is active; scopes
  are revoked when their callback settles;
- a callback that settles with work outstanding is rolled back, never
  committed or released;
- transaction-control statements sent through query are refused by
  command tag and transaction status, and fail the transaction;
- the module ID is carried for Core's attribution only.

Tests:
- 83 integration tests on a database with hostile defaults: queries,
  the one-statement rule, parameters, decoding, errors, transactions,
  savepoints, forgotten awaits, pool safety after every failure class,
  and session state;
- unit tests for encoding, decoding, options, sanitization, and scope
  bookkeeping;
- type-level and source checks that no ORM or driver object crosses
  the boundary.

ADR 0050 is Accepted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 16:01:46 -05:00
The_River d43031997b Merge pull request 'docs: propose ADR 0050, Module Database ABI v1' (#68) from docs/adr-0050-module-database-abi into main
CI / verify (push) Successful in 59s
CI / infrastructure-smoke (push) Successful in 36s
CI / migration-smoke (push) Successful in 1m57s
CI / image-build (push) Successful in 11s
CI / deploy-smoke (same-origin) (push) Successful in 50s
CI / deploy-smoke (split) (push) Successful in 1m22s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/68
Reviewed-by: The_River <[email protected]>
2026-10-07 20:33:49 +00:00
ekklesiaandClaude Opus 5.5 11e2495484 docs: ADR 0050, tighten transaction scope, single-statement, and error rules
CI / verify (pull_request) Successful in 56s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m52s
CI / image-build (pull_request) Successful in 22s
CI / deploy-smoke (same-origin) (pull_request) Successful in 49s
CI / deploy-smoke (split) (pull_request) Successful in 1m29s
Three narrow clarifications to the proposed contract. No other decision
changes.

- Outstanding work: module code awaits every operation before its
  transaction or savepoint callback returns. A callback that settles
  with work outstanding is a scope-usage error and never commits. Core
  may let started operations settle before rolling back and releasing
  the connection, but waiting never turns into a commit. Executors stay
  revoked once their scope has finished.
- One statement per query() call: a correctness and API rule, not a
  security boundary. It is upheld by a reliable driver or protocol
  mechanism, never a home-grown SQL parser. An implementation that
  cannot uphold it stops and reports instead of weakening the ADR.
- Errors: `code` is a stable Ekklesia-owned category and always present.
  `sqlState` is optional, present only when PostgreSQL supplied one.
  ABI-detected failures (closed scope, invalid parameter, invalid
  option) carry a code and no SQLSTATE.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 15:27:29 -05:00
ekklesiaandClaude Opus 5.5 759872834d docs: propose ADR 0050, Module Database ABI v1
CI / verify (pull_request) Successful in 57s
CI / infrastructure-smoke (pull_request) Successful in 35s
CI / migration-smoke (pull_request) Successful in 1m50s
CI / image-build (pull_request) Successful in 22s
CI / deploy-smoke (same-origin) (pull_request) Successful in 49s
CI / deploy-smoke (split) (pull_request) Successful in 1m31s
Core and modules exchange PostgreSQL SQL text, bound parameters, result
rows with field metadata, and Core-owned transaction scopes. Never ORM
objects. A module may use Drizzle, Kysely, raw SQL, or another
module-side builder. Core's own ORM becomes an implementation detail.

ADR 0050 decides:
- an explicit correctness/compatibility boundary, not a security
  sandbox: no claim for SQL parsing, execution-time prefix checks,
  SET ROLE, or PostgreSQL roles;
- opt-in through manifest version 8 `database.abi: 1`, a hard cutover
  like every manifest version; a version 8 module without `database`
  keeps the legacy ModuleDatabase facade during the deprecation window;
- the executor: query({ text, values, rowMode }) returning
  { command, rowCount, rows, fields }, one executor per module;
- a small parameter set (no Date, no implicit JSON, no identifiers);
- a Core-owned decoding table that loses no precision and does not
  depend on time zones (int8, numeric, temporal, and JSON as text;
  bytea as Uint8Array);
- a sanitized ModuleDatabaseError { code, sqlState? }, recognised
  structurally;
- callback-scoped transactions and Core-named savepoints, revoked when
  their callback settles;
- module-side adapters (Drizzle via pg-proxy, raw SQL, later Kysely);
- plain schema metadata for ABI v1 modules instead of Drizzle objects;
- migrations unchanged; packageFormat stays 1; ABI v1 packages omit
  transitional.databaseRuntime.

It partly supersedes ADR 0016 and amends ADR 0007 for ABI v1 modules,
and extends ADR 0049. Status Proposed. Accepted ADRs are unchanged. No
runtime, SDK, manifest, packaging, migration, or Signage changes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 15:18:59 -05:00
The_River 9ddb5e2796 Merge pull request 'chore: license Ekklesia under AGPL-3.0-or-later' (#67) from chore/agpl-license into main
CI / verify (push) Successful in 56s
CI / infrastructure-smoke (push) Successful in 35s
CI / migration-smoke (push) Successful in 1m51s
CI / image-build (push) Successful in 12s
CI / deploy-smoke (same-origin) (push) Successful in 42s
CI / deploy-smoke (split) (push) Successful in 1m21s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/67
Reviewed-by: The_River <[email protected]>
2026-10-07 19:13:05 +00:00
ekklesiaandClaude Opus 5.5 52a665502f chore: license Ekklesia under AGPL-3.0-or-later instead of AGPL-3.0-only
CI / verify (pull_request) Successful in 57s
CI / infrastructure-smoke (pull_request) Successful in 35s
CI / migration-smoke (pull_request) Successful in 1m52s
CI / image-build (pull_request) Successful in 29s
CI / deploy-smoke (same-origin) (pull_request) Successful in 50s
CI / deploy-smoke (split) (pull_request) Successful in 1m30s
The project decision is the GNU Affero General Public License, version 3 or
(at your option) any later version.

- "license": "AGPL-3.0-or-later" in all 14 Ekklesia-owned package.json files.
- README: the License section states AGPL v3 or any later version (SPDX
  AGPL-3.0-or-later) and carries the FSF's standard grant: "either version 3 of
  the License, or (at your option) any later version", with its warranty
  disclaimer and pointer to the license.
- Module template README and module development guide: AGPL-3.0-or-later for
  Ekklesia and official modules.

LICENSE is unchanged: it remains the canonical AGPLv3 text.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 13:53:56 -05:00
ekklesiaandClaude Opus 5.5 9a328f647e chore: license Ekklesia under AGPL-3.0-only
CI / verify (pull_request) Successful in 1m2s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m52s
CI / image-build (pull_request) Successful in 30s
CI / deploy-smoke (same-origin) (pull_request) Successful in 50s
CI / deploy-smoke (split) (pull_request) Successful in 1m39s
Ekklesia is open source under the GNU Affero General Public License, version 3
only. Module Package v1 (#66) bundles Ekklesia's SDK and contracts into
distributable module packages, so the platform needs an explicit license
before such artifacts are distributed.

- LICENSE: the GNU AGPL v3 text, verbatim from
  https://www.gnu.org/licenses/agpl-3.0.txt
  (sha256 0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0).
- "license": "AGPL-3.0-only" in the root package.json and every Ekklesia
  workspace package (13 packages), the module template included. Nothing else in
  any manifest changed.
- README: a License section replacing the "no license selected yet" placeholder:
  AGPL v3 only, source available under its terms, section 13's network-use
  obligation, and the policy that official Ekklesia modules use AGPL-3.0-only.
- Module template README and module development guide: the template declares
  the license; Module Package v1 does not yet record a module's own license,
  which needs a format change under ADR 0049.

AGPL-3.0-only, not -or-later: the decision is AGPL-3.0, and nothing in the
repository grants later versions. No runtime, ADR, package-format, Signage, or
instance change. The packaged template is byte-identical before and after.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 13:38:33 -05:00
The_River 22caa1dceb Merge pull request 'feat: Ekklesia Module Package v1, the canonical packager and verifier (ADR 0049)' (#66) from feat/module-package-v1 into main
CI / verify (push) Successful in 57s
CI / infrastructure-smoke (push) Successful in 26s
CI / migration-smoke (push) Successful in 1m50s
CI / image-build (push) Successful in 11s
CI / deploy-smoke (same-origin) (push) Successful in 42s
CI / deploy-smoke (split) (push) Successful in 1m26s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/66
Reviewed-by: The_River <[email protected]>
2026-10-07 18:33:28 +00:00
ekklesiaandClaude Opus 5.5 27d85e0162 fix(smoke): hand the verifier the package's archive and digest as one readable release pair
CI / verify (pull_request) Successful in 58s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m51s
CI / image-build (pull_request) Successful in 22s
CI / deploy-smoke (same-origin) (pull_request) Successful in 42s
CI / deploy-smoke (split) (pull_request) Successful in 1m31s
The split deploy smoke failed in CI with "The SHA-256 file could not be read."
BuildKit's local exporter creates its output directory (`--output
type=local,dest=$work/module-package`) with mode 0700, owned by whoever ran the
build. That directory was mounted into the verifier, which runs as the image's
unprivileged `node` user (uid 1000). In CI the build runs as root, so the
directory was root:root 0700 and the verifier could read neither file; the
digest file is simply read first. On a workstation the build runs as uid 1000,
the same uid as `node`, so it was readable by coincidence.

scripts/lib/module-package-release.sh turns one build's output into the release
pair: it requires exactly one archive and its .sha256 and nothing else, checks
that the digest line names that archive, then creates a fresh directory that
must not exist yet (0755) and copies both files into it (0644). The smoke stages
and mounts only that directory; BuildKit's output directory is never mounted.
Archive-digest verification stays mandatory: the verifier still reads the
.sha256 file. Nothing in the verifier changed.

Tests: the helper is driven in bash against a 0700 build output, and refuses a
missing, malformed, or mismatched digest, stray output, a second archive, and
an existing release directory, creating nothing. A static test pins the smoke
to the release pair and the verifier's single mount with --sha256-file.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 13:10:58 -05:00
ekklesiaandClaude Opus 5.5 35ef31df9f feat: Ekklesia Module Package v1, the canonical packager and verifier (ADR 0049)
CI / verify (pull_request) Successful in 57s
CI / infrastructure-smoke (pull_request) Successful in 35s
CI / migration-smoke (pull_request) Successful in 1m42s
CI / image-build (pull_request) Successful in 29s
CI / deploy-smoke (same-origin) (pull_request) Successful in 51s
CI / deploy-smoke (split) (pull_request) Failing after 54s
A separately versioned module is now distributed as one deterministic
.tar.gz and its SHA-256, produced by an Ekklesia-owned packager and checked by
Core before anything is installed. This slice implements the portable `any`
target, with the module template as the end-to-end proof. ADR 0049 is
accepted.

@ekklesia/contracts: the Module Package v1 contract. The strict
ekklesia.package.json schema (packageFormat 1, identity, target, runtime,
provenance, incorporated Ekklesia packages, strict transitional facts, an
exact byte-wise-sorted file list with size and SHA-256), target names, and the
generated runtime package.json.

@ekklesia/module-pack (new, tooling): `ekklesia-pack-module --module <dir>
--target any --out <dir> --source-revision <commit>`.
- Bundles server, schema, and jobs with esbuild, SDK, contracts, and Drizzle
  included; only Node built-ins remain imports. Any warning, unresolved
  import, external dependency, or native or platform-specific package fails.
- Copies browser artifacts as built, after proving them current, and the
  migrations; generates package.json and ekklesia.package.json; never ships
  src, tests, declarations, source maps, or workspace ranges.
- THIRD_PARTY_NOTICES: every third-party package traced from the bundles'
  own inputs, credited once with its declared licence and every licence,
  copying, copyright, and notice file it ships. A missing licence fails; a
  missing text fails unless its standard text is complete without copyright
  lines (Apache-2.0, vendored, needed by drizzle-orm).
- Deterministic ustar and gzip: sorted paths, uid/gid 0, empty names, mtime
  0, 0755/0644, no links. Byte-identical across runs and machines.
- Writes only the archive and its .sha256; never deploys, links, or installs,
  so the workspace's install state is untouched.

Core: verifyModulePackage and `verify-module-package.js <archive>
[--sha256 | --sha256-file] [--extract-to <new dir>]`. Reads the archive in
memory and refuses links, special entries, setuid/setgid/sticky modes, unsafe
or escaping paths, and a second top-level directory; checks metadata, every
file, identity, target, Node.js range, SDK, and the transitional Drizzle
agreement; runs the platform's module and migration discovery; loads the
backend once in a forked process with an empty environment; only then
extracts, with normalized modes, into a new directory. Errors are sanitized.

@ekklesia/module-ui-build: analyzeModuleUi reports each artifact's inputs
and whether the built file is current. The nested stylesheet build now works
from the module directory, so artifacts no longer depend on the caller's
working directory.

The split deploy smoke builds the package with the canonical packager
(scripts/lib/build-module-package.Dockerfile replaces the test-only pnpm
deploy packaging), verifies it with the same generic image, installs the
verified tree, removes it, and reinstalls the exact same archive.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 12:44:54 -05:00
The_River 45d6506c10 Merge pull request 'docs: propose ADR 0049, Ekklesia Module Package v1' (#65) from docs/adr-0049-module-package into main
CI / verify (push) Successful in 55s
CI / infrastructure-smoke (push) Successful in 36s
CI / migration-smoke (push) Successful in 1m52s
CI / image-build (push) Successful in 11s
CI / deploy-smoke (same-origin) (push) Successful in 41s
CI / deploy-smoke (split) (push) Successful in 1m26s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia/pulls/65
Reviewed-by: The_River <[email protected]>
2026-10-07 15:21:30 +00:00