package handlers import ( "context" "net/http" "github.com/Fimeg/RedFlag/aggregator-server/internal/database/queries" "github.com/Fimeg/RedFlag/aggregator-server/internal/services" "github.com/gin-gonic/gin" ) // SystemHandler handles system-level operations type SystemHandler struct { signingService *services.SigningService signingKeyQueries *queries.SigningKeyQueries } // NewSystemHandler creates a new system handler func NewSystemHandler(ss *services.SigningService, skq *queries.SigningKeyQueries) *SystemHandler { return &SystemHandler{ signingService: ss, signingKeyQueries: skq, } } // GetPublicKey returns the server's Ed25519 public key for signature verification. // This allows agents to fetch the public key at runtime instead of embedding it at build time. func (h *SystemHandler) GetPublicKey(c *gin.Context) { if h.signingService == nil || !h.signingService.IsEnabled() { c.JSON(http.StatusServiceUnavailable, gin.H{ "error": "signing service not configured", "hint": "Set REDFLAG_SIGNING_PRIVATE_KEY environment variable", }) return } pubKeyHex := h.signingService.GetPublicKey() fingerprint := h.signingService.GetPublicKeyFingerprint() keyID := h.signingService.GetCurrentKeyID() // Try to get version from DB; fall back to 1 if unavailable version := 1 if h.signingKeyQueries != nil { ctx := context.Background() if primaryKey, err := h.signingKeyQueries.GetPrimarySigningKey(ctx); err == nil { version = primaryKey.Version } } c.JSON(http.StatusOK, gin.H{ "public_key": pubKeyHex, "fingerprint": fingerprint, "algorithm": "ed25519", "key_size": 32, "key_id": keyID, "version": version, }) } // GetActivePublicKeys returns all currently active public keys for key-rotation-aware agents. // This is a rate-limited public endpoint — no authentication required. func (h *SystemHandler) GetActivePublicKeys(c *gin.Context) { if h.signingService == nil || !h.signingService.IsEnabled() { c.JSON(http.StatusServiceUnavailable, gin.H{ "error": "signing service not configured", }) return } ctx := c.Request.Context() activeKeys, err := h.signingService.GetAllActivePublicKeys(ctx) // Build response — always return at least the current key type keyEntry struct { KeyID string `json:"key_id"` PublicKey string `json:"public_key"` IsPrimary bool `json:"is_primary"` Version int `json:"version"` Algorithm string `json:"algorithm"` } if err != nil || len(activeKeys) == 0 { // Fall back to single-entry response with current key c.JSON(http.StatusOK, []keyEntry{ { KeyID: h.signingService.GetCurrentKeyID(), PublicKey: h.signingService.GetPublicKeyHex(), IsPrimary: true, Version: 1, Algorithm: "ed25519", }, }) return } entries := make([]keyEntry, 0, len(activeKeys)) for _, k := range activeKeys { entries = append(entries, keyEntry{ KeyID: k.KeyID, PublicKey: k.PublicKey, IsPrimary: k.IsPrimary, Version: k.Version, Algorithm: k.Algorithm, }) } c.JSON(http.StatusOK, entries) } // GetSystemInfo returns general system information func (h *SystemHandler) GetSystemInfo(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "version": "v0.1.21", "name": "RedFlag Aggregator", "description": "Self-hosted update management platform", "features": []string{ "agent_management", "update_tracking", "command_execution", "ed25519_signing", "key_rotation", }, }) }