Skip to content

feat(faucet-app): migrate faucet-app into nitrolite repo#776

Open
nksazonov wants to merge 5 commits into
mainfrom
feat/faucet-app
Open

feat(faucet-app): migrate faucet-app into nitrolite repo#776
nksazonov wants to merge 5 commits into
mainfrom
feat/faucet-app

Conversation

@nksazonov
Copy link
Copy Markdown
Contributor

@nksazonov nksazonov commented May 21, 2026

Best reviewed commit by commit.

What's included

5aeb7281 — General migration

Copies the faucet-app server into faucet-app/ inside this repo. Switches the Go module from the published github.com/erc7824/nitrolite v1.1.1 to local code via a replace directive pointing at ../../, so changes to pkg/ and sdk/go are immediately picked up without a publish step.

b5f82223 — Rename: clearnode → nitronode

Pure textual rename across all files: package name, directory (internal/clearnodeinternal/nitronode), identifiers (ClearnodeClientNitronodeClient, clearnodeURLnitronodeURL, env var CLEARNODE_URLNITRONODE_URL), log messages, config keys in Helm values, and README references. No logic changes.

06dcb25b — Client hardening fixes

Improvements to internal/nitronode/client.go identified during a review against the current sdk/go API:

  • Exponential backoff on reconnectreconnectLocked retries up to 3 times (300 ms → 600 ms → 2 s) instead of giving up immediately; each attempt includes a post-connect Ping to confirm liveness before accepting the new client.
  • WithApplicationID("faucet") — tags all requests to the node for server-side observability.
  • WithErrorHandler — connection errors are now routed through the faucet logger instead of being silently dropped.
  • Token support cachedvalidateTokenSupport calls GetAssets only once per connection; result is stored in tokenSupported and reset on reconnect, eliminating an unnecessary RPC round-trip on every HTTP request.
  • Close() uses write lock — prevents a race where Close() could close a freshly reconnected client instead of the stale one.
  • ownerAddress cached at constructionGetOwnerAddress() no longer takes a lock; the address is derived from the immutable signer and never changes across reconnects.
  • Enforced balance warning — logs a warning when the on-chain enforced balance is below the channel balance, signalling uncheckpointed funds at risk.
  • "0" fallback removedTransfer no longer silently replaces a server-reported zero amount with the configured tip amount.

Summary by CodeRabbit

Release Notes

  • New Features

    • Token faucet server with /requestTokens and /info REST API endpoints
    • Per-wallet and IP-based rate limiting with configurable cooldown
    • JSON logging and graceful shutdown handling
  • Documentation

    • Server documentation and configuration guide
    • Example environment configuration file
  • Chores

    • Docker containerization support for server deployment

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

📝 Walkthrough

Walkthrough

This PR implements a complete Go-based token faucet server with Nitronode SDK integration. It adds configuration management, structured logging, an HTTP REST API with rate limiting, graceful shutdown handling, comprehensive tests, Docker containerization, and full documentation.

Changes

Token Faucet Server Implementation

Layer / File(s) Summary
Configuration loading and environment setup
faucet-app/server/.gitignore, faucet-app/server/.env.example, faucet-app/server/internal/config/config.go, go.mod
Environment variables for server port, owner private key, token parameters, and trusted proxies are loaded and validated; optional .env file support and dependency management for parsing decimals and durations.
Structured logging initialization
faucet-app/server/internal/logger/logger.go
JSON-formatted logger initialized via logrus with configurable log levels and package-level forwarding helpers for Info/Warn/Error/Debug/Fatal output.
Nitronode SDK client wrapper with connection lifecycle
faucet-app/server/internal/nitronode/client.go, faucet-app/server/internal/nitronode/client_validation_test.go
Wraps Nitrolite SDK with liveness detection, exponential backoff reconnection, token support validation, minimum balance verification, and transfer execution; includes input validation tests for private key and URL parsing.
Rate limiting by address and IP
faucet-app/server/internal/server/ratelimiter.go
Per-key cooldown tracking with lazy eviction; atomic check-and-record for combined address/IP rate limiting with configurable cooldown duration.
HTTP server endpoints and request handling
faucet-app/server/internal/server/server.go
Gin-based REST server with POST /requestTokens for token requests and GET /info for metadata; validates Ethereum addresses, enforces rate limits, checks Nitronode connectivity/operational status, and returns structured JSON responses.
HTTP handler and rate limiting tests
faucet-app/server/internal/server/server_test.go
Covers request token success/failure paths (invalid address, connection/operational/transfer failures), rate limiting scoping by wallet and IP, cooldown persistence across failed transfers, and info endpoint validation.
Application startup and graceful shutdown
faucet-app/server/main.go
Loads configuration, initializes logger, creates and validates Nitronode client, starts HTTP server asynchronously, and performs graceful shutdown on signals with client cleanup.
Multi-stage Docker build for deployment
faucet-app/server/Dockerfile
Builder stage compiles static Linux binary with pre-downloaded dependencies; runtime stage uses non-root user with CA certificates on alpine:3.23.3.
Server documentation and API specifications
faucet-app/server/README.md
Comprehensive guide covering features, architecture, quick-start, configuration, REST API contracts, WebSocket reconnection behavior, security practices, build/development workflows, error handling, and troubleshooting.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • dimast-x
  • philanton

🐰 A faucet flows with logic so clean,
Rate limits dance where keys convene,
From config to close, the threads align,
Docker wraps it all—a server so fine!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the PR: migrating the faucet-app server into the nitrolite repository. It is specific, concise, and clearly communicates the primary change from a high level.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/faucet-app

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
faucet-app/server/.env.example (1)

17-49: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

.env.example is out of sync with runtime config keys.

Line 29 uses CLEARNODE_URL while Config expects NITRONODE_URL, and required COOLDOWN_PERIOD is missing. SIGNER_PRIVATE_KEY is also not used by Config. This will break startup for users copying this template.

Suggested fix
-# Clearnode Configuration
+# Nitronode Configuration
@@
-# REQUIRED: Used for EIP-712 authentication with Clearnode
+# REQUIRED: Used for EIP-712 authentication with Nitronode
 OWNER_PRIVATE_KEY=your_owner_private_key_here_without_0x_prefix
 
-# Private key for transaction signing (without 0x prefix)
-# REQUIRED: Used for signing transfer transactions - must be different from OWNER_PRIVATE_KEY
-SIGNER_PRIVATE_KEY=your_signer_private_key_here_without_0x_prefix
-
-# Clearnode WebSocket URL
-# REQUIRED: The WebSocket endpoint for your Clearnode instance
-CLEARNODE_URL=wss://clearnode.example.com/ws
+# Nitronode WebSocket URL
+# REQUIRED: The WebSocket endpoint for your Nitronode instance
+NITRONODE_URL=wss://nitronode.example.com/ws
@@
-# REQUIRED: The symbol of the token to distribute (must be supported by Clearnode)
+# REQUIRED: The symbol of the token to distribute (must be supported by Nitronode)
 TOKEN_SYMBOL=usdc
@@
 MIN_TRANSFER_COUNT=5
+
+# Cooldown between requests per wallet/IP
+# REQUIRED: Duration (e.g. 24h, 1h30m)
+COOLDOWN_PERIOD=24h
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@faucet-app/server/.env.example` around lines 17 - 49, The .env.example keys
are out of sync with the runtime Config: update the file so environment variable
names and required keys match the Config object—replace CLEARNODE_URL with
NITRONODE_URL (used by Config), add the missing COOLDOWN_PERIOD entry with a
short description and required format, and remove or mark SIGNER_PRIVATE_KEY as
unused (since Config does not reference it) so users copying the template won't
cause startup failures; reference the Config symbol and the environment keys
CLEARNODE_URL, NITRONODE_URL, COOLDOWN_PERIOD, and SIGNER_PRIVATE_KEY when
making the edits.
🧹 Nitpick comments (6)
faucet-app/scripts/auto_tag.sh (1)

1-23: ⚡ Quick win

Add fail-fast shell options to avoid cascading tag/push failures.

If git tag fails (e.g., concurrent run/tag exists), the script should stop immediately.

Suggested fix
 #!/bin/sh
+set -eu
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@faucet-app/scripts/auto_tag.sh` around lines 1 - 23, The script can continue
after failures (e.g., concurrent tag) so add fail-fast shell options at the top
(enable set -euo pipefail or equivalent) to cause immediate exit on errors/unset
vars and ensure pipelines fail fast; also ensure the critical commands git tag
and git push are not masked by conditional substitutions—leave git tag
"$new_rc_tag" and git push origin "$new_rc_tag" as-is so they will abort the
script on failure, and verify variables last_rc_tag, last_stable_tag and
new_rc_tag are referenced after enabling -u to catch unset usage.
faucet-app/server/internal/nitronode/client.go (1)

46-46: ⚡ Quick win

Add doc comment for exported constructor.

NewClient is exported and needs a doc comment.

As per coding guidelines, **/*.go: Follow gofmt formatting and include doc comments on all exported names in Go code.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@faucet-app/server/internal/nitronode/client.go` at line 46, Add a Go doc
comment immediately above the exported constructor NewClient that begins with
"NewClient" and briefly explains what the function constructs/returns, its
important parameters (privateKeyHex, nitronodeURL, tokenSymbol, tipAmount,
minTransferCount) and the return values (*Client, error); place the comment
directly above the func NewClient declaration so it follows Go doc conventions
and gofmt formatting.
faucet-app/server/internal/logger/logger.go (1)

9-68: ⚡ Quick win

Document exported logger symbols.

Exported Log, Initialize, and all exported wrapper funcs need doc comments.

As per coding guidelines, **/*.go: Follow gofmt formatting and include doc comments on all exported names in Go code.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@faucet-app/server/internal/logger/logger.go` around lines 9 - 68, Add Go doc
comments for the exported symbols to satisfy the project's linting/gofmt rules:
add a package-level comment if missing, then add brief one-line comments above
the exported Log variable and the exported functions Initialize, Info, Infof,
Warn, Warnf, Error, Errorf, Debug, Debugf, Fatal, and Fatalf describing their
purpose and behavior (e.g., "Log is the package logger.", "Initialize sets up
the package logger with the given level.", "Info logs an info message."), and
run gofmt/golangci-lint to ensure formatting and comment style conform to
guidelines.
faucet-app/server/internal/config/config.go (1)

11-46: ⚡ Quick win

Add Go doc comments to exported names.

Config, Load, and Validate are exported and need doc comments.

As per coding guidelines, **/*.go: Follow gofmt formatting and include doc comments on all exported names in Go code.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@faucet-app/server/internal/config/config.go` around lines 11 - 46, Add proper
Go doc comments for the exported symbols: place a full-sentence comment
immediately above the Config type, the Load function, and the (c *Config)
Validate method describing their purpose and behavior (start the sentence with
the symbol name, e.g., "Config ...", "Load ...", "Validate ..."). Ensure the
comments mention any important semantics (e.g., Load reads .env/env and returns
a validated Config, Validate checks/parses fields like StandardTipAmountDecimal
and CooldownPeriodDuration) and run gofmt/golangci-lint if needed to keep
formatting consistent.
faucet-app/server/internal/server/server.go (1)

35-57: ⚡ Quick win

Add doc comments to all exported types/functions.

Server, FaucetRequest, FaucetResponse, ErrorResponse, NewServer, and Start are exported but undocumented (Line 35, Line 42, Line 46, Line 55, Line 59, Line 198). Please add Go doc comments for each exported name.

As per coding guidelines, "Follow gofmt formatting and add doc comments on all exported names in Go code".

Also applies to: 59-59, 198-198

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@faucet-app/server/internal/server/server.go` around lines 35 - 57, Add Go doc
comments for each exported symbol: Server, FaucetRequest, FaucetResponse,
ErrorResponse, NewServer, and Start. For types (Server, FaucetRequest,
FaucetResponse, ErrorResponse) add a one-line comment immediately above the type
that begins with the type name and briefly states its purpose (e.g., "Server
manages ...", "FaucetRequest represents ..."). For functions NewServer and Start
add comments that begin with the function name and describe what they do and the
meaning of their parameters/returns. Place comments directly above the
corresponding declarations so they follow godoc conventions.
faucet-app/server/internal/server/server_test.go (1)

70-70: ⚡ Quick win

Do not ignore test setup/marshal errors with _.

These lines ignore returned errors (_) from logger.Initialize and json.Marshal. Please assert/require these errors explicitly so test failures are deterministic.

Suggested pattern
- body, _ := json.Marshal(FaucetRequest{UserAddress: testAddress})
+ body, err := json.Marshal(FaucetRequest{UserAddress: testAddress})
+ require.NoError(t, err)
- _ = logger.Initialize("debug")
+ if err := logger.Initialize("debug"); err != nil {
+   panic(err)
+ }

As per coding guidelines, "Always check and return errors in Go; never ignore with _".

Also applies to: 79-79, 106-106, 122-122, 140-140, 158-158, 177-177, 199-199, 227-227, 251-252, 276-277

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@faucet-app/server/internal/server/server_test.go` at line 70, Replace ignored
error returns in the tests with explicit assertions: for calls like
logger.Initialize(...) and json.Marshal(...) (and other occurrences listed)
capture the returned error and assert/require it's nil (e.g., err :=
logger.Initialize("debug"); require.NoError(t, err) or similar) so test setup
failures fail deterministically; do the same for json.Marshal results (data, err
:= json.Marshal(...); require.NoError(t, err)) and any other places where `_ =`
was used to discard errors (use the test's assert/require helpers used elsewhere
in server_test.go).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@faucet-app/.github/dependabot.yml`:
- Around line 8-10: Dependabot config uses package-ecosystem: "gomod" but the
directory is still set to "/server/" which no longer matches the migrated module
layout; update the directory value in .github/dependabot.yml to the repository
root or the actual Go module directory for this faucet-app (replace the current
"/server/" value with "/" or the correct module path) so Dependabot runs gomod
updates against the proper module.

In `@faucet-app/.github/workflows/main-pr.yml`:
- Around line 19-25: Update the GitHub Actions workflow to use the repository's
post-migration paths: change occurrences referencing the old root "server" to
"faucet-app/server" (e.g., go-version-file: 'server/go.mod',
cache-dependency-path: 'server/go.sum', and working-directory: server used in
the "Test" step). Also apply the same replacement for the duplicate occurrences
noted around lines 52-53 so all workflow steps (setup, cache, test, build) point
to faucet-app/server instead of server.

In `@faucet-app/.github/workflows/main-push.yml`:
- Around line 19-25: The workflow uses root-relative paths that still include a
removed top-level "server/" prefix (e.g. go-version-file: 'server/go.mod',
cache-dependency-path: 'server/go.sum', working-directory: server), so update
all occurrences to the repository's current layout by removing the obsolete
"server/" prefix: set go-version-file to 'go.mod', cache-dependency-path to
'go.sum' (or other correct relative file), and change working-directory entries
(e.g. under the "Test" step and any build/tag/deploy steps) to '.' or remove the
working-directory so actions run from the repo root; apply the same replacement
to the other mentioned occurrences (lines ~48, ~91-92, ~155-159) to ensure all
steps reference the correct files and directories.

In `@faucet-app/.github/workflows/stable-tag.yml`:
- Around line 41-42: The workflow references migrated paths that no longer
resolve: replace the literal "context: server" with the repo-relative build
context (e.g., "context: ./server" or the actual relative directory that
contains the Dockerfile) and update every "chart/..." reference to the new
Helm/chart location in the repository (use the exact new relative path to the
chart directory); keep "push: true" as-is and apply the same fixes to the other
occurrences (the block around the later chart references mentioned) so all steps
use the correct migrated paths.
- Line 6: The on.push.tags glob 'v[0-9]+.[0-9]+.[0-9]+' won't match SemVer tags
in GitHub Actions; replace the tag matcher with a permissive glob like 'v*' or
'v*.*.*' in stable-tag.yml (change the value under on.push.tags) and add a
validation step in your workflow job that checks the tag string (use
github.ref_name or GITHUB_REF) with a proper SemVer regex before proceeding so
only true SemVer tags trigger the release steps.

In `@faucet-app/chart/README.md.gotmpl`:
- Line 20: Update the helm install source URL that still points to
"git+https://github.com/erc7824/clearnode@chart?ref=main" so it references the
migrated faucet chart in the nitrolite repo/path; replace that repository path
in the helm command(s) (the "helm install my-release ..." lines found in the
template) with the correct git URL for the nitrolite faucet chart (e.g., the
equivalent "git+https://github.com/nitrolite/<faucet-repo>`@chart`?ref=main") for
all occurrences (including the duplicate at the other noted line).
- Around line 88-90: The README uses a stale selector `-l app=clearnode`; update
the troubleshooting command to use the chart's actual pod labels used in the
templates (for example the standard chart labels such as
`app.kubernetes.io/name=<chart-name>` or the release-specific label rendered by
the template like `app: {{ include "faucet-app.name" . }}`). Replace `kubectl
logs -l app=clearnode` with a selector that matches the Deployment/Pod labels
defined in the chart templates (e.g. `kubectl logs -l
app.kubernetes.io/name=faucet-app` or the exact label key/value used in your
templates) so the command reliably finds pods.

In `@faucet-app/chart/templates/helpers/_common.tpl`:
- Around line 73-75: The environment mapping uses stale keys: replace
.Values.service.http.internalPort with the actual key .Values.service.http.port
and update the clearnode env var to match the values file by renaming
CLEARNODE_URL to NITRONODE_URL and using .Values.config.nitronodeWsUrl (instead
of .Values.config.clearnodeWsUrl) in the template so the env var name and value
key align with the provided values.

In `@faucet-app/chart/templates/helpers/_component.tpl`:
- Around line 45-54: The resources helper ("faucet-app.component.resources")
currently sources limit values and ephemeral-storage from .requests, causing
incorrect mappings; update the template so requests use .requests.cpu,
.requests.memory, and .requests.ephemeral-storage with their defaults, and
limits use .limits.cpu, .limits.memory, and .limits.ephemeral-storage with their
defaults (keeping the existing fallback values like "100m", "128Mi", "100Mi");
specifically change the two limits lines that reference .requests.* to reference
.limits.* and change ephemeral-storage lines to read the correct
.requests.ephemeral-storage and .limits.ephemeral-storage fields.

In `@faucet-app/server/internal/config/config.go`:
- Around line 46-67: The Validate method is missing a range check for
Config.MinTransferCount; update func (c *Config) Validate() to return an error
when c.MinTransferCount is zero or negative (e.g. fmt.Errorf("MIN_TRANSFER_COUNT
must be a positive integer")), so the minimum-balance guard remains effective;
add this check (and error message) alongside the other validations in Validate,
referencing the MinTransferCount field and the Validate method.
- Around line 32-37: The Load function should only fall back to cleanenv.ReadEnv
when the .env file is missing—change the error handling in Load to detect
file-missing errors using os.IsNotExist(err) or errors.As(err, *os.PathError)
and return the original cleanenv.ReadConfig error for any other parse/IO errors;
update references to the Load function and the Config type accordingly. In
(*Config).Validate ensure MinTransferCount is range-validated (e.g., > 0) and
return a clear validation error from Validate when out of range. Add concise Go
doc comments for the exported Config type, Load function, and (*Config).Validate
method. Finally, refactor the Config construction to follow the
functional-options pattern used elsewhere (e.g., provide a NewConfig or Load
accepting variadic option functions) so this file aligns with sdk/go/config.go
conventions.

In `@faucet-app/server/internal/nitronode/client_validation_test.go`:
- Line 34: The test currently discards both results from NewClient; change the
call to capture the values (e.g., client, err := NewClient(...)) and assert the
expected outcome instead of using `_`; for example, check err is nil (using
t.Fatalf/t.Fatal or your test helper like require.NoError) and that client is
non-nil (or validate its fields) so the test fails on unexpected errors from
NewClient; reference the NewClient function and the test function where the call
occurs to locate and update the assertion logic.

In `@faucet-app/server/internal/nitronode/client.go`:
- Around line 205-210: validateTokenSupport can set c.tokenSupported based on an
old sdkClient after a reconnect; to fix, capture the current client into a local
variable at the top of validateTokenSupport (e.g., local := c.sdkClient), use
that local for all calls and comparisons, and only update c.tokenSupported
(under c.mu) if c.sdkClient is still equal to that captured local; ensure any
early returns that set tokenSupported perform the same check so the cache isn't
updated for a replaced client.
- Line 160: The call to newClient.Close() currently ignores its error; change it
to capture and handle the error (e.g. err := newClient.Close()) and then either
return the wrapped error or log it with context instead of discarding it. Update
the code path that calls newClient.Close() (reference symbol: newClient.Close)
to check err and propagate or log a descriptive message (wrap with fmt.Errorf or
use the existing logger) so cleanup failures are not silently ignored.

In `@faucet-app/server/internal/server/server.go`:
- Around line 198-202: Change Server.Start to accept a context.Context and stop
using s.router.Run; instead create an http.Server with Addr set to ":" +
s.config.ServerPort and Handler set to s.router, start it in a goroutine
(server.ListenAndServe), then block on ctx.Done and call server.Shutdown(ctx)
(with a short timeout context if you want deadline control) to perform graceful
shutdown. Update the function signature Server.Start(ctx context.Context) and
ensure errors from ListenAndServe and Shutdown are returned (or logged) so
callers can observe startup/shutdown failures; reference Server.Start,
s.router.Run, s.config.ServerPort, and server.Shutdown in your changes.

In `@faucet-app/server/main.go`:
- Around line 28-29: The current logger.Infof call prints cfg.NitronodeURL
verbatim which may leak embedded credentials; update the logging in main.go
where logger.Infof is called to parse cfg.NitronodeURL (e.g., using url.Parse)
and log only safe parts such as Scheme and Host or replace userinfo with a
redacted placeholder before passing to logger.Infof, keeping cfg.ServerPort as
before and ensuring the log never outputs cfg.NitronodeURL raw.

In `@faucet-app/server/README.md`:
- Around line 20-25: Update README references from the old clearnode names to
the migrated nitronode names: replace any mention of the package
`internal/clearnode` with `internal/nitronode` and change the environment
variable `CLEARNODE_URL` to `NITRONODE_URL` (and any other CLEARNODE_* vars) so
docs match the codebase; ensure the “Clearnode Client” heading and explanatory
text are renamed to “Nitronode Client” and that the description refers to the
Nitrolite SDK `sdk.Client` usage under `internal/nitronode`.

---

Outside diff comments:
In `@faucet-app/server/.env.example`:
- Around line 17-49: The .env.example keys are out of sync with the runtime
Config: update the file so environment variable names and required keys match
the Config object—replace CLEARNODE_URL with NITRONODE_URL (used by Config), add
the missing COOLDOWN_PERIOD entry with a short description and required format,
and remove or mark SIGNER_PRIVATE_KEY as unused (since Config does not reference
it) so users copying the template won't cause startup failures; reference the
Config symbol and the environment keys CLEARNODE_URL, NITRONODE_URL,
COOLDOWN_PERIOD, and SIGNER_PRIVATE_KEY when making the edits.

---

Nitpick comments:
In `@faucet-app/scripts/auto_tag.sh`:
- Around line 1-23: The script can continue after failures (e.g., concurrent
tag) so add fail-fast shell options at the top (enable set -euo pipefail or
equivalent) to cause immediate exit on errors/unset vars and ensure pipelines
fail fast; also ensure the critical commands git tag and git push are not masked
by conditional substitutions—leave git tag "$new_rc_tag" and git push origin
"$new_rc_tag" as-is so they will abort the script on failure, and verify
variables last_rc_tag, last_stable_tag and new_rc_tag are referenced after
enabling -u to catch unset usage.

In `@faucet-app/server/internal/config/config.go`:
- Around line 11-46: Add proper Go doc comments for the exported symbols: place
a full-sentence comment immediately above the Config type, the Load function,
and the (c *Config) Validate method describing their purpose and behavior (start
the sentence with the symbol name, e.g., "Config ...", "Load ...", "Validate
..."). Ensure the comments mention any important semantics (e.g., Load reads
.env/env and returns a validated Config, Validate checks/parses fields like
StandardTipAmountDecimal and CooldownPeriodDuration) and run gofmt/golangci-lint
if needed to keep formatting consistent.

In `@faucet-app/server/internal/logger/logger.go`:
- Around line 9-68: Add Go doc comments for the exported symbols to satisfy the
project's linting/gofmt rules: add a package-level comment if missing, then add
brief one-line comments above the exported Log variable and the exported
functions Initialize, Info, Infof, Warn, Warnf, Error, Errorf, Debug, Debugf,
Fatal, and Fatalf describing their purpose and behavior (e.g., "Log is the
package logger.", "Initialize sets up the package logger with the given level.",
"Info logs an info message."), and run gofmt/golangci-lint to ensure formatting
and comment style conform to guidelines.

In `@faucet-app/server/internal/nitronode/client.go`:
- Line 46: Add a Go doc comment immediately above the exported constructor
NewClient that begins with "NewClient" and briefly explains what the function
constructs/returns, its important parameters (privateKeyHex, nitronodeURL,
tokenSymbol, tipAmount, minTransferCount) and the return values (*Client,
error); place the comment directly above the func NewClient declaration so it
follows Go doc conventions and gofmt formatting.

In `@faucet-app/server/internal/server/server_test.go`:
- Line 70: Replace ignored error returns in the tests with explicit assertions:
for calls like logger.Initialize(...) and json.Marshal(...) (and other
occurrences listed) capture the returned error and assert/require it's nil
(e.g., err := logger.Initialize("debug"); require.NoError(t, err) or similar) so
test setup failures fail deterministically; do the same for json.Marshal results
(data, err := json.Marshal(...); require.NoError(t, err)) and any other places
where `_ =` was used to discard errors (use the test's assert/require helpers
used elsewhere in server_test.go).

In `@faucet-app/server/internal/server/server.go`:
- Around line 35-57: Add Go doc comments for each exported symbol: Server,
FaucetRequest, FaucetResponse, ErrorResponse, NewServer, and Start. For types
(Server, FaucetRequest, FaucetResponse, ErrorResponse) add a one-line comment
immediately above the type that begins with the type name and briefly states its
purpose (e.g., "Server manages ...", "FaucetRequest represents ..."). For
functions NewServer and Start add comments that begin with the function name and
describe what they do and the meaning of their parameters/returns. Place
comments directly above the corresponding declarations so they follow godoc
conventions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2079deb1-ba16-4650-b36e-addac1962676

📥 Commits

Reviewing files that changed from the base of the PR and between b983b23 and 06dcb25.

⛔ Files ignored due to path filters (1)
  • faucet-app/server/go.sum is excluded by !**/*.sum
📒 Files selected for processing (40)
  • faucet-app/.github/CODEOWNERS
  • faucet-app/.github/dependabot.yml
  • faucet-app/.github/workflows/main-pr.yml
  • faucet-app/.github/workflows/main-push.yml
  • faucet-app/.github/workflows/stable-tag.yml
  • faucet-app/LICENSE
  • faucet-app/chart/.helmignore
  • faucet-app/chart/Chart.yaml
  • faucet-app/chart/README.md
  • faucet-app/chart/README.md.gotmpl
  • faucet-app/chart/config/sandbox/secrets.yaml
  • faucet-app/chart/config/sandbox/values.yaml
  • faucet-app/chart/config/uat/secrets.yaml
  • faucet-app/chart/config/uat/values.yaml
  • faucet-app/chart/templates/deployment.yaml
  • faucet-app/chart/templates/gateway.yaml
  • faucet-app/chart/templates/helpers/_common.tpl
  • faucet-app/chart/templates/helpers/_component.tpl
  • faucet-app/chart/templates/helpers/_hpa.tpl
  • faucet-app/chart/templates/helpers/_ingress.tpl
  • faucet-app/chart/templates/http-route-redirect.yaml
  • faucet-app/chart/templates/http-route.yaml
  • faucet-app/chart/templates/ingress.yaml
  • faucet-app/chart/templates/podmonitoring.yaml
  • faucet-app/chart/templates/service.yaml
  • faucet-app/chart/values.yaml
  • faucet-app/scripts/auto_tag.sh
  • faucet-app/server/.env.example
  • faucet-app/server/.gitignore
  • faucet-app/server/Dockerfile
  • faucet-app/server/README.md
  • faucet-app/server/go.mod
  • faucet-app/server/internal/config/config.go
  • faucet-app/server/internal/logger/logger.go
  • faucet-app/server/internal/nitronode/client.go
  • faucet-app/server/internal/nitronode/client_validation_test.go
  • faucet-app/server/internal/server/ratelimiter.go
  • faucet-app/server/internal/server/server.go
  • faucet-app/server/internal/server/server_test.go
  • faucet-app/server/main.go

Comment thread faucet-app/.github/dependabot.yml Outdated
Comment thread faucet-app/.github/workflows/main-pr.yml Outdated
Comment thread faucet-app/.github/workflows/main-push.yml Outdated
Comment thread faucet-app/.github/workflows/stable-tag.yml Outdated
Comment thread faucet-app/.github/workflows/stable-tag.yml Outdated
Comment thread faucet-app/server/internal/nitronode/client.go Outdated
Comment thread faucet-app/server/internal/nitronode/client.go
Comment thread faucet-app/server/internal/server/server.go
Comment thread faucet-app/server/main.go Outdated
Comment thread faucet-app/server/README.md Outdated
Copy link
Copy Markdown
Contributor

@philanton philanton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the migration! A bunch of stuff still needs to go before this can land — most of it is leftover from the standalone repo.

Please remove

  • faucet-app/.github/ — only .github/ at repo root is honored by Actions, these workflows never fire. Root CI already covers Go tests, tagging, releases.
  • faucet-app/LICENSE — duplicate of root LICENSE.
  • faucet-app/chart/ — Helm chart with GKE config + ynet-stage/clearnet-* secret refs + canarynet.yellow.com hostnames. Belongs in infra repo, not OSS.
  • faucet-app/scripts/ — only consumed by the removed workflow, goes with it.

Critical bugs

  • faucet-app/server/.env.example — still references CLEARNODE_URL and SIGNER_PRIVATE_KEY. SIGNER_PRIVATE_KEY isn't read by config.go at all (single key drives both signers in nitronode.NewClient). And COOLDOWN_PERIOD is missing entirely, but it's env-required:"true" — server fails to start from a fresh .env.example. Please rewrite.
  • faucet-app/server/README.md — entire doc still says "Clearnode" (~30 hits), references the old internal/clearnode package name, wrong env var names, stale startup log examples. Please rewrite to match internal/nitronode.

Module layout

  • faucet-app/server/go.mod — please drop the nested go.mod/go.sum and fold faucet-app into the root module. Right now it's a separate module with replace github.com/layer-3/nitrolite => ../../, which means root go test ./... doesn't cover faucet-app and CI never tests it. Also the module name faucet-server doesn't follow the repo path convention — should be github.com/layer-3/nitrolite/faucet-app/server like nitronode/, cerebro/, sdk/go/ already are under the root module.

Code-level concerns

  • server/internal/nitronode/client.go:128-169reconnectLocked holds the write lock across time.Sleep (up to ~6s total) and the full Ping round-trip. All readers stall during a reconnect storm. I'd drop the lock before sleeping/pinging and re-acquire only to swap c.sdkClient.
  • server/internal/nitronode/client.go:185-214validateTokenSupport reads cached under RLock, then writes tokenSupported=true under a separate Lock. Race window where N goroutines all call GetAssets() before one wins. Not catastrophic but worth tightening — sync.Once per connection, or do the check+set under write lock.
  • server/internal/server/ratelimiter.go:64-73r.calls only increments on accepted requests, so rejected (cooldown) requests don't trigger eviction. Map grows unbounded under sustained abuse from blocked IPs. I'd increment on rejection too.
  • server/internal/server/server.go:69SetTrustedProxies(nil) is fine for direct exposure but if the deployment puts the faucet behind any ingress/LB (which the chart implies), c.ClientIP() returns the proxy IP and the IP rate-limit collapses to one bucket. Please either expose a TRUSTED_PROXIES config or document the "direct exposure only" assumption.
  • server/internal/nitronode/client.go:146-153oldClient.Close() runs under the write lock. If SDK close does network I/O it stalls all readers. I'd swap the pointer under the lock, then defer the close until after unlock.

Tests pass locally otherwise. Once the cleanup + module flattening lands, the rest is small)

@nksazonov
Copy link
Copy Markdown
Contributor Author

@philanton — addressed all your points in f0af8f29. Full breakdown:

Removed

  • faucet-app/.github/ — all workflows and Dependabot config gone
  • faucet-app/LICENSE — duplicate of root LICENSE removed
  • faucet-app/chart/ — entire Helm chart removed (belongs in infra repo)
  • faucet-app/scripts/ — removed with the workflows

Module layout

  • Dropped faucet-app/server/go.mod and go.sum; faucet-app is now part of the root module (github.com/layer-3/nitrolite)
  • All internal imports updated from faucet-server/internal/... to github.com/layer-3/nitrolite/faucet-app/server/internal/...
  • go test ./faucet-app/... now works from repo root

reconnectLocked lock scope

  • Introduced a dedicated reconnectMu sync.Mutex that serialises reconnect attempts without holding c.mu (the RW lock) during I/O
  • Dial and Ping now happen outside any lock; c.mu is only held briefly for the pointer swap
  • oldClient.Close() is deferred until after c.mu is released

validateTokenSupport race

  • Added tokenMu sync.Mutex to serialise GetAssets calls — only one goroutine fetches at a time, others wait and then hit the fast-path cache
  • Cache write guarded with if c.sdkClient == cl to handle reconnect races

ratelimiter map growth

  • r.calls now increments (and eviction runs) on both accepted and rejected requests, so blocked IPs trigger eviction on every call

SetTrustedProxies

  • Added TRUSTED_PROXIES env var (comma-separated IPs); NewServer calls router.SetTrustedProxies(cfg.TrustedProxyList) when set, otherwise stays nil
  • Documented the "direct exposure only" assumption in both server.go and README.md

server/.env.example — rewritten (done manually): CLEARNODE_URLNITRONODE_URL, SIGNER_PRIVATE_KEY removed, COOLDOWN_PERIOD added

server/README.md — fully rewritten: no Clearnode references, correct package names, updated env vars, new sections for TRUSTED_PROXIES and reconnect behaviour

Copy link
Copy Markdown
Collaborator

@ihsraham ihsraham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks close after the cleanup pass. I left a few focused inline notes. The Dockerfile is the main one I would fix before merge: go test ./faucet-app/... and go test -race ./faucet-app/... pass, but the Docker build currently fails after the module flattening.

Comment thread faucet-app/server/Dockerfile Outdated
COPY . .

# Build the application
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o faucet-server main.go
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flattening the faucet into the root module broke this Dockerfile. With repo root as context, this command runs in /app, where there is no main.go; with faucet-app/server as context, line 7 cannot find go.mod or go.sum.

I would update the image build to use repo-root context and build ./faucet-app/server explicitly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 683804a.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still failing on the current head. I pulled 683804af and docker build -f faucet-app/server/Dockerfile . stops at this step because faucet-app/server/go.mod and go.sum were removed when the faucet was folded into the root module.

The Dockerfile should drop the nested module copy/download step and build from the root module, for example go build -o /build/bin/faucet-server ./faucet-app/server after copying the source tree.

Comment thread faucet-app/server/internal/server/server.go
Comment thread faucet-app/server/.env.example Outdated

# Cooldown between requests per wallet/IP (Go duration format)
# Optional: default is 24h
COOLDOWN_PERIOD=your_cooldown_period_here (e.g., 24h, 1h, 30m)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two lines still do not produce a usable fresh .env. Config requires COOLDOWN_PERIOD, but the comment says it is optional/defaulted, and this sample value is not a valid Go duration.

Please either add a real default in config or make the example something like COOLDOWN_PERIOD=24h and mark it required.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 683804a.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still looks unresolved on 683804af. The example now comments out COOLDOWN_PERIOD and still calls it optional, but config.Config marks it env-required:"true", so a fresh .env copied from this file will fail validation unless the user already has the variable in their shell.

Please either add an env-default:"24h" in config or make the example include an active value like COOLDOWN_PERIOD=24h and mark it required.

Comment thread faucet-app/server/internal/server/server_test.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
faucet-app/server/internal/nitronode/client.go (1)

315-320: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Close() is not actually serialized with reconnects.

Close() only holds c.mu, while reconnect work is coordinated by reconnectMu. An in-flight reconnect can still swap in a fresh client around shutdown, leaving a live connection after Close() returns.

Proposed minimal fix
 func (c *Client) Close() error {
+	c.reconnectMu.Lock()
+	defer c.reconnectMu.Unlock()
+
 	c.mu.Lock()
-	defer c.mu.Unlock()
-	return c.sdkClient.Close()
+	cl := c.sdkClient
+	c.mu.Unlock()
+	return cl.Close()
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@faucet-app/server/internal/nitronode/client.go` around lines 315 - 320,
Close() currently only acquires c.mu, which doesn't block the reconnect path
protected by reconnectMu and can race in a new sdkClient; change Close() to
acquire reconnectMu first and then c.mu (i.e., reconnectMu.Lock(); defer
reconnectMu.Unlock(); then c.mu.Lock(); defer c.mu.Unlock()) so it serialises
with the reconnect logic; ensure the established global lock ordering
(reconnectMu before mu) is documented and that any reconnect code that touches
c.sdkClient follows the same ordering to avoid deadlocks.
♻️ Duplicate comments (1)
faucet-app/server/internal/nitronode/client_validation_test.go (1)

33-37: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert the error path contract explicitly.

When err != nil, the test currently does not assert that client is nil, so a mixed (client != nil, err != nil) regression would still pass.

Suggested assertion tightening
 		client, err := NewClient(key, "ws://localhost:19999", "usdc", decimal.NewFromInt(10), 1)
 		if err == nil {
 			require.NotNil(t, client)
 			require.NoError(t, client.Close())
+		} else {
+			require.Nil(t, client)
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@faucet-app/server/internal/nitronode/client_validation_test.go` around lines
33 - 37, The test calling NewClient(...) doesn't assert the error-path contract
for client when err != nil; update the test around NewClient, client, err so
that if err != nil you explicitly require.Nil(t, client), and otherwise
require.NotNil(t, client) and require.NoError(t, client.Close()); i.e., branch
on err (or use require.NoError to assert success and then check client) to
ensure no regression where client is non-nil while an error is returned.
🧹 Nitpick comments (1)
faucet-app/server/README.md (1)

117-117: 💤 Low value

Add language identifier to fenced code block.

The fenced code block is missing a language identifier, flagged by markdownlint.

♻️ Proposed fix
-```
+```json
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@faucet-app/server/README.md` at line 117, The fenced code block in README.md
is missing a language identifier; update the opening triple-backtick for that
fenced code block to include the language token (e.g., change ``` to ```json) so
markdownlint recognizes it as JSON code and enables proper syntax highlighting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@faucet-app/server/Dockerfile`:
- Around line 8-10: Remove the now-invalid module-copy and download steps:
delete the COPY command that references "faucet-app/server/go.mod" and
"faucet-app/server/go.sum" and the subsequent "RUN cd faucet-app/server && go
mod download" from the Dockerfile, since the faucet-app was migrated to the root
module and dependencies are handled by the root go.mod already copied earlier.

In `@go.mod`:
- Line 62: Update the quic-go dependency from v0.54.0 to v0.57.0: edit the
go.mod entry for module github.com/quic-go/quic-go to use v0.57.0, then run `go
get github.com/quic-go/quic-go@v0.57.0` (or `go get -u`) and `go mod tidy` to
refresh transitive modules and lockfiles; finally rebuild/run tests to confirm
no breakages.

---

Outside diff comments:
In `@faucet-app/server/internal/nitronode/client.go`:
- Around line 315-320: Close() currently only acquires c.mu, which doesn't block
the reconnect path protected by reconnectMu and can race in a new sdkClient;
change Close() to acquire reconnectMu first and then c.mu (i.e.,
reconnectMu.Lock(); defer reconnectMu.Unlock(); then c.mu.Lock(); defer
c.mu.Unlock()) so it serialises with the reconnect logic; ensure the established
global lock ordering (reconnectMu before mu) is documented and that any
reconnect code that touches c.sdkClient follows the same ordering to avoid
deadlocks.

---

Duplicate comments:
In `@faucet-app/server/internal/nitronode/client_validation_test.go`:
- Around line 33-37: The test calling NewClient(...) doesn't assert the
error-path contract for client when err != nil; update the test around
NewClient, client, err so that if err != nil you explicitly require.Nil(t,
client), and otherwise require.NotNil(t, client) and require.NoError(t,
client.Close()); i.e., branch on err (or use require.NoError to assert success
and then check client) to ensure no regression where client is non-nil while an
error is returned.

---

Nitpick comments:
In `@faucet-app/server/README.md`:
- Line 117: The fenced code block in README.md is missing a language identifier;
update the opening triple-backtick for that fenced code block to include the
language token (e.g., change ``` to ```json) so markdownlint recognizes it as
JSON code and enables proper syntax highlighting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2807cb5f-ccc6-4e6a-a3da-17399312c7d4

📥 Commits

Reviewing files that changed from the base of the PR and between 06dcb25 and 683804a.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (12)
  • faucet-app/server/.env.example
  • faucet-app/server/Dockerfile
  • faucet-app/server/README.md
  • faucet-app/server/internal/config/config.go
  • faucet-app/server/internal/logger/logger.go
  • faucet-app/server/internal/nitronode/client.go
  • faucet-app/server/internal/nitronode/client_validation_test.go
  • faucet-app/server/internal/server/ratelimiter.go
  • faucet-app/server/internal/server/server.go
  • faucet-app/server/internal/server/server_test.go
  • faucet-app/server/main.go
  • go.mod
✅ Files skipped from review due to trivial changes (1)
  • faucet-app/server/.env.example

Comment on lines +8 to +10
# Copy faucet-app/server module files and pre-download dependencies
COPY faucet-app/server/go.mod faucet-app/server/go.sum ./faucet-app/server/
RUN cd faucet-app/server && go mod download
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Remove references to deleted module files.

Lines 8-10 attempt to copy faucet-app/server/go.mod and go.sum, but per the PR objectives these files were removed when the faucet-app was migrated to use the root module. The Docker build will fail with a "file not found" error.

🐛 Proposed fix

Since the faucet-app now uses the root module, remove these lines entirely. Dependencies are already downloaded via the root go.mod copied at line 6:

-# Copy faucet-app/server module files and pre-download dependencies
-COPY faucet-app/server/go.mod faucet-app/server/go.sum ./faucet-app/server/
-RUN cd faucet-app/server && go mod download
+# Pre-download dependencies from root module
+RUN go mod download
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@faucet-app/server/Dockerfile` around lines 8 - 10, Remove the now-invalid
module-copy and download steps: delete the COPY command that references
"faucet-app/server/go.mod" and "faucet-app/server/go.sum" and the subsequent
"RUN cd faucet-app/server && go mod download" from the Dockerfile, since the
faucet-app was migrated to the root module and dependencies are handled by the
root go.mod already copied earlier.

Comment thread go.mod
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pkg/term v1.2.0-beta.2 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

What is the first patched version of github.com/quic-go/quic-go that fixes GO-2025-4017 and GO-2025-4233?

💡 Result:

First patched versions of github.com/quic-go/quic-go that fix GO-2025-4017 and GO-2025-4233: GO-2025-4017: v0.54.1 [1]. GO-2025-4233: v0.57.0 [2][3].

Citations:


Upgrade github.com/quic-go/quic-go from v0.54.0.

v0.54.0 is affected by GO-2025-4017 and GO-2025-4233; the patched versions are v0.54.1 (GO-2025-4017) and v0.57.0 (GO-2025-4233). Bump github.com/quic-go/quic-go to v0.57.0 to address both advisories via transitive HTTP/3 paths.

🧰 Tools
🪛 OSV Scanner (2.3.8)

[HIGH] 62-62: github.com/quic-go/quic-go 0.54.0: Panic occurs when queuing undecryptable packets after handshake completion in github.com/quic-go/quic-go

(GO-2025-4017)


[HIGH] 62-62: github.com/quic-go/quic-go 0.54.0: HTTP/3 QPACK Header Expansion DoS in github.com/quic-go/quic-go

(GO-2025-4233)


[HIGH] 62-62: github.com/quic-go/quic-go 0.54.0: quic-go: Panic occurs when queuing undecryptable packets after handshake completion

(GHSA-47m2-4cr7-mhcw)


[HIGH] 62-62: github.com/quic-go/quic-go 0.54.0: quic-go HTTP/3 QPACK Header Expansion DoS

(GHSA-g754-hx8w-x2g6)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@go.mod` at line 62, Update the quic-go dependency from v0.54.0 to v0.57.0:
edit the go.mod entry for module github.com/quic-go/quic-go to use v0.57.0, then
run `go get github.com/quic-go/quic-go@v0.57.0` (or `go get -u`) and `go mod
tidy` to refresh transitive modules and lockfiles; finally rebuild/run tests to
confirm no breakages.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified this one locally. go run golang.org/x/vuln/cmd/govulncheck@latest ./faucet-app/... reports GO-2025-4233 as reachable through faucet-app/server -> gin -> quic-go/http3, and the Go vulnerability database lists github.com/quic-go/quic-go@v0.57.0 as the fixed version.

Since this PR introduces the Gin dependency that pulls in quic-go, I would bump github.com/quic-go/quic-go before merge rather than leave the vulnerable transitive version in the root module.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ihsraham, thanks for the local verification — that confirms the vulnerability is reachable, not just a transitive phantom. Here's the minimal fix:

go.mod change:

-	github.com/quic-go/qpack v0.5.1 // indirect
-	github.com/quic-go/quic-go v0.54.0 // indirect
+	github.com/quic-go/qpack v0.6.0 // indirect
+	github.com/quic-go/quic-go v0.57.0 // indirect

Note: quic-go v0.56.0 updated qpack to v0.6.0, so qpack needs to be bumped alongside quic-go to keep the dependency graph consistent.

After editing go.mod, regenerate the lockfile from the repo root:

go get github.com/quic-go/quic-go@v0.57.0
go mod tidy
go test ./faucet-app/... # confirm no breakage
govulncheck ./faucet-app/... # confirm advisories are cleared

Since quic-go is an indirect dependency here (Gin pulls it in for HTTP/3 transport, not the faucet server directly), the bump doesn't touch any faucet-app source files — only go.mod and go.sum change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants