refactor(server): remove bootstrap admin path
The bootstrap-admin-from-env-vars flow was a holdover from before self-registration could create the first admin. Now that handleRegister + CreateUserFirstAdminRace promotes the first user to register on an empty users table (verified shipped in v2026.05.08.2 / #376), the bootstrap path is just a second source of truth that confuses operators (and leaves an "admin" account in the DB that nobody asked for). Removes: - internal/auth/bootstrap.go + its test - The auth.Bootstrap call from cmd/minstrel/main.go - AuthConfig + AdminBootstrapConfig structs from config - MINSTREL_AUTH_ADMIN_USERNAME / _PASSWORD env reads + their test - The auth: block from config.example.yaml - Bootstrap-related comments in docker-compose.yml + README.md The README quickstart now points operators at /register on first start instead of "watch the logs for a one-time password." Existing instances keep their bootstrap-admin row in the DB; operators who want it gone can register a new admin via /register, promote them in /admin/users, then delete the old bootstrap user (last-admin guard will require the new admin to be promoted first). No migration needed. Recovery story for forgotten admin passwords now hinges on Fable #321 (admin password reset CLI) — currently the only path back in if no other admin exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,6 @@ type Config struct {
|
||||
Storage StorageConfig `yaml:"storage"`
|
||||
Database DatabaseConfig `yaml:"database"`
|
||||
Log LogConfig `yaml:"log"`
|
||||
Auth AuthConfig `yaml:"auth"`
|
||||
Library LibraryConfig `yaml:"library"`
|
||||
Subsonic SubsonicConfig `yaml:"subsonic"`
|
||||
Events EventsConfig `yaml:"events"`
|
||||
@@ -56,18 +55,6 @@ type LogConfig struct {
|
||||
Format string `yaml:"format"`
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
AdminBootstrap AdminBootstrapConfig `yaml:"admin_bootstrap"`
|
||||
}
|
||||
|
||||
// AdminBootstrapConfig drives the one-shot admin creation on an empty users
|
||||
// table. Username is required to enable the flow; leaving Password blank asks
|
||||
// the server to generate a random one and log it to stderr.
|
||||
type AdminBootstrapConfig struct {
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
}
|
||||
|
||||
type LibraryConfig struct {
|
||||
ScanPaths []string `yaml:"scan_paths"`
|
||||
ScanOnStartup bool `yaml:"scan_on_startup"`
|
||||
@@ -115,11 +102,6 @@ func Default() Config {
|
||||
AppName: "Minstrel",
|
||||
Description: "Self-hosted music server with Subsonic compatibility, smart shuffle, and Lidarr integration.",
|
||||
},
|
||||
Auth: AuthConfig{
|
||||
AdminBootstrap: AdminBootstrapConfig{
|
||||
Username: "admin",
|
||||
},
|
||||
},
|
||||
Events: EventsConfig{
|
||||
SessionTimeoutMinutes: 30,
|
||||
SkipMaxCompletionRatio: 0.5,
|
||||
@@ -182,12 +164,6 @@ func applyEnv(cfg *Config) {
|
||||
if v, ok := os.LookupEnv("MINSTREL_BRANDING_DESCRIPTION"); ok && v != "" {
|
||||
cfg.Branding.Description = v
|
||||
}
|
||||
if v, ok := os.LookupEnv("MINSTREL_AUTH_ADMIN_USERNAME"); ok {
|
||||
cfg.Auth.AdminBootstrap.Username = v
|
||||
}
|
||||
if v, ok := os.LookupEnv("MINSTREL_AUTH_ADMIN_PASSWORD"); ok {
|
||||
cfg.Auth.AdminBootstrap.Password = v
|
||||
}
|
||||
if v, ok := os.LookupEnv("MINSTREL_LIBRARY_SCAN_PATHS"); ok {
|
||||
cfg.Library.ScanPaths = splitPaths(v)
|
||||
}
|
||||
|
||||
@@ -79,22 +79,6 @@ func TestEnvOverrides(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminBootstrapEnvOverrides(t *testing.T) {
|
||||
t.Setenv("MINSTREL_AUTH_ADMIN_USERNAME", "root")
|
||||
t.Setenv("MINSTREL_AUTH_ADMIN_PASSWORD", "sekret")
|
||||
|
||||
cfg, err := Load("")
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
if cfg.Auth.AdminBootstrap.Username != "root" {
|
||||
t.Errorf("admin username = %q", cfg.Auth.AdminBootstrap.Username)
|
||||
}
|
||||
if cfg.Auth.AdminBootstrap.Password != "sekret" {
|
||||
t.Errorf("admin password = %q", cfg.Auth.AdminBootstrap.Password)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLibraryEnvOverrides(t *testing.T) {
|
||||
t.Setenv("MINSTREL_LIBRARY_SCAN_PATHS", "/music:/other::/third")
|
||||
t.Setenv("MINSTREL_LIBRARY_SCAN_ON_STARTUP", "true")
|
||||
|
||||
Reference in New Issue
Block a user