Skip to content

fix: persist generated JWT secret across restarts - #65

Merged
keskad merged 1 commit into
masterfrom
fix/persist-jwt-secret
Aug 23, 2026
Merged

fix: persist generated JWT secret across restarts#65
keskad merged 1 commit into
masterfrom
fix/persist-jwt-secret

Conversation

@keskad

@keskad keskad commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • loco-server generated a random JWT secret on every boot when --jwt-secret / JWT_SECRET / BIGFRED_JWT_SECRET were empty, so hub sessions (and dcc-bus daemons spawned with --jwt-secret) died after a restart.
  • The generated secret is now written into /data/etc/loco-server.conf (JWT_SECRET=…, mode 0600) on first boot and reused afterwards.

Test plan

  • go test ./pkgs/bigfred/server/cli/...
  • On a hub with an empty JWT_SECRET, start loco-server once, confirm the file now contains JWT_SECRET=<hex> and logs generated and persisted JWT secret
  • Restart loco-server and confirm an existing browser session still authenticates

Made with Cursor

A missing JWT_SECRET used to be invented on every boot, so hub sessions
and dcc-bus daemons diverged after a restart. Generate the secret once
and write it into loco-server.conf so the default hub image is
production-safe without operator action.

Co-authored-by: Cursor <cursoragent@cursor.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This Pull Request provides a robust solution for persisting the JWT secret, which is a significant improvement for session stability across restarts. The new PersistJWTSecret function correctly handles file creation, updates existing configurations, and enforces secure file permissions (0600). The updated precedence logic for resolving the JWT secret is clearly documented and implemented, ensuring a consistent and secure approach to secret management. The addition of comprehensive tests for the persistence logic further strengthens the change.

// PersistJWTSecret writes JWT_SECRET=<secret> into path, replacing a
// commented or empty JWT_SECRET line and creating the file when missing.
// The file mode is 0600 because it now holds a signing key.
func PersistJWTSecret(path, secret string) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] POSITIVE: The PersistJWTSecret function is well-implemented, handling file creation, updates, and ensuring secure file permissions (0600). The os.Chmod call after os.WriteFile is a good defensive measure to guarantee the correct permissions are set, regardless of whether the file was newly created or already existed.

return os.Chmod(path, 0o600)
}

func upsertDotenvKey(text, key, value string) string {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] POSITIVE: The upsertDotenvKey and isDotenvKeyLine helper functions are robust, correctly handling commented lines, case-insensitivity for keys, and ensuring proper newline handling when appending.

t.Fatalf("DefaultPath() = %q, want %q", got, want)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] POSITIVE: The new tests provide excellent coverage for the JWT secret persistence logic, including scenarios for replacing commented lines, creating new files, and verifying file permissions. This ensures the reliability of the new functionality.

cmd.Flags().StringVar(&f.JWTSecret, "jwt-secret", "",
"hex/base64 secret used to sign session JWTs. Falls back to BIGFRED_JWT_SECRET "+
"env var; a random per-run secret is generated when empty (sessions don't survive restarts).")
"hex/base64 secret used to sign session JWTs. Falls back to loco-server.conf JWT_SECRET, "+

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] POSITIVE: The updated flag description and the comment for resolveJWTSecret clearly articulate the new precedence order for JWT secret resolution, which now includes persistence to loco-server.conf. This improves clarity and maintainability.

secret := hex.EncodeToString(buf)
path := config.DefaultPath()
if err := config.PersistJWTSecret(path, secret); err != nil {
log.WithError(err).WithField("path", path).Warn("generated JWT secret but could not persist it; sessions will not survive a restart")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] POSITIVE: The logging for both successful persistence and cases where persistence fails is very helpful for debugging and operational monitoring. It clearly indicates the state of the JWT secret generation and storage.

@keskad
keskad merged commit 5a74c34 into master Aug 23, 2026
5 checks passed
@keskad
keskad deleted the fix/persist-jwt-secret branch August 23, 2026 11:03
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