Feat v0.6.0 cluster registry (#36)
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-frontend (push) Successful in 6s
CI/CD / test-go-pg (push) Successful in 2m40s
CI/CD / test-sqlite (push) Successful in 3m1s
CI/CD / build-and-deploy (push) Successful in 1m22s

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #36.
This commit is contained in:
2026-03-30 22:57:11 +00:00
committed by xcaliber
parent 768f15b3cd
commit 1a7f41493d
21 changed files with 1101 additions and 19 deletions

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"strconv"
"time"
"github.com/joho/godotenv"
)
@@ -85,6 +86,17 @@ type Config struct {
OIDCRolesClaim string // JWT claim for role mapping (default "realm_access.roles")
OIDCGroupsClaim string // JWT claim for group sync (default "groups")
OIDCAdminRole string // IdP role value that maps to admin (default "admin")
// Cluster registry (v0.6.0)
// PG-backed node registry for horizontal scaling. No-op on SQLite.
// CLUSTER_NODE_ID: override for deterministic identity (default: hostname-PID).
// CLUSTER_HEARTBEAT_INTERVAL: tick frequency (default "10s").
// CLUSTER_STALE_THRESHOLD: 3x heartbeat = stale (default "30s").
// CLUSTER_ENDPOINT: advertised address for peer mesh (Phase 2, auto-detect).
ClusterNodeID string
ClusterHeartbeatInterval time.Duration
ClusterStaleThreshold time.Duration
ClusterEndpoint string
}
// Load reads configuration from environment variables.
@@ -143,6 +155,12 @@ func Load() *Config {
OIDCRolesClaim: getEnv("OIDC_ROLES_CLAIM", "realm_access.roles"),
OIDCGroupsClaim: getEnv("OIDC_GROUPS_CLAIM", "groups"),
OIDCAdminRole: getEnv("OIDC_ADMIN_ROLE", "admin"),
// Cluster
ClusterNodeID: getEnv("CLUSTER_NODE_ID", ""),
ClusterHeartbeatInterval: getEnvDuration("CLUSTER_HEARTBEAT_INTERVAL", 10*time.Second),
ClusterStaleThreshold: getEnvDuration("CLUSTER_STALE_THRESHOLD", 30*time.Second),
ClusterEndpoint: getEnv("CLUSTER_ENDPOINT", ""),
}
}
@@ -204,6 +222,15 @@ func getEnvInt(key string, fallback int) int {
return fallback
}
func getEnvDuration(key string, fallback time.Duration) time.Duration {
if v := os.Getenv(key); v != "" {
if d, err := time.ParseDuration(v); err == nil {
return d
}
}
return fallback
}
func getEnvBool(key string, fallback bool) bool {
if v := os.Getenv(key); v != "" {
b, err := strconv.ParseBool(v)