feat: publish the exact @ekklesia package train to the npm registry (ADR 0053) #81

Merged
Zayin_Admin merged 3 commits from feat/package-train-publication into main 2026-10-08 16:31:57 +00:00
Member

Publishes a coordinated @ekklesia/* package train to the Gitea npm registry under River_of_Glory_Church (ADR 0053). It publishes exactly the .tgz files that pnpm packages:release produced and the gate proved.

This PR publishes nothing to the real registry. Real publication is only ever started by hand. Signage is untouched, and package APIs, packageFormat, versions and tags are unchanged.

Read model

Packages are publicly readable on purpose: no account or token is needed to search, read or install them. That isn't a public launch; the source is already publicly readable. Only publishing is restricted, to one dedicated publisher account through one manual workflow.

Architecture: ADR 0054

ADR 0054 (Accepted) records the project owner's decision that @ekklesia/* packages may be anonymously readable before launch.

  • What it supersedes. It replaces ADR 0053 only where 0053 describes the pre-launch package phase as private, or requires authenticated package reads.
  • Everything else in ADR 0053 is unchanged. ADR 0053 itself gains only a cross-reference line.
  • What reads do not mean. They are not a launch, and promise no API stability beyond pre-1.0 SemVer.
  • What stays the same. Publication remains authenticated and restricted, published versions stay immutable, and the River_of_Glory_Church namespace is an acceptable host.

What changes

  • scripts/publish-package-train.ts, the publication tool:
    • Validates the whole train before sending anything:
      • the manifest equals the one the gate proved (--manifest-sha256);
      • the requested --version and this commit's --revision match;
      • modified: false, and exactly the nine packages;
      • every artifact's file name, size, SHA-256, npm integrity, SHA256SUMS line, and the package.json inside it.
    • Publishes in dependency order, then alphabetically, never in directory order. Uses npm publish <file.tgz> under the staging tag ekklesia-staging.
    • Reconciles each package first:
      • absent → publish;
      • present with the same bytes → accept and verify;
      • present with other bytes → hard stop. The version is burned, nothing is deleted or overwritten, and nothing after it is published.
    • Verifies each round trip:
      • the registry metadata's name, version, integrity, dependency maps and bin. npm strips ./ from bin paths in the metadata it uploads; the tarball keeps the original form;
      • the tarball URL is on the configured registry;
      • the downloaded tarball is byte-identical to the release artifact;
      • optionally (--require-public-read), so is an anonymous download.
    • latest moves last, only after all nine are verified, and never backwards. Reruns converge.
    • Writes a publication record (--record).
    • Never deletes, unpublishes or overwrites; a test checks the tool's code for this.
    • Credentials:
      • the token comes only from EKKLESIA_PACKAGES_PUBLISH_TOKEN;
      • npm runs with a temporary configuration that refers to the variable, with user, global and project npm configuration ignored;
      • the token is sent only to the registry's own origin;
      • the temporary configuration is removed afterwards.
  • scripts/test-package-publication.sh (pnpm test:package-publication): a disposable Gitea 1.27.2, pinned by digest, configured like the real one: public owners, a publisher team with Packages: Write only, and an outsider. It proves:
    • anonymous reads work;
    • unauthenticated and outsider publishes are refused;
    • round trips are byte-identical, including anonymously;
    • the registry's own 409 on a duplicate version;
    • recovery after an interruption before the sixth package;
    • the burned-version hard stop, unchanged on retry;
    • dist-tag retry convergence.
  • CI.
    • package-release now keeps its proven release (--keep), exposes version, revision and manifest SHA-256 as job outputs, and uploads the release as an artifact.
    • The new package-publication job downloads that exact artifact, checks its manifest hash against the gate's output and runs sha256sum --check, then publishes it to the disposable Gitea. It holds no credentials.
    • The artifact actions are v3 (node20): v4's isGhes() refuses any server other than github.com.
  • .github/workflows/publish-packages.yml: real publication only.
    • Triggered by workflow_dispatch on main only; it never cancels a run in progress.
    • Its own gate run → the publish job (no build, no install) → the publication record artifact.
    • The secret appears in that one step alone. Tests check the trigger, branch guard, dependency and secret placement, and that CI never references the secret.
  • Docs.
    • docs/development/releases.md: the public-read model, the workflow, the tool, the record, credentials, the immutability policy, moving registries, and the manual steps before the first publication.
    • The module guide's standalone workflow: one committed .npmrc line, no token; pnpm install, pnpm build, ekklesia-pack-module.
    • testing.md, the README status, and CLAUDE.md.

Evidence

  • pnpm verify passes: 2,585 tests, including 20 new tests for the tool and the workflow rules.
  • pnpm test:package-publication: 10 of 10, against the disposable Gitea 1.27.2.
  • pnpm test:package-release passes strict, with both builds byte-identical.
  • During development I checked by hand against a throwaway Gitea 1.27.2:
    • npm's bin normalization;
    • that npm itself refuses to publish over an existing version, before contacting the server;
    • the server's 409 on a raw duplicate upload;
    • the outsider's 401;
    • byte-identical anonymous downloads.

Before the first real publication (manual, an instance administrator)

  1. Create a dedicated publisher account and a River_of_Glory_Church team with only Packages: Write.
  2. Issue a token with only write:package, and store it as the repository secret EKKLESIA_PACKAGES_PUBLISH_TOKEN.
  3. Confirm the owner has no package cleanup rule.
  4. Set the next platform version (not 0.1.2), merge, and run Publish packages on main.

🤖 Generated with Claude Code

Publishes a coordinated `@ekklesia/*` package train to the Gitea npm registry under `River_of_Glory_Church` ([ADR 0053](docs/architecture/decisions/0053-standalone-module-development-packages.md)). It publishes exactly the `.tgz` files that `pnpm packages:release` produced and the gate proved. **This PR publishes nothing to the real registry.** Real publication is only ever started by hand. Signage is untouched, and package APIs, `packageFormat`, versions and tags are unchanged. ## Read model Packages are **publicly readable on purpose**: no account or token is needed to search, read or install them. That isn't a public launch; the source is already publicly readable. Only publishing is restricted, to one dedicated publisher account through one manual workflow. ## Architecture: ADR 0054 [ADR 0054](docs/architecture/decisions/0054-public-package-reads-before-launch.md) (Accepted) records the project owner's decision that `@ekklesia/*` packages may be anonymously readable before launch. - **What it supersedes.** It replaces ADR 0053 **only** where 0053 describes the pre-launch package phase as private, or requires authenticated package reads. - **Everything else in ADR 0053 is unchanged.** ADR 0053 itself gains only a cross-reference line. - **What reads do not mean.** They are not a launch, and promise no API stability beyond pre-1.0 SemVer. - **What stays the same.** Publication remains authenticated and restricted, published versions stay immutable, and the `River_of_Glory_Church` namespace is an acceptable host. ## What changes - **`scripts/publish-package-train.ts`**, the publication tool: - **Validates the whole train before sending anything:** - the manifest equals the one the gate proved (`--manifest-sha256`); - the requested `--version` and this commit's `--revision` match; - `modified: false`, and exactly the nine packages; - every artifact's file name, size, SHA-256, npm integrity, `SHA256SUMS` line, and the `package.json` inside it. - **Publishes in dependency order,** then alphabetically, never in directory order. Uses `npm publish <file.tgz>` under the staging tag `ekklesia-staging`. - **Reconciles each package first:** - absent → publish; - present with the same bytes → accept and verify; - present with other bytes → **hard stop**. The version is burned, nothing is deleted or overwritten, and nothing after it is published. - **Verifies each round trip:** - the registry metadata's name, version, integrity, dependency maps and `bin`. npm strips `./` from `bin` paths in the metadata it uploads; the tarball keeps the original form; - the tarball URL is on the configured registry; - the downloaded tarball is byte-identical to the release artifact; - optionally (`--require-public-read`), so is an anonymous download. - **`latest` moves last,** only after all nine are verified, and never backwards. Reruns converge. - **Writes a publication record** (`--record`). - **Never deletes, unpublishes or overwrites;** a test checks the tool's code for this. - **Credentials:** - the token comes only from `EKKLESIA_PACKAGES_PUBLISH_TOKEN`; - npm runs with a temporary configuration that refers to the variable, with user, global and project npm configuration ignored; - the token is sent only to the registry's own origin; - the temporary configuration is removed afterwards. - **`scripts/test-package-publication.sh`** (`pnpm test:package-publication`): a disposable Gitea 1.27.2, pinned by digest, configured like the real one: public owners, a publisher team with Packages: Write only, and an outsider. It proves: - anonymous reads work; - unauthenticated and outsider publishes are refused; - round trips are byte-identical, including anonymously; - the registry's own 409 on a duplicate version; - recovery after an interruption before the sixth package; - the burned-version hard stop, unchanged on retry; - dist-tag retry convergence. - **CI.** - `package-release` now keeps its proven release (`--keep`), exposes version, revision and manifest SHA-256 as job outputs, and uploads the release as an artifact. - The new **`package-publication`** job downloads that exact artifact, checks its manifest hash against the gate's output and runs `sha256sum --check`, then publishes it to the disposable Gitea. It holds no credentials. - The artifact actions are **v3 (node20)**: v4's `isGhes()` refuses any server other than github.com. - **`.github/workflows/publish-packages.yml`**: real publication only. - Triggered by `workflow_dispatch` on `main` only; it never cancels a run in progress. - Its own gate run → the publish job (no build, no install) → the publication record artifact. - The secret appears in that one step alone. Tests check the trigger, branch guard, dependency and secret placement, and that CI never references the secret. - **Docs.** - `docs/development/releases.md`: the public-read model, the workflow, the tool, the record, credentials, the immutability policy, moving registries, and the manual steps before the first publication. - The module guide's standalone workflow: one committed `.npmrc` line, no token; `pnpm install`, `pnpm build`, `ekklesia-pack-module`. - `testing.md`, the README status, and `CLAUDE.md`. ## Evidence - `pnpm verify` passes: 2,585 tests, including 20 new tests for the tool and the workflow rules. - `pnpm test:package-publication`: 10 of 10, against the disposable Gitea 1.27.2. - `pnpm test:package-release` passes strict, with both builds byte-identical. - During development I checked by hand against a throwaway Gitea 1.27.2: - npm's `bin` normalization; - that npm itself refuses to publish over an existing version, before contacting the server; - the server's 409 on a raw duplicate upload; - the outsider's 401; - byte-identical anonymous downloads. ## Before the first real publication (manual, an instance administrator) 1. Create a dedicated publisher account and a `River_of_Glory_Church` team with only Packages: Write. 2. Issue a token with only `write:package`, and store it as the repository secret `EKKLESIA_PACKAGES_PUBLISH_TOKEN`. 3. Confirm the owner has no package cleanup rule. 4. Set the next platform version (not 0.1.2), merge, and run **Publish packages** on `main`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ekklesia-bot added 1 commit 2026-10-08 14:50:51 +00:00
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
d3c62e63bf
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]>
Zayin_Admin added 1 commit 2026-10-08 14:56:08 +00:00
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
1f30c86d4f
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]>
Zayin_Admin added 1 commit 2026-10-08 15:24:20 +00:00
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
486356a01e
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]>
Zayin_Admin merged commit 22404ea2d4 into main 2026-10-08 16:31:57 +00:00
Zayin_Admin deleted branch feat/package-train-publication 2026-10-08 16:31:58 +00:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Zayin/Ekklesia#81