Skip to content

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

Merged
nksazonov merged 9 commits into
mainfrom
feat/faucet-app
May 27, 2026
Merged

feat(faucet-app): migrate faucet-app into nitrolite repo#776
nksazonov merged 9 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

  • New Features

    • Added a token faucet server application with REST API endpoints for requesting tokens and retrieving server information
    • Implemented rate limiting and cooldown mechanisms per wallet address and IP
    • Added configuration management via environment variables with sensible defaults
  • Documentation

    • Added comprehensive server README with API specifications, configuration guide, and troubleshooting
    • Created .env.example template documenting all configurable parameters
  • Chores

    • Added Docker containerization for the faucet server
    • Added Kubernetes Helm chart for deployment with multi-environment support
    • Updated CI/CD workflows to build and publish Docker images to container registry

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

📝 Walkthrough

Walkthrough

This PR introduces a complete token faucet service as a Go application with Nitronode SDK integration, Kubernetes deployment via Helm chart, and CI/CD pipelines for Docker image building and registry publishing.

Changes

Token Faucet Server Implementation

Layer / File(s) Summary
Configuration loading and environment setup
faucet-app/server/.env.example, faucet-app/server/internal/config/config.go
Environment variables for server port, owner key, token parameters, and trusted proxies are loaded from .env or environment; validation ensures decimal tip amounts, positive durations, and valid IP/CIDR entries.
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 connection liveness checks, automatic exponential-backoff reconnection, per-connection token support caching, minimum balance verification (tip × count), and atomic transfer execution; test coverage validates input error handling.
Rate limiting by address and IP
faucet-app/server/internal/server/ratelimiter.go
Per-key cooldown tracking with lazy eviction; dual-identity rate limiting checks both wallet address and client IP atomically without duplicate writes when they match.
HTTP server, endpoints, and request handling
faucet-app/server/internal/server/server.go, faucet-app/server/internal/server/server_test.go
Gin-based REST API with POST /requestTokens (address validation, normalization, rate limiting, Nitronode checks, transfer execution) and GET /info (metadata); test suite covers success/error paths, rate-limit scoping by wallet and IP, and metadata validation.
Application startup, graceful shutdown, and logging
faucet-app/server/main.go
Loads configuration, initializes JSON logger, logs redacted Nitronode URL, creates Nitronode client, performs operational readiness check, starts HTTP server asynchronously, and handles SIGINT/SIGTERM with cleanup.
Containerization and documentation
faucet-app/server/Dockerfile, faucet-app/server/README.md, faucet-app/server/.gitignore
Multi-stage Docker build (golang:1.25-alpine builder → alpine:3.23.3 runtime with non-root user); README documents features, configuration, REST API contracts, WebSocket behavior, security, and troubleshooting.
Go module dependencies
go.mod
Adds direct dependencies on gin-gonic/gin, ilyakaznacheev/cleanenv, shopspring/decimal, sirupsen/logrus; expands indirect set and bumps klauspost/cpuid/v2 to v2.3.0.

Kubernetes Deployment via Helm Chart

Layer / File(s) Summary
Helm chart metadata and base values
faucet-app/chart/Chart.yaml, faucet-app/chart/values.yaml, faucet-app/chart/.helmignore
Chart metadata (apiVersion v2, name, version 1.0.0); comprehensive default values for runtime config, image/resource settings, service exposure, metrics, probes, autoscaling, and networking.
Reusable Helm template helpers
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
Functions for naming, labeling, pod scheduling, image/pull-secret rendering, environment variables, metrics annotations, replica/probe/resource management, and Kubernetes version-adaptive Ingress/HPA field selection.
Core Kubernetes resource templates
faucet-app/chart/templates/deployment.yaml, faucet-app/chart/templates/service.yaml, faucet-app/chart/templates/secret.yaml
Deployment with rolling updates, container args/probes/env/secret injection; Service with HTTP ClusterIP; Secret with hook-enabled SOPS encryption.
Networking templates: Ingress, Gateway, HTTPRoute
faucet-app/chart/templates/ingress.yaml, faucet-app/chart/templates/gateway.yaml, faucet-app/chart/templates/http-route.yaml, faucet-app/chart/templates/http-route-redirect.yaml
Ingress for TLS termination; Gateway API Gateway (HTTP/HTTPS listeners); HTTPRoute for HTTPS traffic and HTTP-to-HTTPS redirect (301).
Observability: PodMonitoring
faucet-app/chart/templates/podmonitoring.yaml
Conditional PodMonitoring resource for Prometheus scrape configuration.
Environment-specific configuration
faucet-app/chart/config/sandbox-v1/faucet.yaml.gotmpl, faucet-app/chart/config/sandbox-v1/secrets.yaml, faucet-app/chart/config/stress-v1/faucet.yaml.gotmpl, faucet-app/chart/config/stress-v1/secrets.yaml
Faucet runtime and service settings per environment; SOPS-encrypted owner private key secrets.
Helmfile orchestration and release management
helmfile.yaml.gotmpl
Root Helmfile defining sandbox-v1, stress-v1, prod-v1 environments; nitronode release (always installed); faucet-app release (non-prod only, depends on nitronode, dynamic image.tag).

CI/CD Pipeline for Docker Image Building

Layer / File(s) Summary
GitHub Actions jobs for Docker build and registry push
.github/workflows/main-pr.yml, .github/workflows/main-push.yml, .github/workflows/stable-tag.yml, .github/workflows/v1-push.yml
New build-and-publish-faucet jobs build faucet Docker images and push to GHCR with environment-specific tags (short SHA, latest-main, release tags, RC tags); all configure Docker Buildx and GitHub Actions cache.

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • dimast-x
  • ihsraham
  • philanton

🐰 A faucet flows where tokens need go,
With Helm and Docker in the show,
Rate-limited taps and SDK grace,
Kubernetes brings us to the right place,
So code and deploy without a care! 💧

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.16% 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 primary change: migrating the faucet-app into the nitrolite repository, which is the main objective of the PR.
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.

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 206e3632. The nested-module copy/download steps (COPY faucet-app/server/go.mod faucet-app/server/go.sum + cd faucet-app/server && go mod download) have been removed since faucet-app/server/go.mod and go.sum no longer exist. The Dockerfile now runs go mod download against the root module, copies the full source tree, and builds with go build ./faucet-app/server.

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.

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 206e3632. CooldownPeriod in config.go has been changed from env-required:"true" to env-default:"24h", so a fresh .env copied from .env.example passes validation without setting COOLDOWN_PERIOD.

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 thread faucet-app/server/Dockerfile Outdated
Comment thread go.mod Outdated
nksazonov and others added 9 commits May 25, 2026 14:05
- Dockerfile: build from root module context now that faucet-app/server is no longer a nested module; previous nested COPY/go mod download steps failed because faucet-app/server/go.mod and go.sum no longer exist.
- config: COOLDOWN_PERIOD switched from env-required to env-default:"24h" so it matches the .env.example comment ("Optional: default is 24h") and a fresh .env copy validates cleanly.
- go.mod: bump github.com/quic-go/quic-go v0.54.0 -> v0.57.0 to clear GO-2025-4233 (reachable via gin -> http3 -> quic-go).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the existing nitronode Docker publish jobs for the faucet server:

- main-pr.yml: build and push :short_sha on PR.
- main-push.yml: build and push :short_sha + :latest-main on merge to main.
- v1-push.yml: build and push :rc-tag + :latest-rc after auto-tag on release/v1.0.0.
- stable-tag.yml: build and push :tag + :latest on stable v* tags.

All jobs reuse the existing test-nitronode / auto-tag gates so faucet builds only run after the Go test suite (which already covers ./faucet-app/...).

Image is published to ghcr.io/${{ github.repository }}/faucet-app.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restore faucet Helm chart and wire it into a root-level helmfile alongside
the existing nitronode release. Faucet ships only to non-prod envs and is
served on the same hostname as nitronode at /v1/faucet-app.

Chart:
- Templates ported from the original migration commit, refit for v1: env
  vars renamed CLEARNODE_URL -> NITRONODE_URL, image repo updated to
  ghcr.io/layer-3/nitrolite/faucet-app, default args use the on-PATH
  binary, COOLDOWN_PERIOD / TRUSTED_PROXIES env vars surfaced, stale
  SIGNER_PRIVATE_KEY usage dropped, README .gotmpl removed.
- imagePullSecret defaults to nitronode-ghcr-pull so the faucet pod
  pulls from GHCR using the Secret created by the co-deployed nitronode
  release in the same namespace.

Env configs (sandbox-v1, stress-v1):
- Intra-cluster WebSocket URL ws://nitronode:7824/ws (bypasses the
  /v1/ws ingress rewrite, talks straight to the nitronode pod listener).
- Ingress attaches to the nitronode hostname (nitronode-{env}.yellow.org)
  at path /v1/faucet-app(/|$)(.*) with the nginx use-regex + rewrite-target
  annotations stripping the prefix.
- SOPS-encrypted secrets.yaml carrying OWNER_PRIVATE_KEY.

Helmfile:
- Moved nitronode/helmfile.yaml.gotmpl to repo root; nitronode paths
  prefixed with nitronode/. Faucet release added with needs: nitronode
  in the same env. installed gated on env name so prod-v1 stays empty.
- Image tag selectable per app via NITRONODE_IMAGE_TAG / FAUCET_IMAGE_TAG
  with IMAGE_TAG as the shared fallback.

Validated with helm lint + helm template and helmfile -e {sandbox-v1,
stress-v1,prod-v1} lint/template. Pod name resolves to faucet-app-*
(release renamed faucet -> faucet-app to satisfy the fullname helper's
contains-chart-name shortcut).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Match the nitronode convention: drop the in-package logger wrapper and
thread a pkg/log.Logger through the call graph.

- internal/logger package deleted. main builds NewZapLogger(cfg.Log).WithName(...)
  and passes named child loggers into nitronode.NewClient and server.NewServer.
- config: legacy LogLevel string replaced by embedded log.Config; LOG_LEVEL,
  LOG_FORMAT and LOG_OUTPUT are now read by pkg/log directly.
- All call sites converted from Sprintf-style formatting to structured
  key/value form (e.g. logger.Info("transfer successful", "address", a,
  "amount", x, "txID", id)).
- requestLogger middleware now receives the logger explicitly.
- Tests use log.NewNoopLogger() — TestMain initialiser dropped.

Chart: LOG_FORMAT and LOG_OUTPUT env vars not surfaced; pkg/log defaults
(console + stderr) apply, matching the nitronode chart. .env.example
documents the new variables for local dev.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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: 4

🤖 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 @.github/workflows/main-pr.yml:
- Around line 171-192: The workflow uses unpinned action versions and leaves
checkout credentials enabled; update the publish job so the checkout step
(actions/checkout) sets with: persist-credentials: false, and replace the
floating tags docker/login-action@v2 and docker/build-push-action@v4 with their
corresponding full commit SHAs (pin to specific commit identifiers) to harden
the job against supply-chain drift; ensure you update the three uses entries
(actions/checkout, docker/login-action, docker/build-push-action) to the exact
SHA strings and keep the rest of the step configurations unchanged.

In @.github/workflows/main-push.yml:
- Around line 189-213: Replace unpinned/outdated action refs and disable
persisting checkout credentials: update uses entries for actions/checkout,
docker/setup-buildx-action, docker/login-action, and docker/build-push-action to
the approved, pinned versions (e.g., a full semver or SHA-based tag for each)
and add persist-credentials: false to the actions/checkout step so credentials
are not kept in the workspace; locate the steps named by those action
identifiers in the workflow and change their uses and checkout configuration
accordingly.

In @.github/workflows/stable-tag.yml:
- Around line 64-84: The workflow uses third-party actions without immutable
pins and leaves checkout credentials persisted; update the actions to use commit
SHA pins for docker/setup-buildx-action, docker/login-action, and
docker/build-push-action in the Build and push Docker image steps and set
persist-credentials: false on the actions/checkout@v6 step (the checkout step
with fetch-depth: 0) to avoid leaking GITHUB_TOKEN to subsequent steps; ensure
you replace the current tags with specific commit SHAs and keep the step ids
(e.g., tagger) and behavior unchanged.

In @.github/workflows/v1-push.yml:
- Around line 127-148: Update the workflow to pin all external action refs to
immutable commit SHAs and prevent credential leakage: replace the `uses:
actions/checkout@v4`, `uses: docker/setup-buildx-action@v2`, `uses:
docker/login-action@v2`, and `uses: docker/build-push-action@v4` entries with
their corresponding full commit SHA refs, and add `persist-credentials: false`
under the `actions/checkout` step (the checkout step block containing
`fetch-depth: 0`) so credentials are not persisted for subsequent steps; keep
the existing `id: tagger` and Docker login inputs unchanged.
🪄 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: d044f740-96ba-4f3d-9ea0-467158a5e508

📥 Commits

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

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (37)
  • .github/workflows/main-pr.yml
  • .github/workflows/main-push.yml
  • .github/workflows/stable-tag.yml
  • .github/workflows/v1-push.yml
  • faucet-app/chart/.helmignore
  • faucet-app/chart/Chart.yaml
  • faucet-app/chart/config/sandbox-v1/faucet.yaml.gotmpl
  • faucet-app/chart/config/sandbox-v1/secrets.yaml
  • faucet-app/chart/config/stress-v1/faucet.yaml.gotmpl
  • faucet-app/chart/config/stress-v1/secrets.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/secret.yaml
  • faucet-app/chart/templates/service.yaml
  • faucet-app/chart/values.yaml
  • faucet-app/server/.env.example
  • faucet-app/server/.gitignore
  • faucet-app/server/Dockerfile
  • faucet-app/server/README.md
  • faucet-app/server/internal/config/config.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
  • helmfile.yaml.gotmpl
  • nitronode/helmfile.yaml.gotmpl
💤 Files with no reviewable changes (33)
  • faucet-app/chart/.helmignore
  • faucet-app/chart/config/sandbox-v1/secrets.yaml
  • faucet-app/server/internal/nitronode/client_validation_test.go
  • faucet-app/chart/Chart.yaml
  • faucet-app/chart/templates/helpers/_hpa.tpl
  • faucet-app/chart/templates/http-route-redirect.yaml
  • faucet-app/chart/templates/http-route.yaml
  • faucet-app/server/Dockerfile
  • faucet-app/chart/config/sandbox-v1/faucet.yaml.gotmpl
  • faucet-app/chart/templates/podmonitoring.yaml
  • faucet-app/chart/config/stress-v1/secrets.yaml
  • helmfile.yaml.gotmpl
  • faucet-app/chart/templates/service.yaml
  • faucet-app/server/.env.example
  • nitronode/helmfile.yaml.gotmpl
  • faucet-app/chart/values.yaml
  • faucet-app/chart/config/stress-v1/faucet.yaml.gotmpl
  • faucet-app/chart/templates/helpers/_ingress.tpl
  • faucet-app/server/.gitignore
  • faucet-app/chart/templates/gateway.yaml
  • faucet-app/chart/templates/ingress.yaml
  • faucet-app/server/main.go
  • faucet-app/chart/templates/deployment.yaml
  • faucet-app/chart/templates/helpers/_component.tpl
  • go.mod
  • faucet-app/server/internal/server/ratelimiter.go
  • faucet-app/server/internal/server/server_test.go
  • faucet-app/chart/templates/helpers/_common.tpl
  • faucet-app/server/internal/server/server.go
  • faucet-app/server/internal/config/config.go
  • faucet-app/server/internal/nitronode/client.go
  • faucet-app/server/README.md
  • faucet-app/chart/templates/secret.yaml

Comment thread .github/workflows/main-pr.yml
Comment thread .github/workflows/main-push.yml
Comment thread .github/workflows/stable-tag.yml
Comment thread .github/workflows/v1-push.yml
@nksazonov nksazonov merged commit e3005c0 into main May 27, 2026
13 checks passed
@nksazonov nksazonov deleted the feat/faucet-app branch May 27, 2026 06:49
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