Skip to content

Phase 1: connection-mode enum + backwards-compat (issue #5989)#6047

Open
MichaelUray wants to merge 14 commits intonetbirdio:mainfrom
MichaelUray:feat/connection-mode-phase1
Open

Phase 1: connection-mode enum + backwards-compat (issue #5989)#6047
MichaelUray wants to merge 14 commits intonetbirdio:mainfrom
MichaelUray:feat/connection-mode-phase1

Conversation

@MichaelUray
Copy link
Copy Markdown
Contributor

@MichaelUray MichaelUray commented May 1, 2026

Summary

This PR is Phase 1 of three that together implement issues #5989 and #5990 (the connection-mode consolidation RFC and its server-side per-peer/per-group companion).

In Scope:

  • New ConnectionMode proto enum with four reserved values (relay-forced, p2p, p2p-lazy, p2p-dynamic); three are functional in this PR. p2p-dynamic is reserved on the wire and in the DB but the daemon currently treats it like p2p (pass-through). The decoupled worker_relay/worker_ice lifecycle that makes p2p-dynamic distinct comes in Phase 2.
  • Two new account-level settings on the management server: relay_timeout_seconds, p2p_timeout_seconds (both nullable, NULL = built-in default).
  • Replaces the asymmetric Lazy/ForceRelay precedence in client/internal/conn_mgr.go with a single resolver: client env > client config > server-pushed.
  • Full backwards-compatibility:
    • Old clients still see only the legacy lazy_connection_enabled boolean (mapped from the resolved mode by toPeerConfig).
    • Old servers send connection_mode = UNSPECIFIED (proto default 0) and the new client falls back to the legacy boolean -- verified via tests.
    • NB_FORCE_RELAY, NB_ENABLE_EXPERIMENTAL_LAZY_CONN, --enable-lazy-connection, and NB_LAZY_CONN_INACTIVITY_THRESHOLD continue to work and emit one-shot deprecation warnings.

The companion Dashboard PR is at netbirdio/dashboard#627 (will be added once it exists).

Implementation map

Layer Files
proto shared/management/proto/management.proto, client/proto/daemon.proto
Mode type shared/connectionmode/ (Mode, ParseString, FromProto, ToProto, ResolveLegacyLazyBool, ToLazyConnectionEnabled)
client env / CLI client/internal/peer/env.go (ResolveModeFromEnv), client/cmd/{root,up}.go (--connection-mode, --relay-timeout, --p2p-timeout)
client engine / conn-mgr client/internal/{engine,conn_mgr,connect}.go (resolveConnectionMode + UpdatedRemotePeerConfig + EngineConfig fields)
client per-peer client/internal/peer/conn.go (Mode-driven skip-ICE branch)
mgmt server management/server/types/settings.go (new nullable columns), management/server/http/handlers/accounts/accounts_handler.go (PUT validation + response), management/server/account.go (audit emission), management/server/activity/codes.go (3 new event codes), management/internals/shared/grpc/conversion.go (toPeerConfig writes both old and new wire fields)
OpenAPI shared/management/http/api/openapi.yml + regenerated types.gen.go
tests shared/connectionmode/mode_test.go, client/internal/peer/env_test.go, client/internal/conn_mgr_test.go, management/internals/shared/grpc/conversion_test.go

Hardware-tested

Verified on a real production NetBird instance with 32 connected peers across 12 OpenWrt-router versions (22.03 to 25.12), Windows 10/11, Debian, Android 12/14, and iOS 26.3.1:

  • Pre-deploy baseline: 32 connected.
  • After deploying the Phase-1 management image: 32 connected, no disconnect, all old clients continued to function unchanged (they read only the legacy lazy_connection_enabled boolean which toPeerConfig keeps writing via the back-compat mapping).
  • Switched the account-wide connection_mode between p2p and p2p-lazy via the API: 32 connected throughout, no disconnect.
  • 8-minute monitoring window after deploy: zero peer-count drift.

The daemon-side change is exercised end-to-end by switching the account-wide setting and observing the daemon pick up the new mode on the next NetworkMap update (handled by UpdatedRemotePeerConfig).

Known limitations (called out in spec section 8)

  1. relay-forced cannot be pushed to old clients because the legacy lazy_connection_enabled boolean cannot express it. The wire field falls back to false and old clients run in normal p2p. Workaround: upgrade the client, or set local NB_FORCE_RELAY=true.
  2. p2p-dynamic is not visible in the Dashboard dropdown in Phase 1 even though the API accepts it; setting it via API gives p2p behaviour until the Phase-2 daemon implementation lands.
  3. Audit events are account-scoped only; per-peer / per-group events follow in Phase 3 (Per-peer and per-group connection-mode and inactivity-threshold override on the management server #5990).

Phase 2 / 3 follow-ups

Test plan

  • All new + existing unit tests in client/internal/... and management/... green
  • TestAccount_GetPeerNetworkMap (existing) remains green -- old peers without connection_mode resolve via ResolveLegacyLazyBool exactly as before
  • TestToPeerConfig_ConnectionModeResolution (new, 9 sub-cases) covers the resolution matrix
  • TestResolveConnectionMode (new, 10 sub-cases) covers the env > config > server precedence chain
  • TestResolveModeFromEnv (new, 9 sub-cases) covers the env-var matrix including the legacy-conflict case
  • Hardware-verified against 32 mixed-version peers in production (no disconnects)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added configurable peer connection modes (relay-forced, p2p, p2p-lazy, p2p-dynamic) via CLI flags and account settings.
    • Added relay and P2P timeout configuration options to fine-tune connection behavior.
    • Added activity logging for connection mode and timeout setting changes.

MichaelUray and others added 14 commits May 1, 2026 07:59
…nfig

Additive change for issue netbirdio#5989 Phase 1. New fields use new tag numbers
(11, 12, 13); existing fields (including LazyConnectionEnabled tag 6) are
unchanged so old clients ignore the additions and old servers send
UNSPECIFIED, which the new client maps back via the legacy boolean.

Note: the regenerated pb.go files now report protoc v5.29.3 in their
header (this branch was generated with locally-installed protoc 29.3
instead of upstream's v7.34.1). Functionally identical; header diff is
the only delta beyond the actual schema additions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Defines Mode enum (relay-forced, p2p, p2p-lazy, p2p-dynamic plus the
client-only sentinels Unspecified and FollowServer), ParseString for
CLI/env input, ToProto/FromProto for wire translation, and the two
backwards-compat helpers ResolveLegacyLazyBool / ToLazyConnectionEnabled
that bridge the old Settings.LazyConnectionEnabled boolean.

Phase 1 of issue netbirdio#5989. Pure addition -- no existing callers touched
in this commit; the engine/conn_mgr migration follows in subsequent
commits in the same PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…on warns

NB_CONNECTION_MODE wins over the legacy pair (NB_FORCE_RELAY,
NB_ENABLE_EXPERIMENTAL_LAZY_CONN); when the legacy pair is set
together, NB_FORCE_RELAY wins (most-restrictive, mirrors the
group-conflict rule from issue netbirdio#5990).

Each legacy var emits a one-shot deprecation warning when it actually
contributes to the resolved mode. NB_LAZY_CONN_INACTIVITY_THRESHOLD
becomes an alias for the future relay_timeout setting and warns once.

IsForceRelayed() is kept for callers that have not yet been migrated
(conn.go, statusrecorder); they will be updated in the engine/conn
refactor commits later in this PR.

Phase 1 of issue netbirdio#5989.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three new CLI flags map onto the new connection-mode plumbing:
- --connection-mode <relay-forced|p2p|p2p-lazy|p2p-dynamic|follow-server>
- --relay-timeout <seconds>
- --p2p-timeout <seconds>

Plumbed through three sites in cmd/up.go (SetConfigRequest, ConfigInput,
LoginRequest), persisted in profilemanager.Config, and added as new
fields on the daemon.proto IPC messages. Empty / not-changed flags fall
back to the server-pushed value (which itself falls back to the legacy
lazy_connection_enabled boolean for old servers).

Phase 1 of issue netbirdio#5989.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EngineConfig gains ConnectionMode, RelayTimeoutSeconds, P2pTimeoutSeconds.
ConnMgr now stores the resolved Mode plus the raw inputs (env, config)
so it can re-resolve when the server pushes a new PeerConfig.

UpdatedRemoteFeatureFlag is renamed to UpdatedRemotePeerConfig and
takes the full PeerConfig pointer; a thin shim with the old name
delegates to it for callers that haven't been updated yet.

connect.go copies the three new fields from profilemanager.Config into
the EngineConfig builder, with a tolerant parser that logs and falls
through to Unspecified on invalid input.

Phase 1 of issue netbirdio#5989. peer/conn.go forwarding follows in C4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ConnConfig gains a Mode field forwarded from the engine. Open() now
checks Mode == ModeRelayForced instead of calling the global env-reader
IsForceRelayed(). The local 'forceRelay' variable name is renamed to
'skipICE' to make the new branching intent explicit.

The PeerStateUpdate block at the end of Open() also reads from
conn.config.Mode now, so the StatusRecorder sees the per-peer mode
rather than the global env var.

A single remaining caller of IsForceRelayed() (srWatcher.Start in
engine.go) is left for a follow-up; that path uses a process-wide flag
not per-peer state, so it can be migrated in Phase 2 once srWatcher
itself learns about ConnectionMode.

Phase 1 of issue netbirdio#5989. Engine forwarding (C5) follows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
createPeerConn now reads ConnMgr.Mode() and copies it into
peer.ConnConfig, so the per-peer Open() loop in conn.go can take the
ModeRelayForced skip-ICE branch without reading the global env var.

This is the last wiring commit for the client side of Phase 1; the
server-side mgmt changes (Settings + OpenAPI + handler + audit +
NetworkMap-build) follow in Section D.

Phase 1 of issue netbirdio#5989.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
All three fields are nullable to distinguish 'use built-in default'
(NULL) from explicit values (incl. 0 = never tear down). Copy() now
deep-clones the new pointer fields via two small helpers.

GORM AutoMigrate creates the new columns at first start; existing
accounts have NULL in all three columns and resolve via the legacy
LazyConnectionEnabled boolean.

Phase 1 of issue netbirdio#5989.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tings

Three new optional, nullable fields with descriptions of the NULL =
built-in-default semantics and the Phase-1-vs-Phase-2 status of
p2p-dynamic. Regenerated types.gen.go via the existing oapi-codegen
tooling.

The generated AccountSettingsConnectionMode enum has the canonical
values relay-forced / p2p / p2p-lazy / p2p-dynamic, plus a Valid()
helper for handler-side validation.

Phase 1 of issue netbirdio#5989.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PUT /api/accounts/{id} now accepts connection_mode (validated against
the four-value enum via the generated AccountSettingsConnectionMode.
Valid()), p2p_timeout_seconds and relay_timeout_seconds. NULL in the
JSON body keeps the existing value untouched (= "no client-side
override on this round-trip"); explicit NULL-clear via API uses a
distinct PATCH-style call which is out-of-scope for Phase 1.

Response payload mirrors the input fields back as nullable so the
dashboard can distinguish "use default" from "explicit value".

Phase 1 of issue netbirdio#5989. Audit-event emission follows in D5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AccountConnectionModeChanged (121), AccountRelayTimeoutChanged (122),
AccountP2pTimeoutChanged (123) -- emitted from account.go when settings
change. Per-peer / per-group event codes are reserved for Phase 3
(issue netbirdio#5990).

Phase 1 of issue netbirdio#5989.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
handleConnectionModeSettings is invoked from the same diff-detection
block as handleLazyConnectionSettings; emits one StoreEvent per
changed field (ConnectionMode, RelayTimeoutSeconds, P2pTimeoutSeconds)
with old/new values in the meta payload.

Four small ptr-equality / deref helpers are added for nullable string
and uint32 fields. They are package-private and named after the
existing convention used elsewhere in the package.

Phase 1 of issue netbirdio#5989.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…elds

Two changes in one commit because they're inseparable:

1. Move client/internal/peer/connectionmode/ to shared/connectionmode/.
   The package now needs to be importable from BOTH client/ and
   management/ (which is impossible while it lives under client/internal/
   per Go's internal-package rule). All imports updated; tests pass on
   both sides.

2. Extend management/internals/shared/grpc/conversion.go::toPeerConfig
   to populate the three new PeerConfig fields (ConnectionMode,
   P2PTimeoutSeconds, RelayTimeoutSeconds) using the connectionmode
   helpers. The legacy LazyConnectionEnabled boolean is now derived
   from the resolved Mode via ToLazyConnectionEnabled() rather than
   copied verbatim from Settings -- this is the central backwards-compat
   contract: old clients see only the boolean, new clients prefer the
   explicit enum and ignore the bool.

Resolution rules (Phase 1, account-wide only):
- Settings.ConnectionMode != nil and parses -> wins
- Otherwise -> ResolveLegacyLazyBool(LazyConnectionEnabled)
- timeouts: Settings.RelayTimeoutSeconds / P2pTimeoutSeconds when non-NULL,
  else 0 (= server has no preference; client uses built-in default)

Per-peer / per-group resolution comes in Phase 3 (netbirdio#5990).

Phase 1 of issue netbirdio#5989.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nine sub-cases cover the Phase-1 resolution matrix from spec section 3:
- no settings -> default (P2P + lazy=false)
- legacy bool only -> mapped via ResolveLegacyLazyBool
- explicit ConnectionMode -> wins over the legacy bool
- timeouts propagate
- garbage ConnectionMode value -> tolerant fallback to legacy bool

Particular attention to the structural compat gap: relay-forced cannot
be expressed via the legacy boolean, so the wire field for old clients
is sent as false. Documented in the spec, asserted here.

Existing TestAccount_GetPeerNetworkMap remains green: existing test
peers have ConnectionMode=NULL in Settings, falls through to the legacy
ResolveLegacyLazyBool(false) -> ModeP2P -> wire bool false.

Phase 1 of issue netbirdio#5989.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 1, 2026

📝 Walkthrough

Walkthrough

This PR introduces comprehensive peer connection mode and timeout configuration across the NetBird client and server stack. It adds CLI flags for connection-mode and timeout parameters, defines a new connectionmode.Mode enum with resolution logic across environment/config/server precedence layers, updates the connection manager to conditionally manage lazy connections based on resolved mode, extends management proto/API schemas with new settings fields, and implements server-side settings storage and activity tracking for connection mode changes.

Changes

Cohort / File(s) Summary
CLI & Command Setup
client/cmd/root.go, client/cmd/up.go
Added three experimental persistent flags (connection-mode, relay-timeout, p2p-timeout) and wired them into CLI requests/configs with conditional propagation based on flag-changed state.
Connection Mode Enum & Parsing
shared/connectionmode/mode.go, shared/connectionmode/mode_test.go
Introduced connectionmode.Mode enum with six modes (Unspecified, RelayForced, P2P, P2PLazy, P2PDynamic, FollowServer), string parsing, proto bidirectional translation, and legacy LazyConnectionEnabled boolean compatibility helpers.
Client Configuration & Profile
client/internal/profilemanager/config.go, client/internal/connect.go
Extended ConfigInput and Config structures with three new fields (ConnectionMode, RelayTimeoutSeconds, P2pTimeoutSeconds) and updated EngineConfig creation to parse and carry these values with fallback handling.
Environment Variable Resolution
client/internal/peer/env.go, client/internal/peer/env_test.go, client/internal/lazyconn/env.go
Introduced ResolveModeFromEnv() with precedence chain (new NB_CONNECTION_MODE over legacy NB_FORCE_RELAY/NB_ENABLE_EXPERIMENTAL_LAZY_CONN) and deprecation warnings; added export constant EnvKeyNBConnectionMode.
Connection Manager Mode Resolution
client/internal/conn_mgr.go, client/internal/conn_mgr_test.go
Refactored from boolean lazy-enable flag to mode-based resolution with resolveConnectionMode() precedence chain (env → client config → server PeerConfig), added Mode() and RelayTimeout() accessors, replaced UpdatedRemoteFeatureFlag() with UpdatedRemotePeerConfig(), and conditionally start/stop lazy manager based on resolved mode.
Engine & Peer Connection Configuration
client/internal/engine.go, client/internal/peer/conn.go
Updated engine to pass resolved connection mode from connMgr.Mode() into peer ConnConfig.Mode, added EngineConfig fields for mode/timeout overrides, and refactored peer ICE initialization to conditionally skip based on mode rather than global IsForceRelayed() function.
Client-Daemon Protocol
client/proto/daemon.proto
Added three optional fields (connection_mode, p2p_timeout_seconds, relay_timeout_seconds) to both LoginRequest and SetConfigRequest messages.
Management Proto & Wire Format
shared/management/proto/management.proto
Extended PeerConfig message with ConnectionMode enum field, plus P2pTimeoutSeconds and RelayTimeoutSeconds timeout fields; reserved tags 9-10 for future use; added top-level ConnectionMode enum with five values.
Management API Schema & Types
shared/management/http/api/openapi.yml, shared/management/http/api/types.gen.go
Added three new AccountSettings schema properties (connection_mode enum, p2p_timeout_seconds, relay_timeout_seconds); generated corresponding Go type definitions with AccountSettingsConnectionMode enum and optional pointer fields.
Server Settings Model
management/server/types/settings.go
Extended Settings struct with three new nullable fields (ConnectionMode *string, RelayTimeoutSeconds *uint32, P2pTimeoutSeconds *uint32); updated Copy() method with nil-safe pointer-cloning helpers.
Server Account Settings Handler
management/server/account.go, management/server/http/handlers/accounts/accounts_handler.go
Updated account update flow to validate and persist new connection-mode/timeout settings, emit activity events for Phase-1 changes with old/new metadata; extended account handler to validate connection_mode and map timeout fields from request.
Server Activity Tracking
management/server/activity/codes.go
Added three new activity type constants (AccountConnectionModeChanged, AccountRelayTimeoutChanged, AccountP2pTimeoutChanged with codes 121-123) and registered them in activity map.
Server gRPC Conversion
management/internals/shared/grpc/conversion.go, management/internals/shared/grpc/conversion_test.go
Updated toPeerConfig() to resolve connection mode from settings with fallback to legacy boolean, populate proto ConnectionMode/timeout fields, and verify backward-compatibility with legacy LazyConnectionEnabled; added comprehensive test coverage for resolution precedence.

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as Client CLI
    participant ConnMgr as Connection Manager
    participant Engine as Engine
    participant Peer as Peer Conn
    participant Server as Management Server

    User->>CLI: Set connection-mode flag<br/>(or env var/config file)
    CLI->>ConnMgr: NewConnMgr(env, config)
    ConnMgr->>ConnMgr: resolveConnectionMode()<br/>(env → config → default)
    ConnMgr->>ConnMgr: Conditionally start<br/>lazy manager per mode
    
    Engine->>Server: updateNetworkMap()
    Server->>Engine: PeerConfig with<br/>ConnectionMode + timeouts
    
    Engine->>ConnMgr: UpdatedRemotePeerConfig(ctx, pc)
    ConnMgr->>ConnMgr: Re-resolve mode<br/>(env → config → server)
    ConnMgr->>ConnMgr: Start/stop lazy manager<br/>only on state change
    
    Engine->>Peer: Create connection<br/>with config.Mode
    Peer->>Peer: Skip ICE/relay logic<br/>per Mode value
    Peer->>Peer: isConnectedOnAllWay()<br/>computed from mode
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • pascal-fischer
  • crn4

Poem

🐰 Hops with glee through modes so new,
Relay, P2P, and lazy too!
Timeouts tuned and servers heard,
Connection config, perfectly stirred!
From CLI flags to peer's embrace—
Each frame finds its proper place!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% 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
Title check ✅ Passed The title clearly and concisely summarizes the main change: introducing Phase 1 of connection-mode enum with backwards compatibility, directly addressing issue #5989.
Description check ✅ Passed The PR description comprehensively covers all required template sections with detailed implementation details, test coverage, hardware verification, and known limitations clearly documented.
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 unit tests (beta)
  • Create PR with unit tests

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 1, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
5 New issues
5 New Code Smells (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

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: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@client/cmd/up.go`:
- Around line 437-439: The connection-mode flag value must be normalized before
persisting or sending: when the flag value (connectionMode variable read via
connectionModeFlag) equals "relay" (case-insensitive, trimmed) convert it to the
canonical "relay-forced" string and then assign that normalized value to
req.ConnectionMode and any profile write paths; leave other values unchanged and
ensure the same normalization is applied in the other occurrences that set
req.ConnectionMode or write the profile (the other blocks referenced around the
same flag usage).

In `@client/internal/peer/env_test.go`:
- Line 25: Update the test case name string in the table entry that currently
reads "connection_mode unparseable falls through to legacy" to use the correct
spelling "connection_mode unparsable falls through to legacy"; locate the tuple
in peer/env_test.go (the table entry with values "garbage", "true",
connectionmode.ModeRelayForced, etc.) and change the first element only to keep
codespell green.

In `@client/internal/peer/env.go`:
- Around line 75-77: Validate the parsed duration from time.ParseDuration(raw)
before assigning to timeoutSecs: ensure the duration is non-negative and that
d.Seconds() does not exceed the maximum uint32 value (so it won't wrap when
cast). Specifically, in the block handling envInactivityThreshold, check d >= 0
and d.Seconds() <= float64(math.MaxUint32) (or compare against
time.Duration(maxUint32Seconds)*time.Second) and only then set timeoutSecs =
uint32(d.Seconds()); otherwise log or handle the invalid value (e.g., ignore the
env value or clamp to max) while still calling
warnDeprecated(envInactivityThreshold, "the relay_timeout setting on the
management server").

In `@client/internal/profilemanager/config.go`:
- Around line 177-179: The timeout fields RelayTimeoutSeconds and
P2pTimeoutSeconds should be made nullable so the persisted profile can
distinguish "unset" from an explicit zero: change their types from uint32 to
*uint32 in the profile struct(s) (e.g., the struct containing ConnectionMode,
RelayTimeoutSeconds, P2pTimeoutSeconds) and keep json:",omitempty" so nil is
omitted while an explicit zero is preserved when set; update any code that
reads/writes these fields (decoders, defaults, and consumers) to handle nil vs
non-nil dereferencing and apply the same change for the corresponding
p2p_timeout_seconds occurrences referenced around lines 604-618.

In `@management/server/account.go`:
- Around line 372-375: UpdateAccountSettings currently doesn't mark
updateAccountPeers or bump the network serial when ConnectionMode,
RelayTimeoutSeconds, or P2pTimeoutSeconds change, so peers won't receive the new
values; inside UpdateAccountSettings (near the existing updateAccountPeers
gating above the calls to am.handleRoutingPeerDNSResolutionSettings /
handleLazyConnectionSettings / handleConnectionModeSettings /
handlePeerLoginExpirationSettings) detect differences on ConnectionMode,
RelayTimeoutSeconds, and P2pTimeoutSeconds between oldSettings and newSettings,
set updateAccountPeers = true (or equivalent), and ensure the account/network
serial is bumped so connected peers are notified of the change.

In `@management/server/http/handlers/accounts/accounts_handler.go`:
- Around line 229-236: The code casts signed API timeout fields
(req.Settings.P2pTimeoutSeconds, req.Settings.RelayTimeoutSeconds) straight to
uint32 which will silently wrap on negative or out-of-range values; add explicit
validation before casting: check each non-nil value is >= 0 and <=
math.MaxUint32 (or 4294967295), and if not return a 400 validation error (use
the same error/response helper used elsewhere in accounts_handler.go). Implement
the checks inline where returnSettings.P2pTimeoutSeconds and
returnSettings.RelayTimeoutSeconds are set (or extract to a small helper
validateTimeout(name string, v *int64) error) and only perform the uint32
conversion after the value passes validation.

In `@shared/management/http/api/types.gen.go`:
- Around line 1517-1521: The handler currently casts pointer timeout fields like
P2pTimeoutSeconds (and the other *int64 timeout fields) directly to uint32,
which allows negative values to wrap; update the account handler code that
performs the cast to first check for nil, then validate the pointed int64 is >=
0 and <= math.MaxUint32, returning a validation error (or reject the request) if
out of range; only then convert safely to uint32. Identify the conversion sites
in the accounts handler where P2pTimeoutSeconds (and the similar timeout fields)
are dereferenced/cast and apply this guard before performing the uint32 cast.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a58c2ed6-4fbf-4f05-a219-4fc44e13ce33

📥 Commits

Reviewing files that changed from the base of the PR and between c4b2da4 and c272786.

⛔ Files ignored due to path filters (4)
  • client/proto/daemon.pb.go is excluded by !**/*.pb.go
  • client/proto/daemon_grpc.pb.go is excluded by !**/*.pb.go
  • shared/management/proto/management.pb.go is excluded by !**/*.pb.go
  • shared/management/proto/proxy_service.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (23)
  • client/cmd/root.go
  • client/cmd/up.go
  • client/internal/conn_mgr.go
  • client/internal/conn_mgr_test.go
  • client/internal/connect.go
  • client/internal/engine.go
  • client/internal/lazyconn/env.go
  • client/internal/peer/conn.go
  • client/internal/peer/env.go
  • client/internal/peer/env_test.go
  • client/internal/profilemanager/config.go
  • client/proto/daemon.proto
  • management/internals/shared/grpc/conversion.go
  • management/internals/shared/grpc/conversion_test.go
  • management/server/account.go
  • management/server/activity/codes.go
  • management/server/http/handlers/accounts/accounts_handler.go
  • management/server/types/settings.go
  • shared/connectionmode/mode.go
  • shared/connectionmode/mode_test.go
  • shared/management/http/api/openapi.yml
  • shared/management/http/api/types.gen.go
  • shared/management/proto/management.proto

Comment thread client/cmd/up.go
Comment on lines +437 to +439
if cmd.Flag(connectionModeFlag).Changed {
req.ConnectionMode = &connectionMode
}
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 | 🟠 Major | ⚡ Quick win

Normalize the advertised relay value before persisting or sending it.

client/cmd/root.go:195-210 exposes --connection-mode as (p2p, relay), but shared/connectionmode/mode.go:47-71 only accepts relay-forced. These three paths currently forward the raw flag value unchanged, so netbird up --connection-mode relay follows the help text yet gets rejected later or written into the profile as an invalid value in foreground mode.

Also applies to: 564-566, 688-690

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/cmd/up.go` around lines 437 - 439, The connection-mode flag value must
be normalized before persisting or sending: when the flag value (connectionMode
variable read via connectionModeFlag) equals "relay" (case-insensitive, trimmed)
convert it to the canonical "relay-forced" string and then assign that
normalized value to req.ConnectionMode and any profile write paths; leave other
values unchanged and ensure the same normalization is applied in the other
occurrences that set req.ConnectionMode or write the profile (the other blocks
referenced around the same flag usage).

{"lazy alone", "", "", "true", "", connectionmode.ModeP2PLazy, 0},
{"force_relay AND lazy: force_relay wins", "", "true", "true", "", connectionmode.ModeRelayForced, 0},
{"only inactivity threshold", "", "", "", "30m", connectionmode.ModeUnspecified, 1800},
{"connection_mode unparseable falls through to legacy", "garbage", "true", "", "", connectionmode.ModeRelayForced, 0},
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 | 🟡 Minor | ⚡ Quick win

Fix typo in test case name to keep codespell green.

"unparseable" should be "unparsable".

✏️ Suggested patch
-		{"connection_mode unparseable falls through to legacy", "garbage", "true", "", "", connectionmode.ModeRelayForced, 0},
+		{"connection_mode unparsable falls through to legacy", "garbage", "true", "", "", connectionmode.ModeRelayForced, 0},
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{"connection_mode unparseable falls through to legacy", "garbage", "true", "", "", connectionmode.ModeRelayForced, 0},
{"connection_mode unparsable falls through to legacy", "garbage", "true", "", "", connectionmode.ModeRelayForced, 0},
🧰 Tools
🪛 GitHub Check: codespell

[failure] 25-25:
unparseable ==> unparsable

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/internal/peer/env_test.go` at line 25, Update the test case name
string in the table entry that currently reads "connection_mode unparseable
falls through to legacy" to use the correct spelling "connection_mode unparsable
falls through to legacy"; locate the tuple in peer/env_test.go (the table entry
with values "garbage", "true", connectionmode.ModeRelayForced, etc.) and change
the first element only to keep codespell green.

Comment on lines +75 to +77
if d, err := time.ParseDuration(raw); err == nil {
timeoutSecs = uint32(d.Seconds())
warnDeprecated(envInactivityThreshold, "the relay_timeout setting on the management server")
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 | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Go, what are the conversion rules and edge-case behaviors when converting negative or out-of-range float/duration-derived values to uint32?

💡 Result:

In Go, converting float64 or time.Duration-derived values to uint32 follows the language specification for non-constant numeric conversions. For float64 to uint32: - The conversion always succeeds without panic. - The fractional part is truncated toward zero. - If the float64 value is negative or exceeds the uint32 range [0, 4294967295], the result is implementation-dependent (platform/architecture-specific). - On amd64/x86_64: negative float64 converts to a large uint32 value (e.g., uint32(-2.99) ≈ 18446744073709551614 for uint64, truncated for uint32). - On arm64: negative float64 converts to 0. - This behavior is documented in the Go spec and confirmed in compiler issues. For time.Duration to uint32 (Duration is int64 nanoseconds): - First converts int64 to uint32 per integer conversion rules: sign-extended to infinite precision, then truncated to 32 bits. - Positive Duration in uint32 range: exact value (ns % 2^32). - Large positive Duration: wraps around (int64 % 2^32 as uint32). - Negative Duration: sign-extended, so large negative becomes large uint32 (all 1s for truncation, e.g., uint32(int64(-1)) == 4294967295). - No panic; always succeeds. Edge cases with extreme values (near int64 limits) may wrap due to arithmetic. Avoid direct conversions for negative/out-of-range values; check range first or use safe alternatives like conditional clamping. Examples (platform-dependent for float): f := float64(-1.5); u := uint32(f) // amd64: large value near 4294967295; arm64: 0 d := time.Duration(-1000); u := uint32(d) // 4294967295 (uint32(-1000))

Citations:


🏁 Script executed:

fd -t f "env.go" client/internal/peer/

Repository: netbirdio/netbird

Length of output: 155


🏁 Script executed:

# Find and read the env.go file with context
fd -t f "env.go" client/internal/peer/ --exec cat -n {} \;

Repository: netbirdio/netbird

Length of output: 8815


🏁 Script executed:

# Search for uses of timeoutSecs to understand downstream validation
rg "timeoutSecs|envInactivityThreshold" client/internal/peer/ -B 2 -A 5

Repository: netbirdio/netbird

Length of output: 1554


🏁 Script executed:

# Check if there's any validation of this timeout value elsewhere
rg "inactivity|InactivityThreshold" client/ -t go --max-count=20

Repository: netbirdio/netbird

Length of output: 6003


🏁 Script executed:

# Find callers of ResolveModeFromEnv()
rg "ResolveModeFromEnv" client/ --type go -B 2 -A 5

Repository: netbirdio/netbird

Length of output: 4525


🏁 Script executed:

# Check where the timeout value is ultimately used
rg "conn_mgr\.go|inactivityThresholdEnv" client/ --type go -B 3 -A 8

Repository: netbirdio/netbird

Length of output: 2144


🏁 Script executed:

# Check the test expectations for parsing
cat -n client/internal/peer/env_test.go | head -80

Repository: netbirdio/netbird

Length of output: 2672


🏁 Script executed:

# Check if there's any usage of the timeout that expects valid uint32 bounds
rg "relayTimeoutSecs|RelayTimeout" client/ --type go -B 2 -A 3

Repository: netbirdio/netbird

Length of output: 12907


Guard duration bounds before converting to uint32.

The code accepts negative durations via time.ParseDuration() without validation. Converting a negative time.Duration to uint32 produces a wrapped integer with platform-dependent behavior. Additionally, oversized durations exceeding uint32 range will wrap around, producing invalid timeout values.

🛠️ Suggested patch
 	if raw := os.Getenv(envInactivityThreshold); raw != "" {
 		if d, err := time.ParseDuration(raw); err == nil {
-			timeoutSecs = uint32(d.Seconds())
-			warnDeprecated(envInactivityThreshold, "the relay_timeout setting on the management server")
+			secs := d / time.Second
+			const maxTimeoutSecs = int64(^uint32(0))
+			switch {
+			case secs < 0:
+				log.Warnf("ignoring %s=%q: duration must be non-negative", envInactivityThreshold, raw)
+			case secs > maxTimeoutSecs:
+				log.Warnf("ignoring %s=%q: duration exceeds max supported value", envInactivityThreshold, raw)
+			default:
+				timeoutSecs = uint32(secs)
+				warnDeprecated(envInactivityThreshold, "the relay_timeout setting on the management server")
+			}
 		} else {
 			log.Warnf("ignoring %s=%q: %v", envInactivityThreshold, raw, err)
 		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if d, err := time.ParseDuration(raw); err == nil {
timeoutSecs = uint32(d.Seconds())
warnDeprecated(envInactivityThreshold, "the relay_timeout setting on the management server")
if raw := os.Getenv(envInactivityThreshold); raw != "" {
if d, err := time.ParseDuration(raw); err == nil {
secs := d / time.Second
const maxTimeoutSecs = int64(^uint32(0))
switch {
case secs < 0:
log.Warnf("ignoring %s=%q: duration must be non-negative", envInactivityThreshold, raw)
case secs > maxTimeoutSecs:
log.Warnf("ignoring %s=%q: duration exceeds max supported value", envInactivityThreshold, raw)
default:
timeoutSecs = uint32(secs)
warnDeprecated(envInactivityThreshold, "the relay_timeout setting on the management server")
}
} else {
log.Warnf("ignoring %s=%q: %v", envInactivityThreshold, raw, err)
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/internal/peer/env.go` around lines 75 - 77, Validate the parsed
duration from time.ParseDuration(raw) before assigning to timeoutSecs: ensure
the duration is non-negative and that d.Seconds() does not exceed the maximum
uint32 value (so it won't wrap when cast). Specifically, in the block handling
envInactivityThreshold, check d >= 0 and d.Seconds() <= float64(math.MaxUint32)
(or compare against time.Duration(maxUint32Seconds)*time.Second) and only then
set timeoutSecs = uint32(d.Seconds()); otherwise log or handle the invalid value
(e.g., ignore the env value or clamp to max) while still calling
warnDeprecated(envInactivityThreshold, "the relay_timeout setting on the
management server").

Comment on lines +177 to +179
ConnectionMode string `json:",omitempty"`
RelayTimeoutSeconds uint32 `json:",omitempty"`
P2pTimeoutSeconds uint32 `json:",omitempty"`
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 | 🟠 Major | 🏗️ Heavy lift

Keep timeout overrides nullable in the persisted profile.

RelayTimeoutSeconds and P2pTimeoutSeconds are stored as plain uint32 values, so once the profile is written you lose the distinction between “unset” and “explicit 0”. That breaks the documented relay semantics (0 = never tear down) across restarts, because omitempty drops the zero and the next load treats it like “no override”. p2p_timeout_seconds has the same bug waiting for Phase 2.

Also applies to: 604-618

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/internal/profilemanager/config.go` around lines 177 - 179, The timeout
fields RelayTimeoutSeconds and P2pTimeoutSeconds should be made nullable so the
persisted profile can distinguish "unset" from an explicit zero: change their
types from uint32 to *uint32 in the profile struct(s) (e.g., the struct
containing ConnectionMode, RelayTimeoutSeconds, P2pTimeoutSeconds) and keep
json:",omitempty" so nil is omitted while an explicit zero is preserved when
set; update any code that reads/writes these fields (decoders, defaults, and
consumers) to handle nil vs non-nil dereferencing and apply the same change for
the corresponding p2p_timeout_seconds occurrences referenced around lines
604-618.

Comment on lines 372 to 375
am.handleRoutingPeerDNSResolutionSettings(ctx, oldSettings, newSettings, userID, accountID)
am.handleLazyConnectionSettings(ctx, oldSettings, newSettings, userID, accountID)
am.handleConnectionModeSettings(ctx, oldSettings, newSettings, userID, accountID)
am.handlePeerLoginExpirationSettings(ctx, oldSettings, newSettings, userID, accountID)
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 | 🟠 Major | ⚡ Quick win

Trigger peer updates when these new settings change.

These fields are now sent to clients via management/internals/shared/grpc/conversion.go, but UpdateAccountSettings still doesn't set updateAccountPeers / bump the network serial when ConnectionMode, RelayTimeoutSeconds, or P2pTimeoutSeconds change. The new audit events added here will fire, but connected peers won't receive the new mode/timeouts until some unrelated settings change or they reconnect.

Suggested fix (apply near the existing `updateAccountPeers` gating above)
 if oldSettings.RoutingPeerDNSResolutionEnabled != newSettings.RoutingPeerDNSResolutionEnabled ||
 	oldSettings.LazyConnectionEnabled != newSettings.LazyConnectionEnabled ||
+	!equalStringPtr(oldSettings.ConnectionMode, newSettings.ConnectionMode) ||
+	!equalUint32Ptr(oldSettings.RelayTimeoutSeconds, newSettings.RelayTimeoutSeconds) ||
+	!equalUint32Ptr(oldSettings.P2pTimeoutSeconds, newSettings.P2pTimeoutSeconds) ||
 	oldSettings.DNSDomain != newSettings.DNSDomain ||
 	oldSettings.AutoUpdateVersion != newSettings.AutoUpdateVersion ||
 	oldSettings.AutoUpdateAlways != newSettings.AutoUpdateAlways {
 	updateAccountPeers = true
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@management/server/account.go` around lines 372 - 375, UpdateAccountSettings
currently doesn't mark updateAccountPeers or bump the network serial when
ConnectionMode, RelayTimeoutSeconds, or P2pTimeoutSeconds change, so peers won't
receive the new values; inside UpdateAccountSettings (near the existing
updateAccountPeers gating above the calls to
am.handleRoutingPeerDNSResolutionSettings / handleLazyConnectionSettings /
handleConnectionModeSettings / handlePeerLoginExpirationSettings) detect
differences on ConnectionMode, RelayTimeoutSeconds, and P2pTimeoutSeconds
between oldSettings and newSettings, set updateAccountPeers = true (or
equivalent), and ensure the account/network serial is bumped so connected peers
are notified of the change.

Comment on lines +229 to +236
if req.Settings.P2pTimeoutSeconds != nil {
v := uint32(*req.Settings.P2pTimeoutSeconds)
returnSettings.P2pTimeoutSeconds = &v
}
if req.Settings.RelayTimeoutSeconds != nil {
v := uint32(*req.Settings.RelayTimeoutSeconds)
returnSettings.RelayTimeoutSeconds = &v
}
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 | 🟠 Major | ⚡ Quick win

Validate timeout ranges before casting to uint32.

p2p_timeout_seconds and relay_timeout_seconds come from signed API fields here. Negative values or anything above uint32 max will silently wrap on the cast (-1 becomes 4294967295), so the handler can persist a completely different timeout instead of returning a 400.

Suggested fix
 if req.Settings.P2pTimeoutSeconds != nil {
+	if *req.Settings.P2pTimeoutSeconds < 0 || *req.Settings.P2pTimeoutSeconds > int64(^uint32(0)) {
+		return nil, status.Errorf(status.InvalidArgument, "p2p_timeout_seconds must be between 0 and %d", ^uint32(0))
+	}
 	v := uint32(*req.Settings.P2pTimeoutSeconds)
 	returnSettings.P2pTimeoutSeconds = &v
 }
 if req.Settings.RelayTimeoutSeconds != nil {
+	if *req.Settings.RelayTimeoutSeconds < 0 || *req.Settings.RelayTimeoutSeconds > int64(^uint32(0)) {
+		return nil, status.Errorf(status.InvalidArgument, "relay_timeout_seconds must be between 0 and %d", ^uint32(0))
+	}
 	v := uint32(*req.Settings.RelayTimeoutSeconds)
 	returnSettings.RelayTimeoutSeconds = &v
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if req.Settings.P2pTimeoutSeconds != nil {
v := uint32(*req.Settings.P2pTimeoutSeconds)
returnSettings.P2pTimeoutSeconds = &v
}
if req.Settings.RelayTimeoutSeconds != nil {
v := uint32(*req.Settings.RelayTimeoutSeconds)
returnSettings.RelayTimeoutSeconds = &v
}
if req.Settings.P2pTimeoutSeconds != nil {
if *req.Settings.P2pTimeoutSeconds < 0 {
return nil, status.Errorf(codes.InvalidArgument, "p2p_timeout_seconds must be non-negative")
}
v := uint32(*req.Settings.P2pTimeoutSeconds)
returnSettings.P2pTimeoutSeconds = &v
}
if req.Settings.RelayTimeoutSeconds != nil {
if *req.Settings.RelayTimeoutSeconds < 0 {
return nil, status.Errorf(codes.InvalidArgument, "relay_timeout_seconds must be non-negative")
}
v := uint32(*req.Settings.RelayTimeoutSeconds)
returnSettings.RelayTimeoutSeconds = &v
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@management/server/http/handlers/accounts/accounts_handler.go` around lines
229 - 236, The code casts signed API timeout fields
(req.Settings.P2pTimeoutSeconds, req.Settings.RelayTimeoutSeconds) straight to
uint32 which will silently wrap on negative or out-of-range values; add explicit
validation before casting: check each non-nil value is >= 0 and <=
math.MaxUint32 (or 4294967295), and if not return a 400 validation error (use
the same error/response helper used elsewhere in accounts_handler.go). Implement
the checks inline where returnSettings.P2pTimeoutSeconds and
returnSettings.RelayTimeoutSeconds are set (or extract to a small helper
validateTimeout(name string, v *int64) error) and only perform the uint32
conversion after the value passes validation.

Comment on lines +1517 to +1521
// P2pTimeoutSeconds Default ICE-worker idle timeout in seconds. 0 = never tear down.
// Effective only in p2p-dynamic mode (added in Phase 2).
// NULL means "use built-in default" (180 minutes).
P2pTimeoutSeconds *int64 `json:"p2p_timeout_seconds,omitempty"`

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 | 🟠 Major | ⚡ Quick win

Validate non-negative timeout values before cast.

Line 1517 and Line 1543 describe non-negative timeout semantics, but these fields are *int64; the account handler currently casts to uint32 directly. Negative inputs can wrap into very large values and silently corrupt timeout behavior.

Suggested fix (in management/server/http/handlers/accounts/accounts_handler.go)
 if req.Settings.P2pTimeoutSeconds != nil {
-    v := uint32(*req.Settings.P2pTimeoutSeconds)
+    if *req.Settings.P2pTimeoutSeconds < 0 {
+        return nil, fmt.Errorf("invalid p2p_timeout_seconds %d: must be >= 0", *req.Settings.P2pTimeoutSeconds)
+    }
+    v := uint32(*req.Settings.P2pTimeoutSeconds)
     returnSettings.P2pTimeoutSeconds = &v
 }
 if req.Settings.RelayTimeoutSeconds != nil {
-    v := uint32(*req.Settings.RelayTimeoutSeconds)
+    if *req.Settings.RelayTimeoutSeconds < 0 {
+        return nil, fmt.Errorf("invalid relay_timeout_seconds %d: must be >= 0", *req.Settings.RelayTimeoutSeconds)
+    }
+    v := uint32(*req.Settings.RelayTimeoutSeconds)
     returnSettings.RelayTimeoutSeconds = &v
 }

Also applies to: 1543-1547

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shared/management/http/api/types.gen.go` around lines 1517 - 1521, The
handler currently casts pointer timeout fields like P2pTimeoutSeconds (and the
other *int64 timeout fields) directly to uint32, which allows negative values to
wrap; update the account handler code that performs the cast to first check for
nil, then validate the pointed int64 is >= 0 and <= math.MaxUint32, returning a
validation error (or reject the request) if out of range; only then convert
safely to uint32. Identify the conversion sites in the accounts handler where
P2pTimeoutSeconds (and the similar timeout fields) are dereferenced/cast and
apply this guard before performing the uint32 cast.

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.

1 participant