May 03, 8-9 AM (1)
May 03, 9-10 AM (3)
May 03, 10-11 AM (10)
May 03, 11-12 PM (11)
May 03, 12-1 PM (16)
May 03, 1-2 PM (11)
May 03, 2-3 PM (2)
May 03, 3-4 PM (2)
May 03, 4-5 PM (5)
May 03, 5-6 PM (0)
May 03, 6-7 PM (5)
May 03, 7-8 PM (6)
May 03, 8-9 PM (8)
May 03, 9-10 PM (15)
May 03, 10-11 PM (23)
May 03, 11-12 AM (17)
May 04, 12-1 AM (4)
May 04, 1-2 AM (4)
May 04, 2-3 AM (10)
May 04, 3-4 AM (9)
May 04, 4-5 AM (5)
May 04, 5-6 AM (6)
May 04, 6-7 AM (6)
May 04, 7-8 AM (28)
May 04, 8-9 AM (26)
May 04, 9-10 AM (43)
May 04, 10-11 AM (36)
May 04, 11-12 PM (61)
May 04, 12-1 PM (34)
May 04, 1-2 PM (49)
May 04, 2-3 PM (64)
May 04, 3-4 PM (33)
May 04, 4-5 PM (64)
May 04, 5-6 PM (49)
May 04, 6-7 PM (13)
May 04, 7-8 PM (32)
May 04, 8-9 PM (45)
May 04, 9-10 PM (9)
May 04, 10-11 PM (54)
May 04, 11-12 AM (24)
May 05, 12-1 AM (4)
May 05, 1-2 AM (5)
May 05, 2-3 AM (5)
May 05, 3-4 AM (11)
May 05, 4-5 AM (11)
May 05, 5-6 AM (50)
May 05, 6-7 AM (16)
May 05, 7-8 AM (37)
May 05, 8-9 AM (81)
May 05, 9-10 AM (68)
May 05, 10-11 AM (34)
May 05, 11-12 PM (72)
May 05, 12-1 PM (115)
May 05, 1-2 PM (118)
May 05, 2-3 PM (66)
May 05, 3-4 PM (91)
May 05, 4-5 PM (41)
May 05, 5-6 PM (26)
May 05, 6-7 PM (28)
May 05, 7-8 PM (73)
May 05, 8-9 PM (31)
May 05, 9-10 PM (18)
May 05, 10-11 PM (25)
May 05, 11-12 AM (17)
May 06, 12-1 AM (10)
May 06, 1-2 AM (5)
May 06, 2-3 AM (9)
May 06, 3-4 AM (23)
May 06, 4-5 AM (7)
May 06, 5-6 AM (13)
May 06, 6-7 AM (30)
May 06, 7-8 AM (11)
May 06, 8-9 AM (106)
May 06, 9-10 AM (27)
May 06, 10-11 AM (41)
May 06, 11-12 PM (46)
May 06, 12-1 PM (86)
May 06, 1-2 PM (53)
May 06, 2-3 PM (43)
May 06, 3-4 PM (33)
May 06, 4-5 PM (18)
May 06, 5-6 PM (8)
May 06, 6-7 PM (12)
May 06, 7-8 PM (26)
May 06, 8-9 PM (13)
May 06, 9-10 PM (9)
May 06, 10-11 PM (30)
May 06, 11-12 AM (23)
May 07, 12-1 AM (7)
May 07, 1-2 AM (2)
May 07, 2-3 AM (1)
May 07, 3-4 AM (10)
May 07, 4-5 AM (4)
May 07, 5-6 AM (33)
May 07, 6-7 AM (97)
May 07, 7-8 AM (235)
May 07, 8-9 AM (42)
May 07, 9-10 AM (29)
May 07, 10-11 AM (55)
May 07, 11-12 PM (39)
May 07, 12-1 PM (59)
May 07, 1-2 PM (48)
May 07, 2-3 PM (47)
May 07, 3-4 PM (42)
May 07, 4-5 PM (58)
May 07, 5-6 PM (10)
May 07, 6-7 PM (24)
May 07, 7-8 PM (30)
May 07, 8-9 PM (12)
May 07, 9-10 PM (18)
May 07, 10-11 PM (65)
May 07, 11-12 AM (20)
May 08, 12-1 AM (6)
May 08, 1-2 AM (19)
May 08, 2-3 AM (19)
May 08, 3-4 AM (8)
May 08, 4-5 AM (3)
May 08, 5-6 AM (20)
May 08, 6-7 AM (19)
May 08, 7-8 AM (53)
May 08, 8-9 AM (34)
May 08, 9-10 AM (12)
May 08, 10-11 AM (32)
May 08, 11-12 PM (40)
May 08, 12-1 PM (30)
May 08, 1-2 PM (45)
May 08, 2-3 PM (46)
May 08, 3-4 PM (26)
May 08, 4-5 PM (23)
May 08, 5-6 PM (29)
May 08, 6-7 PM (15)
May 08, 7-8 PM (10)
May 08, 8-9 PM (14)
May 08, 9-10 PM (16)
May 08, 10-11 PM (23)
May 08, 11-12 AM (12)
May 09, 12-1 AM (4)
May 09, 1-2 AM (1)
May 09, 2-3 AM (0)
May 09, 3-4 AM (6)
May 09, 4-5 AM (6)
May 09, 5-6 AM (2)
May 09, 6-7 AM (6)
May 09, 7-8 AM (4)
May 09, 8-9 AM (4)
May 09, 9-10 AM (3)
May 09, 10-11 AM (9)
May 09, 11-12 PM (6)
May 09, 12-1 PM (18)
May 09, 1-2 PM (23)
May 09, 2-3 PM (12)
May 09, 3-4 PM (14)
May 09, 4-5 PM (7)
May 09, 5-6 PM (0)
May 09, 6-7 PM (2)
May 09, 7-8 PM (3)
May 09, 8-9 PM (1)
May 09, 9-10 PM (8)
May 09, 10-11 PM (33)
May 09, 11-12 AM (16)
May 10, 12-1 AM (15)
May 10, 1-2 AM (0)
May 10, 2-3 AM (0)
May 10, 3-4 AM (5)
May 10, 4-5 AM (0)
May 10, 5-6 AM (4)
May 10, 6-7 AM (0)
May 10, 7-8 AM (5)
May 10, 8-9 AM (0)
4,326 commits this week May 03, 2026 - May 10, 2026
chore(ui): a11y skip-link, useMemo transaction parse, cleanup deletions
Small UI/hook cleanup that fell out of the audit:

- overall-layout: add skip-link + main-content anchor; aria-label on the
  main form. Trims unused legacy nav code.
- transaction-card: useMemo the JSON.parse(transaction.txJson) so the
  parse only runs when txJson changes, not on every render. Removes a
  dead `import { get } from \"http\"` that was sneaking into the client
  bundle.
- signable-card: defensive parse for legacy payload shapes.
- card-show-signers, signing/index: small render fixes.
- ImgDragAndDrop, MeshProviderClient, BotManagementCard, background.tsx:
  drop dead state vars / unused imports surfaced by the audit.
- useAppWallet, useMultisigWallet: stable returns; missing-wallet path
  no longer spins indefinitely.
- Delete `src/components/multisig/proxy/ProxyControlExample.tsx`. It was
  example-only code, not exported from the proxy index, never rendered
  anywhere. The barrel import is updated.

Test plan
- 165/165 staged-suite tests pass on top of #237
- Typecheck clean

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
refactor(wallet-flow): stable signerIds for React keys; tripwire test
The new-wallet-flow and wallet-migration flow used the raw signer
address as a React key in ReviewSignersCard. Two issues:

1. The address can be empty/undefined while the user is editing, which
   makes the key non-unique → React reuses inputs across rows, swapping
   names and addresses while typing.
2. Two signers can momentarily share the same partial input, again
   producing duplicate keys.

Fix: every row gets a stable opaque `signerId` generated when the row
is created; the component uses `signerId` as the React key. Address
becomes regular state, free to be empty/duplicate transiently without
breaking React identity.

The same fix applied to both `useWalletFlowState` (new wallet) and
`useMigrationWalletFlowState` (migration). The shared `signerRows.ts`
module emits the id and keeps the parallel arrays in sync.

`reviewSignersCardKey.test.ts` is a tripwire suite that:
- greps the source to assert the raw address is never used as a key
- verifies both flow-state hooks expose signerIds parallel to
  signersAddresses

This catches future regressions structurally — the type system can't
enforce \"don't use address as key\", but a regex over source can.

Test plan
- 171/171 tests pass deterministically on top of #237
- Typecheck clean

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
feat(server): AuditLog table, DB indexes, security hardening, observability
Comprehensive server-side hardening pass: closes auth/security gaps,
adds an append-only AuditLog for security-relevant events, indexes
frequently queried columns, centralizes ctx typing, and lands shared
auth helpers.

## Database (already deployed to prod via prisma migrate deploy)

- New `AuditLog` table for append-only security audit trail
  (auth flows, wallet/transaction mutations, privilege grants, signer
  changes). Five indexes for the common access patterns.
- Btree indexes on Wallet/NewWallet `ownerAddress`, Signable+Transaction
  `walletId`/`state`/`(walletId, state)`, Proxy `walletId`/`userId`/
  `(walletId, isActive)`/`(userId, isActive)`, Ballot `walletId`,
  BalanceSnapshot `walletId`/`(walletId, snapshotDate)`.
- GIN indexes on Wallet/NewWallet `signersAddresses` (array_ops) — the
  signer-membership query was a full table scan.
- Restored `Crowdfund` model declaration (production drift: table exists
  in prod but was never declared in main's schema; see PR description
  for full archaeology). Marked as retained-but-unused.
- WalletBotAccess: `@@unique` -> `@@id` to match prod (drift from
  PR #207 / commit 1facdc3 where schema and migration disagreed at
  landing).
- Ballot.updatedAt: restored `@default(now()) @updatedAt` to match
  prod's column default (drift accumulated across multiple commits).
- Ballot.anchorUrls / anchorHashes: added `DEFAULT ARRAY[]::TEXT[]` to
  match the schema's `@default([])` annotation.

## Observability primitives

- `src/lib/observability/audit.ts` — `audit(db, event)` emitter; never
  throws (audit miss must not break user flow); redacts secrets in
  metadata before write.
- `src/lib/observability/logger.ts` — structured logger; JSON in prod,
  human-readable in dev; never logs raw tokens/signatures/cookies.

## Security fixes (Wave 1-3)

- Closed `ownerAddress === "all"` bypass in `assertWalletAccess`. The
  string "all" was being treated as a wildcard owner — any session
  could claim ownership of any wallet whose `ownerAddress` happened to
  contain that literal.
- `lookupMultisigWallet`: validate stake-credential-hash format before
  query (prevents prefix-match abuse and full-table scans on malformed
  input).
- Centralized rate-limit and request-guard surface (`src/lib/security/
  rateLimit.ts`, `requestGuards`). Bot routes now use bot-scoped
  rate limit; user routes use IP-scoped.
- `verifyJwt`: stricter token-type narrowing; explicit `isBotJwt`
  predicate.
- `walletSession`: tighter expiry handling, no implicit refresh.

## Auth helpers (Wave 8)

- New `src/server/api/auth.ts` consolidates `requireSessionAddress`,
  `getSessionAddresses`, and wallet-access checks that were duplicated
  in nearly every router. One source of truth, one place to extend.
- All routers and v1 API handlers migrated.

## ctx typing (Wave 2)

- New `AuthCtx` and `TRPCContext` exported from `src/server/api/trpc.ts`.
- All router helpers use `AuthCtx` instead of `any`.
- `protectedProcedure` middleware: type-narrows `sessionWallets`,
  `primaryWallet`, `sessionAddress` correctly.

## Audit emitters (Wave 5)

Wired into:
- auth flow (login success/failure, JWT mint, bot auth)
- wallet mutations (create, update, archive, transfer, signer changes)
- signable + transaction mutations (sign, reject, broadcast)
- bot privilege grants

All emitters fire after the underlying action and never block it.

## SSRF defense for `/api/v1/og`

The OG metadata endpoint now:
- requires https, denies non-allowlisted hosts
- DNS-resolves and rejects private/loopback/link-local addresses
- denies upstream redirects (no auto-follow)

`OG_ALLOWED_HOSTS` env var configures the allow list; "*" allows any
public host (still SSRF-guarded).

## Test infrastructure

- jest.config.mjs — moduleNameMapper for CSS, transformIgnorePatterns
  for ESM-only deps (superjson, @trpc, @meshsdk, jose, etc.)
- setupEnv.cjs — pre-test env bootstrap (SKIP_ENV_VALIDATION=1, dummy
  DB/JWT/Blockfrost values) so `src/env.js` doesn't throw on import.
- Frozen wall clock (`Date.now`/`new Date`) for byte-identical test
  runs; real timer APIs preserved.
- `__mocks__/styleMock.cjs` — CSS imports mock for jest.

## Tests

- New: `og.test.ts` (SSRF tripwire suite — 9 cases for the og handler).
- New: `signing.test.ts` (source tripwires preventing the
  `return true ? signature : undefined` regression and similar).
- Updated existing tests to match Jest 30 strict mock typing
  (jest.fn<...>() generics) and new ctx fields.

## Verification

- Typecheck clean
- All 165 staged-suite tests pass deterministically across two runs
- Migration `20260510160404_audit_log_and_indexes` already applied to
  the multisig Supabase production DB — `prisma migrate deploy` on
  this branch is a no-op (idempotent).

Depends on: #236 (build fix; without it `next build --webpack` will
crash on `/wallets/[wallet]/transactions/new`).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
fix(build): lazy-init mainnet provider; remove global sideEffects:false
Two interacting issues caused the production build to crash on routes
that imported resolve-adahandle (notably /wallets/[wallet]/transactions/new):

1. resolve-adahandle.tsx called getProvider(1) at module top level. Under
   `next build` page-data collection, every server-rendered page imports
   that module, which constructs BlockfrostProvider(undefined) when
   NEXT_PUBLIC_BLOCKFROST_API_KEY_MAINNET isn't readable at build time
   (e.g. SKIP_ENV_VALIDATION=true). The constructor throws and the
   webpack runtime reports it as a generic "factory error".

2. next.config.js had `optimization.sideEffects: false` set globally,
   which tells webpack that *every* file is side-effect-free. That
   silently strips global CSS imports and any module-level initialization,
   masking issues like (1) until you hit a route that exercises them.

Fix:
- Lazy-init the mainnet provider with a cached singleton, so import is
  free and instantiation only happens when a caller actually resolves a
  handle (always client-side).
- Remove the global sideEffects:false override. Per-package sideEffects
  declarations in package.json are the correct mechanism; the global
  override was masking real bugs.
- Move swagger-ui-react CSS import from api-docs.tsx into _app.tsx so
  Next.js Pages Router's "global CSS only from _app" rule is satisfied.

Verified locally: `next build --webpack` completes; both
/wallets/[wallet]/transactions/new and /api-docs render.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
chore(ci): gate typecheck/test/build; add dependabot
PR checks were silently passing because typecheck, test, and build steps
were marked continue-on-error, so the gates only reported status, never
blocked merges. Drop continue-on-error from those three; keep it on lint
until the rule set is cleaned up.

Add dependabot config for npm + github-actions, with grouping for
@meshsdk/*, next/@next/*, prisma/@prisma/*, @trpc/*, and @types/*.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
chore(nix): update `flake.lock`
Flake lock file updates:

• Updated input 'advisory-db':
    'github:rustsec/advisory-db/930c3aa' (2026-04-25)
  → 'github:rustsec/advisory-db/881a159' (2026-05-07)
• Updated input 'blockfrost-tests':
    'github:blockfrost/blockfrost-tests/ee42a8a' (2026-04-24)
  → 'github:blockfrost/blockfrost-tests/fd5a2ba' (2026-05-07)
• Updated input 'crane':
    'github:ipetkov/crane/60c8293' (2026-04-25)
  → 'github:ipetkov/crane/6d015ea' (2026-05-06)
• Updated input 'fenix':
    'github:nix-community/fenix/f374034' (2026-04-25)
  → 'github:nix-community/fenix/f54d645' (2026-05-09)
• Updated input 'fenix/rust-analyzer-src':
    'github:rust-lang/rust-analyzer/8954b66' (2026-04-21)
  → 'github:rust-lang/rust-analyzer/73ca1d4' (2026-05-08)
• Updated input 'flake-parts':
    'github:hercules-ci/flake-parts/3107b77' (2026-04-01)
  → 'github:hercules-ci/flake-parts/0678d89' (2026-05-05)
• Updated input 'flake-parts/nixpkgs-lib':
    'github:nix-community/nixpkgs.lib/333c4e0' (2026-03-29)
  → 'github:nix-community/nixpkgs.lib/f590132' (2026-04-26)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/10e7ad5' (2026-04-21)
  → 'github:nixos/nixpkgs/0c88e1f' (2026-05-05)
shellFor: route through buildPackages so cross targets evaluate
`shellFor` (both v1 and v2) ran into "no C compiler provided for
this platform" under cross targets where the host stdenv has no
cc — most visibly ghcjs, also wasm and android-prebuilt.  Three
distinct issues, all fixed here:

1. `mkShell` itself was `pkgs.mkShell` (host-stdenv-bound).
   `pkgs.bashInteractive`'s eval reads `stdenv.cc.isClang`, and
   under ghcjs cross there is no cc → throw.  Pass
   `mkShell = pkgs.buildPackages.mkShell` to both shellForV1 and
   shellForV2 so the shell drv lives on the build platform
   regardless of the host (the user opens the shell on their
   build host anyway).

2. `pkgs.runCommand` was building `packageEnv` and `wrappedGhc`
   (the wrapped GHC the shell exports as `shell.ghc`) against
   the cross stdenv.  Once these landed in the shell's
   `nativeBuildInputs`, mkShell's dep-splicing eventually
   re-evaluated the cross stdenv's bash via setup hooks, hitting
   the same cc.isClang.  Switch both to
   `pkgs.pkgsBuildBuild.runCommand` and `pkgs.pkgsBuildBuild.makeWrapper`
   so the wrappers themselves are build-platform drvs.  (Native
   builds are unaffected — `pkgsBuildBuild == pkgs` there.)

3. `store = composedStore` was a top-level attr to mkShell.
   Recent nixpkgs's `mkDerivation` folds unknown top-level attrs
   into `env` and type-checks each value, and `isDerivation
   composedStore` forces evaluation of the cross stdenv's bash
   the same way.  Move `store` into `passthru.store` — mkDerivation
   lifts passthru into the result drv anyway, so `shell.store`
   continues to resolve.

Verified `tests.with-packages.test-shell` now evaluates under both
the `aarch64-darwin.unstable.ghc9141.ghcjs` (cross) and `.native`
jobsets, and `shell.store` still resolves on both.
dummy-ghc: add Android branch matching real cross-ghc capabilities
`dummy-ghc` is the eval-time stand-in cabal-install runs against
during plan-to-nix.  Its `--info` output shapes plan.json's
per-unit `configure-args` (which feed `pkgHashConfigInputs` and
therefore the UnitId).  When the dummy reports different
capabilities than the real cross-ghc the slice rebuilds with,
plan-nix and slice compute different UnitIds and the slice's
expected-package check fails.

The "otherwise" branch (Linux / Darwin / native) reports
`Support shared libraries: YES`, `GHC Dynamic: YES`, RTS ways
including the full `_dyn` family, and `Stage: 2`.  Real Android
cross-ghc (`aarch64-unknown-linux-android-ghc 9.14.1`) is a
stage-1 NDK build with no `_dyn` ways and no `Support shared
libraries` field at all (its `GHC Dynamic` is `NO`).  Falling
through to "otherwise" made plan-nix record `--enable-shared`
for Android packages while the slice's real ghc silently flipped
to `--disable-shared`, forking the UnitId on
`pkgHashSharedLib`.

Add an explicit Android branch.  Reference values cross-checked
against `aarch64-unknown-linux-android-ghc --info` on the live
9.14.1 cross GHC derivation; key fields:
  * Support dynamic-too: YES   (GHC's flag, kept like real ghc)
  * GHC Dynamic: NO            (was YES — drove --enable-shared)
  * RTS ways: no `_dyn` family
  * Stage: 1
  * No `Support shared libraries` field (cabal interprets
    absence as no-shared-libs, matching real ghc)

Verified `tests.extra-hackage` slices for
`x86_64-linux.unstable.ghc9141.aarch64-android-prebuilt` no
longer fail the slice's UnitId-alignment check.  (The `.run`
step still fails because qemu-aarch64 can't load Android's
`/system/bin/linker64` to actually exec the binary — that's a
pre-existing runtime issue, not a v2 alignment problem.)

For musl, real and dummy ghc already agree
(`Support dynamic-too: YES, GHC Dynamic: YES`); the
`executable-static: True` injection in
`modules/cabal-project.nix` stays — it's a user-opt-in flag,
not capability-derived from `ghc --info`.
flake.lock: Update
Flake lock file updates:

• Updated input 'haskell-nix':
    'github:input-output-hk/haskell.nix/cad2645' (2026-01-03)
  → 'github:input-output-hk/haskell.nix/3971e5c' (2026-05-09)
• Removed input 'haskell-nix/cabal-32'
• Updated input 'haskell-nix/hackage':
    'github:input-output-hk/hackage.nix/05797dd' (2026-01-03)
  → 'github:input-output-hk/hackage.nix/bb7c1b8' (2026-05-09)
• Updated input 'haskell-nix/hackage-for-stackage':
    'github:input-output-hk/hackage.nix/fba3874' (2026-01-03)
  → 'github:input-output-hk/hackage.nix/cdfa372' (2026-05-09)
• Added input 'haskell-nix/hls-2.12':
    'github:haskell/haskell-language-server/7d983de' (2025-09-24)
• Updated input 'haskell-nix/iserv-proxy':
    'github:stable-haskell/iserv-proxy/1506051' (2025-08-15)
  → 'github:stable-haskell/iserv-proxy/3f7b281' (2026-04-08)
• Updated input 'haskell-nix/nixpkgs-2511':
    'github:NixOS/nixpkgs/b0924ea' (2025-12-01)
  → 'github:NixOS/nixpkgs/74b8795' (2026-04-09)
• Updated input 'haskell-nix/nixpkgs-unstable':
    'github:NixOS/nixpkgs/c1cb7d0' (2025-12-01)
  → 'github:NixOS/nixpkgs/1304392' (2026-04-11)
• Updated input 'haskell-nix/stackage':
    'github:input-output-hk/stackage.nix/8a7333a' (2026-01-03)
  → 'github:input-output-hk/stackage.nix/166b880' (2026-05-07)
flake.lock: Update
Flake lock file updates:

• Updated input 'haskellNix':
    'github:input-output-hk/haskell.nix/cc939d0' (2026-02-23)
  → 'github:input-output-hk/haskell.nix/3971e5c' (2026-05-09)
• Removed input 'haskellNix/cabal-32'
• Updated input 'haskellNix/hackage':
    'github:input-output-hk/hackage.nix/06f38c7' (2026-02-23)
  → 'github:input-output-hk/hackage.nix/bb7c1b8' (2026-05-09)
• Updated input 'haskellNix/hackage-for-stackage':
    'github:input-output-hk/hackage.nix/2d5bbb1' (2026-02-23)
  → 'github:input-output-hk/hackage.nix/cdfa372' (2026-05-09)
• Updated input 'haskellNix/iserv-proxy':
    'github:stable-haskell/iserv-proxy/91ef7ff' (2026-02-04)
  → 'github:stable-haskell/iserv-proxy/3f7b281' (2026-04-08)
• Updated input 'haskellNix/nixpkgs-2511':
    'github:NixOS/nixpkgs/b0924ea' (2025-12-01)
  → 'github:NixOS/nixpkgs/74b8795' (2026-04-09)
• Updated input 'haskellNix/nixpkgs-unstable':
    'github:NixOS/nixpkgs/c1cb7d0' (2025-12-01)
  → 'github:NixOS/nixpkgs/1304392' (2026-04-11)
• Updated input 'haskellNix/stackage':
    'github:input-output-hk/stackage.nix/862226e' (2026-02-23)
  → 'github:input-output-hk/stackage.nix/166b880' (2026-05-07)
• Updated input 'iohk-nix':
    'github:input-output-hk/iohk-nix/a704b93' (2025-11-12)
  → 'github:input-output-hk/iohk-nix/9de0011' (2026-05-05)
• Updated input 'iohk-nix/blst':
    'github:supranational/blst/8c7db7f' (2025-02-12)
  → 'github:supranational/blst/6d960cd' (2025-06-06)