@subsquid/ponder is a drop-in fork of ponder: same ponder bin, plus a portal:
field per chain that routes the historical backfill through SQD Portal (realtime stays on rpc).
- Version =
<ponder-version>-sqd.<rev>.@subsquid/ponder@0.16.6-sqd.1isponder@0.16.6+ the Portal layer (fork revision 1). The ponder version stays visible, and-sqd.<rev>lets the fork re-cut a fix on the same ponder version — npm permanently retires an unpublished version number, so a plain mirror (0.16.6) can't be re-published after a bad build. The newest ponder version's release is published with--tag latest, sonpm i @subsquid/ponderresolves it (npm doesn't auto-pick a prerelease otherwise); an older ponder version publishes under aponder-<version>dist-tag rather than clobberinglatest, and is pinned as@subsquid/ponder@<version>-sqd.<rev>. - We don't hand-maintain a fork. This repo holds only the Portal layer (
portal/): theportal-*.tsmodules (an invariant-first functional core behind theportal.tsshell — seeportal/INVARIANTS.md) + a per-versionwiring/<ver>.patch(the 4 one-line touch-points). That's the entire diff against upstream. - The fork is generated.
scripts/sync-upstream.sh <ver>clonesponder@<ver>, applies the layer, renames the package, and builds — producing the publishable package. Tracking a new ponder version is "author one small patch + run the script", not "merge a fork". - Version-aware by construction.
versions.jsonis the source of truth: which ponder versions we support, each with its wiring patch, astatusverification state (verified/planned) plus a separatepublishedboolean, and thecompat.testedlist the CI matrix proves the seam against. We publish a fork release only for the ponder versions that are needed, while tracking which past and future versions still hold.
Releases run through .github/workflows/release.yml and
authenticate to npm with OIDC Trusted Publishing — no NPM_TOKEN (npm deprecated long-lived
tokens for CI in favour of the GitHub integration). The job applies the Portal layer to
ponder@<version>, builds, runs the Portal tests, then publishes.
One-time setup (a maintainer with publish rights on the @subsquid scope):
- On npmjs.com →
@subsquid/ponder→ Settings → Trusted Publisher → GitHub Actions, pointing at reposubsquid-labs/portal-ponder, workflowrelease.yml.- Brand-new package: if npm requires the package to exist before you can add a trusted publisher, seed it with one manual publish (below), then configure the trusted publisher — every release after that is tokenless.
- (When the repo is public) add
--provenanceto the publish step for a signed provenance attestation.
Cut a release: Actions → release → Run workflow → enter the ponder version (e.g. 0.16.6)
and the rev. The workflow guards that the version is in versions.json, builds + tests, and
publishes @subsquid/ponder@<version>-sqd.<rev>. A v0.16.6 tag push also triggers it, but the
tag path always publishes rev 1 (SYNC_REV defaults to 1 on a tag — a tag can't express a
revision), so a rev bump (-sqd.2 and up) must go through the manual workflow_dispatch with the
rev input. After a successful publish, set that row's "published": true in versions.json (leave
"status" as its verification state — the schema tracks publish state in the separate published
boolean, not via a status value).
Manual / local (to seed the first publish, or as a fallback):
SYNC_REV=1 scripts/sync-upstream.sh 0.16.6 --test # → @subsquid/ponder@0.16.6-sqd.1, built + tested
cd "$SYNC_WORKDIR/0.16.6/packages/core" && npm publish --access public --tag latest # local npm login- Author the wiring patch. The seam (
syncBlockRangeData/syncBlockData) is stable, but the 4 touch-points (internal/types.ts,config/index.ts,build/config.ts,runtime/historical.ts) can drift. Clone the target, hand-apply the same 4 edits (seeportal/wiring/0.16.6.patchfor the shape), thengit diff > portal/wiring/<ver>.patch. scripts/sync-upstream.sh <ver> --test— confirm it applies + builds + the Portal unit tests pass.- Add a
{ "ponder": "<ver>", "wiring": "wiring/<ver>.patch", "status": "verified" }row toversions.jsonand add<ver>tocompat.tested. - Commit; publish when a version is needed.
CI (.github/workflows/ci.yml) runs step 2 for every version in versions.json on each push, so a
ponder upgrade that breaks the seam is caught before release.
ponder's published exports only expose . and ./virtual; the internals the Portal sync needs
(runtime/filter, internal/types, the SyncStore) aren't importable. A loader-hook plugin can reach
them but is fragile and version-coupled in a different way. The fork is the robust, drop-in path today;
the thin plugin remains a possible future once ponder exposes a HistoricalSync hook (tracked upstream).