github agglayer/aggkit v0.11.0-rc11

latest releases: v0.12.0-rc4, v0.12.0-rc3, v0.12.0-rc2...
pre-releaseone month ago

aggkit v0.11.0-rc11

Changes since v0.11.0-rc10 (11 commits).

🚀 What's New

  • SQLite-backed cache for the bridge tracker (#1822, #1852): a new bridgetracker/db package can back the tracker's supervised-bridges registry and the activity cache with SQLite instead of pure in-memory, so already-resolved bridges survive a proxy restart. Opt-in via Tracker.DBPath — see Configuration Updates. Pruning/retention is still a no-op on the SQLite adapter for now (rows accumulate unboundedly once enabled).
  • Tracker step dates now come from on-chain facts, not "now" (#1864): each step's start_date/end_date is derived from its own deterministic on-chain fact (block timestamps, claim tx, settlement tx, etc.) where one exists, instead of always being stamped with the poll tick's timestamp. Also fixes a latent nil-pointer bug in UpdateStep.
  • Concurrency for tracker/activity scans (#1844): the activity engine now scans up to MaxConcurrentNetworkScans networks in parallel (default 10), and the tracker's poll tick resolves up to MaxConcurrentResolutions active bridges at once (default 50) instead of one at a time. Also makes the per-call bridge-service HTTP timeout configurable and fixes the "skip step" fallback incorrectly overwriting a step's real error type.
  • Bridge tracker resilience once a bridge is already claimed (#1839): if an intermediate step can no longer resolve its historical fact (e.g. pruned RPC history), the tracker now falls back to the destination network's own isClaimed() check and marks unresolvable prior steps as Skipped instead of getting permanently stuck in TrackingStatusError.
  • Error message redaction (#1845, part of #1862): raw backend URLs, host:port, and bare IPs (sometimes carrying API keys) are no longer echoed into client-facing fields (ErrorStep.Description, ErrorData.Message, CertificateData.Error, activity endpoint errors/warnings, proxy JSON error bodies). Redaction happens automatically — no config flag.
  • Gated network auto-registration (#1855, part of #1862): bridgeservicefinder previously served any network discovered after Start immediately. New AutoRegisterNewNetworks flag lets operators freeze the served set at start; late-discovered networks are recorded as pending and surfaced on the health endpoint instead. Defaults to true (today's behavior).
  • Per-destination autoclaim cursors / stricter bridge-service health / stricter emitter verification (#1651, #1689, #1751 via #1854):
    • autoclaim_ler_cursor is now keyed by (source_network, destination_network) — a claimer added later no longer inherits or corrupts another destination's progress. Ships with migration autoclaim0003.
    • Bridge-service /health now reports per-source sync status (L1 info tree, GERs, claims) instead of pure liveness, backed by a short-lived cache and bounded compute timeout. Additive — still always returns HTTP 200.
    • A BridgeEvent-shaped log is now rejected unless it's confirmed to come from the network's real bridge contract (resolved from Tracker.BridgeAddrs, or via the bridge service finder when the network is absent from that map). Previously any contract emitting a matching log was silently accepted.
  • Independent L1 finality knobs + claim trimming (#1846, #1847 via #1849): fixes L1InfoTreeSync.Finality() incorrectly returning the multidownloader's BlockFinality instead of its own, which caused the aggsender startup guard to reject valid mixed-finality configs. Also restores trimming (instead of a hard error) for claims whose GER exists on L1 but isn't yet under the selected L1 info root — needed for the relaxed/conservative finality combination this PR enables.

⚠️ Breaking Changes

  • 🔌 API/CLI: bridgeservicefinder.Finder gains a PendingNetworks() []PendingNetwork method — a compile-time breaking change for any out-of-repo implementer of the interface. All in-repo implementers (proxy, tracker, autoclaim, test doubles) are already updated. types.HealthResponse gains an optional pending_networks field, which is backwards compatible for existing clients.
  • 🔌 Behavioral: a network absent from Tracker.BridgeAddrs now must resolve its bridge address via bridgeservicefinder.Finder.BridgeAddress to have its BridgeEvent logs accepted; previously a matching log from any emitter was accepted for such networks. A genuine mismatch now correctly rejects the tx as ErrBridgeTxNotABridge.
  • No behavior change at default settings for the redaction or auto-registration changes.

📋 Configuration Updates

aggkit (main binary)

  1. New AutoRegisterNewNetworks under [AutoClaim.BridgeServiceFinder]. Default true (no behavior change). With false, networks discovered after startup are recorded as pending instead of being served live. (#1862)
[AutoClaim.BridgeServiceFinder]
HealthCheckPath = "/"
HealthCheckTimeout = "5s"
RequireAllHealthyOnStart = false
AutoRegisterNewNetworks = true   # <-- new
IgnoreNetworkIDs = []

proxy — proxy/config/default.go

  • [BridgeServiceFinder] gains AutoRegisterNewNetworks (also overridable via env var CDK_PROXY_BRIDGESERVICEFINDER_AUTOREGISTERNEWNETWORKS). (#1862)
  • [Tracker] gains DBPath, MaxConcurrentResolutions, ActivityPollInterval, ActivityRegisterResolveTimeout, ActivityMaxConcurrentRefreshes, and changes the default of L2InjectionLookbackBlocks. BridgeAddrs is unchanged as a field but is now an override on top of bridge-service-finder resolution rather than the sole source of truth (see Breaking Changes). (#1822/#1852, #1844, #1854)
  • [Tracker.ActivitySourceBridgeService] gains Timeout and MaxConcurrentNetworkScans. (#1844)

Full example (proxy/config/default.go, only the sections with changes shown):

[BridgeServiceFinder]
HealthCheckPath = "/"
HealthCheckTimeout = "5s"
RequireAllHealthyOnStart = false
AutoRegisterNewNetworks = true   # <-- 🆕 New: freeze the served network set at startup when false; late-discovered networks are recorded as pending instead of served live
IgnoreNetworkIDs = []

[Tracker]
RetentionPeriod = "10m"
IdleTimeout = "30m"
ActivityIdleTimeout = "30m"
L1BlockFinality = "LatestBlock"
L2BlockFinality = "LatestBlock"
MaxTrackedBridges = 100000
L2InjectionLookbackBlocks = 10000000    # <-- 🔄 Changed (was 1000): how many blocks the L2GlobalExitRootAddress fallback scans backwards before giving up

DBPath = ""                             # <-- 🆕 New: SQLite file for the supervised-bridges registry; empty keeps the in-memory adapter (opt-in, don't point at a shared/world-writable dir like /tmp)
MaxConcurrentResolutions = 50           # <-- 🆕 New: how many active bridges the engine's poll tick resolves at once
ActivityPollInterval = "30s"            # <-- 🆕 New: how often the activity engine refreshes every supervised from_address in the background
ActivityRegisterResolveTimeout = "10s"  # <-- 🆕 New: how long a freshly registered from_address's first request waits for the engine's immediate refresh before answering
ActivityMaxConcurrentRefreshes = 10     # <-- 🆕 New: how many supervised addresses the activity engine's poll tick refreshes at once

[Tracker.ActivitySourceBridgeService]
PageSize = 100
Timeout = "30s"                  # <-- 🆕 New: per-HTTP-call timeout for the activity source's bridge-service client (previously hardcoded to 30s)
MaxConcurrentNetworkScans = 10   # <-- 🆕 New: how many networks GET /activity/from/{address} scans at once

🔌 API Updates

Bridge tracker REST API (GET /tracker/v1/*) — CurrentAPIRevision bumped from 2 → 5 (bridgetracker/types/health.go):

  1. rev 3 — new step status value "skipped" (StepStatusSkipped) and error type "skipped" (StepErrorSkipped): a step that can no longer be verified, but whose bridge was independently confirmed on the destination network (ClaimChecker.IsClaimed), now moves to skipped instead of getting stuck in error. (#1839)
  2. rev 4 — corrects rev 3: the step that actually triggered the fallback keeps its own real error_type (transient/permanent) instead of always being overwritten to "skipped"; other steps skipped alongside it that were never attempted now omit error entirely instead of carrying a placeholder. (#1844)
  3. rev 5 — GET /tracker/v1/health gains two fields, both optional/backwards-compatible:
    • start_date (RFC3339, the instant the instance started)
    • pending_networks[] (networks discovered after startup and not activated because AutoRegisterNewNetworks = false; omitted when empty)
      (#1862)
// GET /tracker/v1/health (example, rev 5)
{
  "status": "ok",
  "instance_id": "e3b0c442-...",
  "start_date": "2026-09-23T08:00:00Z",
  "config_sha1": "a1b2c3...",
  "version": { "...": "..." },
  "pending_networks": [
    {
      "network_id": 3,
      "rollup_address": "0x...",
      "block_number": 12345,
      "first_seen": "2026-09-23T08:05:00Z",
      "reason": "AutoRegisterNewNetworks=false: network appeared after Start"
    }
  ]
}

Other tracker API changes (not their own revision bump, folded into rev 5 / content-only changes to existing fields):

  • Automatic error redaction: ErrorData.message, ErrorStep.description[] and CertificateData.error no longer expose raw URLs/host:port/IPs — they're redacted in the type's own MarshalJSON before leaving over HTTP/WebSocket. (#1845)
  • New StepErrorType value "warning" (StepErrorWarning): the step completed normally (status: done) but some optional piece of data could not be resolved deterministically (e.g. the L2 GER-injection block); not a failure. (#1864)
  • LERUpdateResult gains block_timestamp (previously only network_id/ler/block_number).
  • GET /activity/from/{from_address} can now return 503 with a Retry-After header (seconds = ActivityPollInterval) when a freshly registered address's first refresh isn't ready yet — previously this blocked the response until ActivityRegisterResolveTimeout; now, once that elapses, it answers 503 instead of empty data. (#1844)
  • bridgeservicefinder.Finder (Go package, not HTTP) gains the PendingNetworks() []PendingNetwork method — breaks the interface for any out-of-repo implementer (see Breaking Changes). (#1862)

Aggkit bridge service API (GET /bridge/v1/health, bridgeservice/types.HealthCheckResponse) — additive, still always returns HTTP 200:

  • New sync_status: "done" / "pending" / "error".
  • New details with a per-component summary (l1, l2, l2_ger), each with is_active, is_synced (optional) and error (optional). (#1854, part of #1689)
// GET /bridge/v1/health (example)
{
  "status": "ok",
  "time": "2026-09-23T08:00:00Z",
  "version": "v0.11.0-rc11",
  "sync_status": "pending",
  "details": {
    "l1": { "is_active": true, "is_synced": false },
    "l2": { "is_active": true, "is_synced": true },
    "l2_ger": { "is_active": true }
  }
}

Swagger docs regenerated for both services: bridgetracker/api/docs/*, bridgeservice/docs/* (make generate-swagger-docs).

✅ Testing & Validation

New/expanded e2e coverage:

  • test/e2e/proxy_autoregister_test.go (TestProxyAutoRegisterNewNetworks, #1855)
  • test/e2e/proxy_tracker_redaction_test.go (error redaction, #1845)
  • test/e2e/finality_knobs_test.go (independent L1 finality knobs, #1846/#1847)
  • test/e2e/autoclaim_test.go (per-destination autoclaim cursors, #1651)
  • test/e2e/health_test.go (bridge-service /health sync status, #1689)
  • test/e2e/proxy_tracker_test.go — extended for the emitter-verification fix (#1751)

Plus extensive new/updated unit tests across bridgetracker/db, bridgetracker/domain, bridgetracker/sources, bridgeservicefinder, autoclaim/bridgedetector, and common/redact_test.go.

📦 Full Changelog

PR Summary
#1862 fix(bridgetracker): redact error URLs + gate bridgeservicefinder auto-registration
#1864 feat(bridgetracker): step start_date/end_date from on-chain facts, not now
#1852 feat(bridgetracker): add SQLite-backed cache for tracker/activity state (#1822)
#1854 fix: per-destination autoclaim cursors, bridge-service health sync status, bridgetracker emitter verification (#1651, #1689, #1751)
#1844 feat(bridgetracker): parallelize activity/tracker scans and make bridge-service timeout configurable. Fix skip step
#1849 fix: make L1 finality knobs independent — Finality() passthrough (#1846) and claim trimming (#1847)
#1839 fix(bridgetracker): skip unverifiable intermediate steps once a bridge is already claimed
#1857, #1858, #1859, #1860 ci: pin/fix Claude Interactive workflow (claude-code-action version, model pin, toggling show_full_output)

Full Changelog: v0.11.0-rc10...v0.11.0-rc11

Don't miss a new aggkit release

NewReleases is sending notifications on new releases.