Skip to content

feat(exit-certificate-claimer): prepare exit_certificate_claimer for deployment#1690

Merged
joanestebanr merged 11 commits into
feature/exit-certificate-toolfrom
feat/exit-ceritificate-tool-docker-claimer
Jul 10, 2026
Merged

feat(exit-certificate-claimer): prepare exit_certificate_claimer for deployment#1690
joanestebanr merged 11 commits into
feature/exit-certificate-toolfrom
feat/exit-ceritificate-tool-docker-claimer

Conversation

@joanestebanr

@joanestebanr joanestebanr commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

🔄 Changes Summary

Packaging and deployment preparation for the exit_certificate_claimer service.

  • 🐳 Dockerfile: ship the exit_certificate (generator) and
    exit_certificate_claimer (HTTP service) binaries in the aggkit image under
    /usr/local/bin, so the tools travel alongside aggkit and the same image can
    be used to deploy the exit_certificate_claimer.

  • 📦 docker-compose: add tools/exit_certificate_claimer/docker/docker-compose.yml
    to run the exit_certificate_claimer as a read-only HTTP service. It builds from
    the repo-root Dockerfile, overrides the entrypoint with the
    exit_certificate_claimer binary, and derives its whole config from the
    exit_certificate parameters.toml via --exit-certificate-config. Mounts the
    exit_certificate working dir at /data (read-write, since L1 sync keeps its
    SQLite DBs updated) and wires host.docker.internal so a host-pointing
    l1RpcUrl is reachable on Linux. Defines a healthcheck: and documents the
    volume-permission expectations (the container runs as appuser, uid 1000).

  • 🩺 healthcheck subcommand: exit_certificate_claimer healthcheck probes
    GET /claimer/v1/health of a running claimer and exits 0 (healthy) or 1.
    The production image ships without a shell or curl, so the binary itself
    performs the HTTP probe; the docker-compose healthcheck is backed by it.

  • 📜 JSON logs: new --log-json flag switches the claimer logs to JSON
    format (aggkit's production log environment, zap JSON encoder); the default
    remains the human-readable console format. Fatal startup errors are emitted
    through the configured logger too (JSON or console), instead of urfave/cli's
    plain Error: ... line. Exposed in docker-compose via CLAIMER_LOG_JSON
    (default false).

  • 🧪 e2e docker mode: 60-claim_exit_certificate_funds.sh gains
    CLAIMER_MODE=docker to run the claimer through the docker-compose deployment
    instead of a host binary (a sibling config copy rewrites 127.0.0.1/localhost
    to host.docker.internal so the container reaches the Kurtosis-published L1
    RPC). The CI workflow sets it, so the e2e exercises the same artifact users
    deploy — image build from the repo-root Dockerfile included — and now also
    triggers on tools/exit_certificate_claimer/** and Dockerfile changes. The
    compose service user is parameterizable (CLAIMER_USER, default appuser) so
    L1 sync can write its SQLite DBs whatever the host uid.

  • ⚙️ .env.example: documented env vars (EXIT_TOOL_DIR, EXIT_CERT_CONFIG,
    CLAIMER_ADDRESS, CLAIMER_PORT, CLAIMER_LOG_JSON, AGGKIT_IMAGE) consumed
    by compose.

  • 🩹 e2e fix: agglayer_status (agglayer_certificate_status.sh --wait) now
    retries on the transient agglayer error raised right after the network starts,
    before the node has classified the network:

    ERROR: get network info: failed to get network info: Code: NotFound,
    Message: Network type could not be determined,
    Details: [Reason: GET_NETWORK_INFO_ERROR_KIND_UNKNOWN_NETWORK_TYPE,
    Domain: agglayer-node.grpc-api.v1.node-state-service.get_network_info. ]
    

    waitForSettled previously aborted on any GetNetworkInfo error, making the
    e2e prepare_network.sh step fail intermittently. This specific error is now
    treated as transient and polling continues (respecting the configured interval
    and timeout); other errors still abort.

⚠️ Breaking Changes

  • None.

📋 Config Updates

  • New env-var driven setup for the exit_certificate_claimer (see .env.example).
    No changes to the existing TOML config.
  • New optional --log-json CLI flag (CLAIMER_LOG_JSON in compose) to emit
    JSON-formatted logs; defaults to the previous console format.

✅ Testing

  • 🖱️ Manual:
    • make build-tools / go build ./tools/exit_certificate/... ./tools/exit_certificate_claimer/... build cleanly.
    • From tools/exit_certificate_claimer/docker/: cp .env.example .env then
      docker compose up --build starts the exit_certificate_claimer serving on CLAIMER_PORT.
    • exit_certificate_claimer healthcheck verified against a live health endpoint
      (exit 0) and a closed port (exit 1).
    • Ran the claimer with and without --log-json and verified the log output
      switches between the zap JSON encoder and the console format, including
      fatal startup errors (logged as an error line in the active format, exit code 1).
  • 🧪 Unit: go test ./tools/exit_certificate/scripts/agglayer_status/ covers the
    new retry-on-undetermined-network-type path;
    go test ./tools/exit_certificate_claimer/... covers the healthcheck probe
    (URL building, 200/non-200/unreachable, end-to-end against the real router).
  • 🚀 E2E: the exit-certificate tool E2E (CI) runs the claim step with
    CLAIMER_MODE=docker, validating the full docker deployment end to end:
    compose build of the aggkit image, container startup deriving the config,
    in-container L1 sync, /claimer/v1/health readiness and claiming every bridge
    exit against the containerized service.

📝 Notes

  • The exit_certificate_claimer runs from the same working directory the
    exit_certificate tool used (config + output/), so no separate config file is needed.
  • PR base: feature/exit-certificate-tool (packaging/deployment work only).

🤖 Generated with Claude Code

@joanestebanr joanestebanr self-assigned this Jul 2, 2026
@joanestebanr joanestebanr added the exit_certificate_tool Tool to create a final exit certificate label Jul 2, 2026
joanestebanr and others added 5 commits July 8, 2026 10:05
The build stage already compiles every tool, but the final runtime image
only copied aggkit and aggsender_find_imported_bridge. Add the
exit_certificate generator and the exit_certificate_claimer HTTP service
so they ship in the image.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…vice

Add a docker-compose.yml (and .env.example) that runs the
exit_certificate_claimer over the exit_certificate working directory.

- Builds the aggkit image and overrides the entrypoint with the claimer
  binary (exec form; the production image has no shell).
- Mounts the exit_certificate run dir (parameters.toml + output/) at /data
  read-write, and derives the claimer config via --exit-certificate-config,
  which enables L1 sync.
- All parameters configurable via env vars: EXIT_TOOL_DIR, EXIT_CERT_CONFIG,
  CLAIMER_ADDRESS, CLAIMER_PORT, AGGKIT_IMAGE.
- Wires host.docker.internal so an l1RpcUrl pointing at the host is
  reachable from the container on Linux.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rk type could not be determined"

Right after the network starts, the agglayer node has not yet classified
the network and GetNetworkInfo fails with:

  Code: NotFound, Message: Network type could not be determined,
  Reason: GET_NETWORK_INFO_ERROR_KIND_UNKNOWN_NETWORK_TYPE

waitForSettled aborted on any GetNetworkInfo error, so the e2e
prepare_network.sh step (agglayer_certificate_status.sh --wait) failed
intermittently. Treat this specific error as transient and keep polling,
respecting the configured interval and timeout. Other errors still abort.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…cker/

Relocate docker-compose.yml and .env.example under
tools/exit_certificate_claimer/docker/ and fix the relative paths that
shift one level deeper: build context ../.. -> ../../.. and the default
EXIT_TOOL_DIR ../exit_certificate -> ../../exit_certificate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a --version flag (via cli.VersionPrinter) and include the aggkit
build traceability (version, git rev/branch, build date, Go version,
OS/arch) in the GET /health response through the new HealthResponse /
VersionInfo types. Build the binary with ldflags in the Makefile so the
values are populated. Docs updated in README and SPEC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@joanestebanr joanestebanr force-pushed the feat/exit-ceritificate-tool-docker-claimer branch from 61f9dca to ae3b1a1 Compare July 8, 2026 08:06
joanestebanr and others added 3 commits July 8, 2026 10:25
…se healthcheck

The production image ships without a shell or curl, so the claimer binary
itself performs the HTTP probe: `exit_certificate_claimer healthcheck` GETs
/claimer/v1/health and exits 0 (healthy) or 1. Wired as the docker-compose
healthcheck; also documented volume-permission expectations (container runs
as appuser) addressing the PR review suggestions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…laim step

60-claim_exit_certificate_funds.sh now supports CLAIMER_MODE=docker to run the
claimer through tools/exit_certificate_claimer/docker/docker-compose.yml instead
of a host background binary (default CLAIMER_MODE=binary, unchanged behavior).

Docker mode writes a sibling copy of the exit_certificate config with
127.0.0.1/localhost rewritten to host.docker.internal so the container reaches
the Kurtosis-published L1 RPC, mounts the config dir at /data (relative
outputDir keeps resolving), and tears the stack down on exit. AGGKIT_IMAGE and
CLAIMER_DOCKER_BUILD=0 allow reusing a prebuilt image.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ompose

The exit-certificate E2E workflow now sets CLAIMER_MODE=docker so the claim
step exercises the claimer via its docker-compose deployment (the artifact
users actually deploy) instead of a host binary, and also triggers on
tools/exit_certificate_claimer/** and Dockerfile changes.

The compose service user is now parameterizable (CLAIMER_USER, default the
image's appuser): the e2e passes the host uid:gid so L1 sync can write its
SQLite DBs inside the mounted config dir regardless of the runner's uid.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@agglayer agglayer deleted a comment from claude Bot Jul 8, 2026
@joanestebanr

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: f944a5f07b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

joanestebanr and others added 3 commits July 10, 2026 10:31
Adds a --log-json boolean flag that switches the logger to aggkit's
production environment (zap JSON encoder); the default stays the
human-readable console format. Exposed in docker-compose via
CLAIMER_LOG_JSON (default false).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…json

Startup errors (config load, missing data files, ...) were returned to
main and printed as a plain "Error: ..." line, escaping the JSON log
stream. With --log-json they now go through the configured logger and
the process exits 1 via cli.Exit; without the flag the plain error line
is preserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gger unconditionally

Fatal startup errors now always go through the configured logger (console
format by default, JSON under --log-json) instead of urfave/cli's plain
"Error: ..." line, matching how aggkit's cmd handles them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@joanestebanr joanestebanr merged commit 3e2c1b4 into feature/exit-certificate-tool Jul 10, 2026
23 of 24 checks passed
@joanestebanr joanestebanr deleted the feat/exit-ceritificate-tool-docker-claimer branch July 10, 2026 10:04
joanestebanr added a commit that referenced this pull request Jul 10, 2026
…deployment (#1690)

## 🔄 Changes Summary
Packaging and deployment preparation for the exit_certificate_claimer
service.
- 🐳 **Dockerfile**: ship the `exit_certificate` (generator) and
`exit_certificate_claimer` (HTTP service) binaries in the aggkit image
under
`/usr/local/bin`, so the tools travel alongside `aggkit` and the same
image can
  be used to deploy the exit_certificate_claimer.
- 📦 **docker-compose**: add
`tools/exit_certificate_claimer/docker/docker-compose.yml`
to run the exit_certificate_claimer as a read-only HTTP service. It
builds from
  the repo-root Dockerfile, overrides the entrypoint with the
  exit_certificate_claimer binary, and derives its whole config from the
exit_certificate `parameters.toml` via `--exit-certificate-config`.
Mounts the
exit_certificate working dir at `/data` (read-write, since L1 sync keeps
its
SQLite DBs updated) and wires `host.docker.internal` so a host-pointing
`l1RpcUrl` is reachable on Linux. Defines a `healthcheck:` and documents
the
volume-permission expectations (the container runs as `appuser`, uid
1000).
- 🩺 **healthcheck subcommand**: `exit_certificate_claimer healthcheck`
probes
`GET /claimer/v1/health` of a running claimer and exits 0 (healthy) or
1.
The production image ships without a shell or curl, so the binary itself
performs the HTTP probe; the docker-compose healthcheck is backed by it.
- 📜 **JSON logs**: new `--log-json` flag switches the claimer logs to
JSON
format (aggkit's `production` log environment, zap JSON encoder); the
default
remains the human-readable console format. Fatal startup errors are
emitted
through the configured logger too (JSON or console), instead of
urfave/cli's
plain `Error: ...` line. Exposed in docker-compose via
`CLAIMER_LOG_JSON`
  (default `false`).
- 🧪 **e2e docker mode**: `60-claim_exit_certificate_funds.sh` gains
`CLAIMER_MODE=docker` to run the claimer through the docker-compose
deployment
instead of a host binary (a sibling config copy rewrites
`127.0.0.1`/`localhost`
to `host.docker.internal` so the container reaches the
Kurtosis-published L1
RPC). The CI workflow sets it, so the e2e exercises the same artifact
users
deploy — image build from the repo-root Dockerfile included — and now
also
triggers on `tools/exit_certificate_claimer/**` and `Dockerfile`
changes. The
compose service user is parameterizable (`CLAIMER_USER`, default
`appuser`) so
  L1 sync can write its SQLite DBs whatever the host uid.
- ⚙️ **.env.example**: documented env vars (`EXIT_TOOL_DIR`,
`EXIT_CERT_CONFIG`,
`CLAIMER_ADDRESS`, `CLAIMER_PORT`, `CLAIMER_LOG_JSON`, `AGGKIT_IMAGE`)
consumed
  by compose.
- 🩹 **e2e fix**: `agglayer_status` (`agglayer_certificate_status.sh
--wait`) now
retries on the transient agglayer error raised right after the network
starts,
  before the node has classified the network:

  ```
  ERROR: get network info: failed to get network info: Code: NotFound,
  Message: Network type could not be determined,
  Details: [Reason: GET_NETWORK_INFO_ERROR_KIND_UNKNOWN_NETWORK_TYPE,
Domain: agglayer-node.grpc-api.v1.node-state-service.get_network_info. ]
  ```

`waitForSettled` previously aborted on any `GetNetworkInfo` error,
making the
e2e `prepare_network.sh` step fail intermittently. This specific error
is now
treated as transient and polling continues (respecting the configured
interval
  and timeout); other errors still abort.

## ⚠️ Breaking Changes
- None.

## 📋 Config Updates
- New env-var driven setup for the exit_certificate_claimer (see
`.env.example`).
  No changes to the existing TOML config.
- New optional `--log-json` CLI flag (`CLAIMER_LOG_JSON` in compose) to
emit
  JSON-formatted logs; defaults to the previous console format.

## ✅ Testing
- 🖱️ **Manual**:
- `make build-tools` / `go build ./tools/exit_certificate/...
./tools/exit_certificate_claimer/...` build cleanly.
- From `tools/exit_certificate_claimer/docker/`: `cp .env.example .env`
then
`docker compose up --build` starts the exit_certificate_claimer serving
on `CLAIMER_PORT`.
- `exit_certificate_claimer healthcheck` verified against a live health
endpoint
    (exit 0) and a closed port (exit 1).
- Ran the claimer with and without `--log-json` and verified the log
output
switches between the zap JSON encoder and the console format, including
fatal startup errors (logged as an error line in the active format, exit
code 1).
- 🧪 **Unit**: `go test
./tools/exit_certificate/scripts/agglayer_status/` covers the
  new retry-on-undetermined-network-type path;
`go test ./tools/exit_certificate_claimer/...` covers the healthcheck
probe
(URL building, 200/non-200/unreachable, end-to-end against the real
router).
- 🚀 **E2E**: the exit-certificate tool E2E (CI) runs the claim step with
`CLAIMER_MODE=docker`, validating the full docker deployment end to end:
compose build of the aggkit image, container startup deriving the
config,
in-container L1 sync, `/claimer/v1/health` readiness and claiming every
bridge
  exit against the containerized service.

## 📝 Notes
- The exit_certificate_claimer runs from the same working directory the
exit_certificate tool used (config + `output/`), so no separate config
file is needed.
- PR base: `feature/exit-certificate-tool` (packaging/deployment work
only).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

exit_certificate_tool Tool to create a final exit certificate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant