Skip to content

Conversation

@parkervcp
Copy link
Contributor

@parkervcp parkervcp commented Jan 6, 2026

Implements the following security fixes.
pterodactyl/wings#287
pterodactyl/wings#288
pterodactyl/wings#289
pterodactyl/wings#290

Summary by CodeRabbit

  • New Features

    • Added an endpoint to deauthorize users and revoke active sessions across servers.
    • Per-server WebSocket connection limits with clear overflow errors.
    • WebSocket message rate limiting with a throttled event signal and enabled compression.
    • Machine-id generation and configurable passwd/group mounting for server environments.
  • Bug Fixes

    • Suspended servers now reliably close all active WebSocket and SFTP connections.

✏️ Tip: You can customize this high-level summary in your review settings.

@parkervcp parkervcp requested a review from a team as a code owner January 6, 2026 02:23
@coderabbitai
Copy link

coderabbitai bot commented Jan 6, 2026

📝 Walkthrough

Walkthrough

Adds a protected POST /api/deauthorize-user endpoint to revoke user access per-server, implements per-event websocket rate limiting with throttling signals, enhances websocket lifecycle/suspension handling, per-server-per-user token denylist, SFTP per-user context management, and machine-id/passwd configuration and creation.

Changes

Cohort / File(s) Summary
Deauthorization Endpoint
router/router.go, router/router_system.go
New protected POST /api/deauthorize-user route and handler that cancels websockets, cancels SFTP user contexts, and records per-server user denials.
Tokens / Denylist
router/tokens/websocket.go
Adds DenyForServer(s, u) and userDenylist to deny all JWTs for a user on a specific server; Denylisted() now checks this store.
Websocket Rate Limiting & Messaging
router/websocket/limiter.go, router/websocket/message.go, router/websocket/listeners.go, router/websocket/websocket.go
New LimiterBucket and handler.IsThrottled; introduces Event type, ThrottledEvent, Message.Args; per-event rate limits, throttle signaling, read/compression limits, and pre/post-auth suspension/throttling checks.
Websocket Server Flow & Limits
router/router_server_ws.go
Adds per-server connection cap (30), security headers, cancellation on server deletion, immediate close for suspended servers, per-connection limiter usage, and improved error propagation.
SFTP Lifecycle & Handler
sftp/server.go, sftp/handler.go
Refactors AcceptInbound to use Handle(...) helper; adds Handler.User(); uses server-provided per-user context and ensures SFTP server closes when context canceled.
Context Bag Utility
system/context_bag.go
New ContextBag type with Context(key), Cancel(key), and CancelAll() for thread-safe per-key cancelable contexts.
Server Connection APIs
server/connections.go, server/websockets.go
Adds Server.Sftp() to lazily provide per-user ContextBag; adds WebsocketBag.Len() to report connection count.
Server Machine-ID & Lifecycle
server/server.go, server/power.go, server/mounts.go
Adds sftpBag field, CreateMachineID() method and pre-start/create-environment machine-id generation; mounts /etc/machine-id and passwd/group from configured directories; cancels websockets/SFTP on suspension.
Configuration Changes
config/config.go, cmd/root.go
Replaces boolean passwd flag with structured User.Passwd (Enable/Directory) and adds MachineID config; adds ConfigurePasswd() and calls in root command to create passwd files.
Router Server Cleanup & Deprecation
router/router_server.go
Minor formatting, moves machine-id removal to goroutine in deleteServer, and deprecates legacy postServerDenyWSTokens in favor of /api/deauthorize-user.
Miscellaneous
router/tokens/..., go.mod, ...
Various import adjustments and small logging/formatting tweaks across added files.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Router as Router<br/>/api/deauthorize-user
    participant Manager as Server Manager
    participant Websockets as WebSocket Manager
    participant SFTP as SFTP Manager
    participant Tokens as Token Denier

    Client->>Router: POST { user, servers? }
    Router->>Manager: For each targeted server -> Get(serverID)
    Router->>Websockets: server.Websockets().CancelAll()
    Websockets->>Websockets: Cancel/close all conns (per-connection contexts)
    Router->>SFTP: server.Sftp().Cancel(user)
    SFTP->>SFTP: Cancel per-user contexts
    Router->>Tokens: tokens.DenyForServer(serverID, user)
    Tokens->>Tokens: Record timestamp in userDenylist
    Router->>Client: 204 No Content
Loading
sequenceDiagram
    actor Client
    participant WS as WebSocket Handler
    participant Lim as LimiterBucket
    participant Queue as Server Event Processor

    Client->>WS: Send events rapidly
    loop per event
        WS->>Lim: IsThrottled(event)
        alt allowed
            Lim-->>WS: false
            WS->>Queue: process event
        else throttled
            Lim-->>WS: true
            rect rgba(255, 240, 240, 0.5)
            WS->>Client: { event: "throttled", args: [...] }
            end
            WS->>WS: drop event
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I nibbled code beneath the moon,
Denied a token, closed a room,
Throttled chatter, kept it neat,
Contexts tucked in cozy peat,
Warren hums — the network sleeps.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.68% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of the changeset: implementing security fixes across multiple files including WebSocket connection management, user deauthorization, machine-id configuration, and SFTP improvements.

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

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dabd2c9 and 8e29a9e.

📒 Files selected for processing (6)
  • cmd/root.go
  • config/config.go
  • router/router_server.go
  • server/mounts.go
  • server/power.go
  • server/server.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • router/router_server.go
🧰 Additional context used
🧬 Code graph analysis (5)
cmd/root.go (1)
config/config.go (1)
  • ConfigurePasswd (668-688)
server/mounts.go (2)
config/config.go (1)
  • Get (440-448)
environment/settings.go (1)
  • Mount (14-32)
server/power.go (2)
config/config.go (1)
  • Get (440-448)
system/system.go (1)
  • System (60-67)
server/server.go (1)
config/config.go (1)
  • Get (440-448)
config/config.go (1)
system/system.go (1)
  • System (60-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build and Test (ubuntu-22.04, 1.25.1, linux, amd64)
  • GitHub Check: Build and Test (ubuntu-22.04, 1.24.7, linux, amd64)
  • GitHub Check: Analyze (go)
🔇 Additional comments (14)
cmd/root.go (1)

142-144: Passwd file setup is correctly wired into boot sequence.

This is in the right spot (after user creation, before user logging).

server/power.go (1)

217-222: Machine-id preflight is properly gated.

Looks good as an opt-in preflight step.

server/mounts.go (2)

33-49: Passwd/group mounts align with the new config layout.

Mounts are correctly set to read-only and sourced from the configured directory.


51-58: Machine-id mount wiring looks correct.

Per-server source path and read-only target are appropriate.

server/server.go (6)

8-10: No review needed for this import-only change.


271-275: Suspension-triggered disconnects make sense.

This enforces suspension immediately for both websockets and SFTP.


280-282: No review needed for this comment-only tweak.


326-331: Pre-create machine-id on environment setup looks correct.

This matches the mount expectations and keeps creation centralized.


336-350: CreateMachineID implementation looks solid.

Clear logging, deterministic ID, and error wrapping are good.


70-74: The sftpBag field is safely lazy-initialized via the Sftp() accessor.

The field is not initialized in New(), but the Sftp() accessor (in server/connections.go) implements proper nil-checking and initialization with thread-safety via Lock()/Unlock(). No nil dereference risk if this accessor is used for all access to sftpBag.

config/config.go (4)

21-25: No review needed for this import-only change.


131-134: No review needed for this comment-only tweak.


189-205: Passwd/MachineID config blocks are well-structured.

The enable + directory split is clear and aligns with new mounts.


666-688: ConfigurePasswd centralization looks good.

Clear logging and error wrapping.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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

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

Fix all issues with AI Agents 🤖
In @router/router_system.go:
- Around line 281-286: When deauthorizing from all servers the code currently
cancels websockets and SFTP but omits revoking JWTs; add a call to
tokens.DenyForServer for each server in the m.All() loop so tokens are denied
just like in the per-server path. Specifically, inside the else branch iterating
over m.All(), after or before s.Websockets().CancelAll() and
s.Sftp().Cancel(data.User), invoke tokens.DenyForServer(ctx, s.Id(), data.User)
(matching the signature used in the per-server branch) and handle/log any
returned error consistently with the existing per-server error handling.
- Around line 262-269: The handler currently binds JSON into the local struct
via c.BindJSON(&data) but does not validate data.User; add a check after
BindJSON to return a 400 Bad Request when data.User == "" (use the Gin context
like c.JSON or c.AbortWithStatusJSON with an error message) to stop processing
malformed requests before calling the connection cancel/deny-token logic that
relies on data.User.

In @router/websocket/limiter.go:
- Around line 50-67: The For method uses a double-checked locking pattern that
can race between RUnlock() and Lock(); after acquiring the write lock in For,
re-check l.limits[name] before creating and assigning a new limiter to avoid
overwriting a limiter another goroutine may have created. Concretely, inside For
after l.mu.Lock() (and before creating rate.NewLimiter), check if v, ok :=
l.limits[name]; if ok return v, otherwise create, assign l.limits[name] =
rate.NewLimiter(limit, burst) and return it; keep the existing RLock/Unlock and
defer l.mu.Unlock() usage.
- Around line 16-38: IsThrottled holds h.limiter.mu while calling
h.unsafeSendJson which acquires the Handler RWMutex and can deadlock; fix by
updating h.limiter.throttles inside the locked section but record locally (e.g.
needSend := true) whether to send the ThrottledEvent, then unlock (remove defer
lock/unlock pattern) and only call h.unsafeSendJson when not holding
h.limiter.mu; keep calls to l.Allow() and setting throttles[e]=true/false inside
the limiter lock so state remains consistent.

In @server/server.go:
- Around line 203-210: The comment about websocket and SFTP disconnection logic
is misplaced inside parseInvocation; remove or move that block to the
appropriate suspension/Sync() area. Locate the parseInvocation function and
delete the irrelevant comment lines referencing websocket/SFTP/JWT disconnects
(around the code that replaces ${SERVER_PORT}, ${SERVER_MEMORY}, ${SERVER_IP} on
the invocation string), or relocate that comment to the Sync() suspension
handler where websocket/SFTP disconnect logic lives to avoid confusion.

In @sftp/server.go:
- Around line 170-176: The goroutine waiting on ctx.Done() can leak if
rs.Serve() returns first; create a done channel (e.g., done := make(chan
struct{})) and have the goroutine select on both <-ctx.Done() and <-done so it
exits on normal session completion; after rs.Serve() returns, close(done) (or
send a value) to signal normal termination, keeping the existing log call to
srv.Log().WithField("user", conn.User()).Warn("sftp: terminating active
session") and still calling _ = rs.Close() when ctx.Done() triggers.
🧹 Nitpick comments (5)
server/connections.go (1)

10-19: Consider using RLock for read path optimization.

The method uses a write lock for lazy initialization, which is correct but could be optimized since subsequent calls only need read access after initialization.

🔎 Optional optimization using double-checked locking
 func (s *Server) Sftp() *system.ContextBag {
-	s.Lock()
-	defer s.Unlock()
+	s.RLock()
+	if s.sftpBag != nil {
+		defer s.RUnlock()
+		return s.sftpBag
+	}
+	s.RUnlock()
 
+	s.Lock()
+	defer s.Unlock()
+	// Double-check after acquiring write lock
 	if s.sftpBag == nil {
 		s.sftpBag = system.NewContextBag(s.Context())
 	}
 
 	return s.sftpBag
 }

This optimization reduces lock contention for read operations after initialization, though the current implementation is correct.

router/router_server_ws.go (1)

71-82: Minor: Redundant break statement.

The break on line 80 is unnecessary since there's only one case in the select. While not a bug, it can be removed for cleaner code.

🔎 Proposed fix
 	go func() {
 		select {
 		// When the main context is canceled (through disconnect, server deletion, or server
 		// suspension) close the connection itself.
 		case <-ctx.Done():
 			handler.Logger().Debug("closing connection to server websocket")
 			if err := handler.Connection.Close(); err != nil {
 				handler.Logger().WithError(err).Error("failed to close websocket connection")
 			}
-			break
 		}
 	}()
system/context_bag.go (1)

19-21: Consider: Nil context handling.

If nil is passed to NewContextBag, calling Context() will panic when context.WithCancel(nil) is invoked. Consider either documenting this requirement or adding a nil check.

🔎 Proposed fix (option: add nil check)
 func NewContextBag(ctx context.Context) *ContextBag {
+	if ctx == nil {
+		ctx = context.Background()
+	}
 	return &ContextBag{ctx: ctx, items: make(map[string]ctxHolder)}
 }
router/websocket/websocket.go (1)

285-305: Clarify the rationale for the duplicate suspension check.

There are two IsSuspended() checks: one at lines 285-287 (before authentication validation) and another at lines 303-305 (after authentication). While this may be intentional to handle a race where suspension occurs during auth processing, consider adding a brief comment explaining why both checks are necessary. This helps future maintainers understand the security rationale.

The throttling check at lines 289-291 correctly returns nil to silently drop throttled events without surfacing an error.

sftp/server.go (1)

173-173: Minor: Consider using handler.User() for consistency.

The log message uses conn.User() which returns the SSH username, while handler.User() returns the user UUID extracted from permissions. Using handler.User() would be consistent with the context key used at line 167.

🔎 Proposed fix
-		srv.Log().WithField("user", conn.User()).Warn("sftp: terminating active session")
+		srv.Log().WithField("user", handler.User()).Warn("sftp: terminating active session")
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4161a2 and dabd2c9.

📒 Files selected for processing (15)
  • router/router.go
  • router/router_server.go
  • router/router_server_ws.go
  • router/router_system.go
  • router/tokens/websocket.go
  • router/websocket/limiter.go
  • router/websocket/listeners.go
  • router/websocket/message.go
  • router/websocket/websocket.go
  • server/connections.go
  • server/server.go
  • server/websockets.go
  • sftp/handler.go
  • sftp/server.go
  • system/context_bag.go
🧰 Additional context used
🧬 Code graph analysis (6)
server/connections.go (2)
server/server.go (1)
  • Server (30-82)
system/context_bag.go (2)
  • ContextBag (13-17)
  • NewContextBag (19-21)
router/websocket/listeners.go (3)
router/websocket/message.go (2)
  • Message (19-26)
  • Event (3-3)
events/events.go (1)
  • Event (13-16)
internal/models/activity.go (1)
  • Event (11-11)
sftp/server.go (3)
config/config.go (1)
  • Get (429-437)
server/server.go (1)
  • Server (30-82)
sftp/handler.go (1)
  • NewHandler (40-60)
router/router_server_ws.go (3)
router/websocket/limiter.go (1)
  • NewLimiter (40-45)
router/websocket/message.go (3)
  • Message (19-26)
  • Event (3-3)
  • ThrottledEvent (16-16)
server/errors.go (1)
  • ErrSuspended (9-9)
router/websocket/limiter.go (2)
router/websocket/message.go (6)
  • Event (3-3)
  • Message (19-26)
  • ThrottledEvent (16-16)
  • AuthenticationEvent (9-9)
  • SendServerLogsEvent (11-11)
  • SendCommandEvent (12-12)
router/websocket/websocket.go (1)
  • Handler (41-49)
server/server.go (1)
system/context_bag.go (1)
  • ContextBag (13-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build and Test (ubuntu-22.04, 1.25.1, linux, amd64)
  • GitHub Check: Build and Test (ubuntu-22.04, 1.24.7, linux, amd64)
  • GitHub Check: Analyze (go)
🔇 Additional comments (27)
server/websockets.go (1)

28-33: LGTM!

The Len() method is correctly implemented with proper mutex locking, consistent with other methods in WebsocketBag. The thread-safe accessor provides a clean way to check connection count.

router/router_server.go (1)

297-298: LGTM!

The deprecation notice correctly points users to the new /api/deauthorize-user endpoint, facilitating migration to the improved API.

router/router.go (1)

69-69: Verify authorization checks in the deauthorizeUser handler.

The handler is properly defined in router/router_system.go at line 261. However, the implementation lacks explicit authorization validation. The handler does not verify whether the authenticated user has permission to deauthorize the specified user or to target specific servers. Currently, any request that passes the protected middleware can deauthorize any user across any server without additional permission checks. Ensure the handler validates that the authenticated user is authorized to perform this action (e.g., system admin verification or server ownership check).

server/connections.go (1)

10-19: ContextBag implementation is properly thread-safe.

The system.ContextBag is defined in system/context_bag.go and correctly implements thread-safe, per-key context management using sync.Mutex. The NewContextBag() factory function and associated methods are available in the codebase. The Sftp() method's usage of lazy initialization paired with the Server's mutex is correct.

router/websocket/message.go (1)

3-3: Type safety improvement for websocket events.

The introduction of the Event type alias strengthens type safety by preventing arbitrary strings from being used as event identifiers. The addition of ThrottledEvent aligns with the rate limiting features mentioned in the PR objectives. All Message construction sites in the codebase correctly use the Event type, either through predefined constants or explicit type casting.

router/tokens/websocket.go (2)

27-48: LGTM! Per-server-per-user token denial mechanism.

The new DenyForServer function correctly implements per-server-per-user token denial using a composite key. The thread-safe sync.Map is appropriate here.

Consider: Memory growth over time. The userDenylist entries are never cleaned up. For long-running instances with many deauthorization events, this could lead to unbounded memory growth. A periodic cleanup of entries older than the maximum JWT lifetime would prevent this.


104-108: LGTM!

The userDenylist check correctly uses the same composite key format as DenyForServer and properly compares token issuance time against the denial timestamp.

router/router_server_ws.go (3)

31-47: LGTM! Connection limiting with appropriate feedback.

The 30-connection limit per server is a reasonable safeguard against resource exhaustion. The TODO comment acknowledges the limitation of not being per-user, which is acceptable for now.


108-134: LGTM! Rate limiting with throttle notification.

The rate limiter correctly allows a burst of 10 messages then enforces ~5 messages/second sustained rate. The throttle notification is sent once per throttle period, avoiding notification spam.


98-106: LGTM! Suspended server handling.

Properly checks suspension status after connection upgrade and sends a descriptive close message with custom code 4409, allowing clients to handle this case appropriately.

system/context_bag.go (1)

27-57: LGTM! Thread-safe context management.

The ContextBag implementation correctly handles:

  • Lazy context creation with proper mutex protection
  • Safe cancellation of individual or all contexts
  • Proper cleanup by calling cancel functions before removing entries
server/server.go (2)

270-274: LGTM! Immediate connection termination on suspension.

When the server is suspended, all websocket and SFTP connections are immediately canceled. This ensures users cannot continue interacting with a suspended server.


72-72: LGTM!

The sftpBag field is correctly added to support per-user SFTP context management. Based on the AI summary, it's lazily initialized via a Sftp() accessor method.

sftp/handler.go (1)

315-318: LGTM!

Simple accessor method exposing the user UUID for per-user context management in SFTP connection lifecycle. Aligns with the broader PR changes for user-specific connection handling.

router/websocket/listeners.go (1)

132-150: LGTM! Type-safe event handling.

The changes correctly adapt to the Event type introduced in message.go:

  • Line 132: Converts e.Topic (string) to Event type for the Message struct
  • Line 150: Converts back to string for the error handler which expects a string parameter
router/websocket/websocket.go (5)

24-24: LGTM!

Import addition for internal/models is correctly placed to support the activity logging functionality.


48-48: LGTM!

Adding the limiter field to the Handler struct properly integrates the per-connection rate limiting mechanism.


87-87: LGTM - Compression and read limits configured.

Enabling compression with level 5 provides a good balance between CPU usage and bandwidth savings. The 4096-byte read limit is a reasonable security measure to prevent oversized message attacks.

Also applies to: 114-115


123-123: LGTM!

Initializing the limiter via NewLimiter() ensures each handler has its own rate limiting bucket.


158-158: LGTM - Type conversion for prefix check.

Casting v.Event to string before calling HasPrefix aligns with the new Event type alias.

sftp/server.go (3)

129-134: LGTM!

Ignoring the error from ch.Reject is acceptable since the connection is being rejected anyway and there's no meaningful recovery action.


146-146: LGTM!

Ignoring the req.Reply error is appropriate; logging or handling errors here would not change the outcome.


150-157: LGTM - Clean extraction to Handle method.

Delegating SFTP session handling to c.Handle improves code organization and enables per-user context management.

router/websocket/limiter.go (4)

10-14: LGTM!

The LimiterBucket struct is well-designed with appropriate synchronization primitives and maps for tracking rate limiters and throttle state per event type.


40-45: LGTM!

The NewLimiter() function correctly initializes the maps with reasonable initial capacity.


69-83: LGTM - Rate limits are reasonable for security.

The rate limits are well-chosen:

  • Authentication/logs: 2 per 5 seconds (prevents brute force)
  • Commands: 10 per second (allows reasonable interaction)
  • Default: 4 per second (balanced protection)

85-91: LGTM!

The limiterName function correctly groups events to share rate limiters where appropriate while isolating specific high-risk events.

* Implement pterodactyl 292 changes

Add the same change as  pterodactyl/wings#292

This adds configuration for the `machone-id` file that is required by hytale

Creates and manages machine-id files on a per-server basis

Adds code to remove machine-id files when a server is deleted as well.

It also adds a group file for use along with the passwd file

Updated config for passwd

Updated mounts to not set default except for the the correct default.

* Update machine-id generation

Moved machine-id generation code outside of server create only called during initial server creation

Create machine-id file for servers that already exists if the file is missing.

Makes sure tempdir is created on wings start

* remove append

removes the append where not needed
Copy link

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

Caution

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

⚠️ Outside diff range comments (1)
config/config.go (1)

639-662: Guard passwd/machine-id directory creation with enable flags.

Currently these directories are created unconditionally, so disabling the features does not prevent a startup failure if the paths are unwritable (rootless/hardened installs). Gate them by the corresponding Enable flags.

✅ Suggested fix
-	log.WithField("path", _config.System.User.Passwd.Directory).Debug("ensuring passwd directory exists")
-	if err := os.MkdirAll(_config.System.User.Passwd.Directory, 0o700); err != nil {
-		return err
-	}
+	if _config.System.User.Passwd.Enable {
+		log.WithField("path", _config.System.User.Passwd.Directory).Debug("ensuring passwd directory exists")
+		if err := os.MkdirAll(_config.System.User.Passwd.Directory, 0o700); err != nil {
+			return err
+		}
+	}
 
-	log.WithField("path", _config.System.MachineID.Directory).Debug("ensuring machine-id directory exists")
-	if err := os.MkdirAll(_config.System.MachineID.Directory, 0o700); err != nil {
-		return err
-	}
+	if _config.System.MachineID.Enable {
+		log.WithField("path", _config.System.MachineID.Directory).Debug("ensuring machine-id directory exists")
+		if err := os.MkdirAll(_config.System.MachineID.Directory, 0o700); err != nil {
+			return err
+		}
+	}
♻️ Duplicate comments (1)
server/server.go (1)

195-209: Remove the misplaced suspension/SFTP comment block.

This comment is unrelated to parseInvocation and was previously flagged. It’s still present.

🧹 Proposed cleanup
-	// and any connected SFTP clients. We don't need to worry about revoking any JWTs
-	// here since they'll be blocked from re-connecting to the websocket anyways. This
-	// just forces the client to disconnect and attempt to reconnect (rather than waiting
-	// on them to send a message and hit that disconnect logic).
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dabd2c9 and 8e29a9e.

📒 Files selected for processing (6)
  • cmd/root.go
  • config/config.go
  • router/router_server.go
  • server/mounts.go
  • server/power.go
  • server/server.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • router/router_server.go
🧰 Additional context used
🧬 Code graph analysis (5)
cmd/root.go (1)
config/config.go (1)
  • ConfigurePasswd (668-688)
server/mounts.go (2)
config/config.go (1)
  • Get (440-448)
environment/settings.go (1)
  • Mount (14-32)
server/power.go (2)
config/config.go (1)
  • Get (440-448)
system/system.go (1)
  • System (60-67)
server/server.go (1)
config/config.go (1)
  • Get (440-448)
config/config.go (1)
system/system.go (1)
  • System (60-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build and Test (ubuntu-22.04, 1.25.1, linux, amd64)
  • GitHub Check: Build and Test (ubuntu-22.04, 1.24.7, linux, amd64)
  • GitHub Check: Analyze (go)
🔇 Additional comments (14)
cmd/root.go (1)

142-144: Passwd file setup is correctly wired into boot sequence.

This is in the right spot (after user creation, before user logging).

server/power.go (1)

217-222: Machine-id preflight is properly gated.

Looks good as an opt-in preflight step.

server/mounts.go (2)

33-49: Passwd/group mounts align with the new config layout.

Mounts are correctly set to read-only and sourced from the configured directory.


51-58: Machine-id mount wiring looks correct.

Per-server source path and read-only target are appropriate.

server/server.go (6)

8-10: No review needed for this import-only change.


271-275: Suspension-triggered disconnects make sense.

This enforces suspension immediately for both websockets and SFTP.


280-282: No review needed for this comment-only tweak.


326-331: Pre-create machine-id on environment setup looks correct.

This matches the mount expectations and keeps creation centralized.


336-350: CreateMachineID implementation looks solid.

Clear logging, deterministic ID, and error wrapping are good.


70-74: The sftpBag field is safely lazy-initialized via the Sftp() accessor.

The field is not initialized in New(), but the Sftp() accessor (in server/connections.go) implements proper nil-checking and initialization with thread-safety via Lock()/Unlock(). No nil dereference risk if this accessor is used for all access to sftpBag.

config/config.go (4)

21-25: No review needed for this import-only change.


131-134: No review needed for this comment-only tweak.


189-205: Passwd/MachineID config blocks are well-structured.

The enable + directory split is clear and aligns with new mounts.


666-688: ConfigurePasswd centralization looks good.

Clear logging and error wrapping.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

}

// Replace the defaults with their configured values.
// and any connected SFTP clients. We don't need to worry about revoking any JWTs
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment feels like it is in the wrong place?

QuintenQVD0
QuintenQVD0 previously approved these changes Jan 18, 2026
Copy link
Contributor

@QuintenQVD0 QuintenQVD0 left a comment

Choose a reason for hiding this comment

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

LGTM

@parkervcp parkervcp dismissed QuintenQVD0’s stale review January 18, 2026 14:24

The merge-base changed after approval.

@parkervcp parkervcp merged commit eb6db92 into pelican-dev:main Jan 18, 2026
7 checks passed
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.

2 participants