package store import ( "context" "crypto/rand" "encoding/hex" "fmt" "gitea.mynodes.xyz/ilker/llmgw/internal/secrets" "time" ) // ClusterSecret returns the named secret, creating a random one the first time // it is asked for. // // Insert-if-absent and then read, so two control planes asking at once end up // with the same value: the loser's insert changes nothing and it reads the // winner's. func (s *DB) ClusterSecret(ctx context.Context, name string) (string, error) { b := make([]byte, 32) if _, err := rand.Read(b); err != nil { return "", err } fresh := hex.EncodeToString(b) stored := fresh if s.sealer != nil { var err error if stored, err = s.sealer.Seal(ctx, []byte(fresh)); err != nil { return "", err } } if _, err := s.db.ExecContext(ctx, `INSERT INTO cluster_secrets (name, value, created_at) VALUES (?, ?, ?) ON CONFLICT (name) DO NOTHING`, name, stored, time.Now().UnixMilli()); err != nil { return "", err } var v string if err := s.db.QueryRowContext(ctx, `SELECT value FROM cluster_secrets WHERE name = ?`, name).Scan(&v); err != nil { return "", err } if secrets.IsSealed(v) { if s.sealer == nil { return "", fmt.Errorf("the store's secret %q is sealed, and secrets.master_key is not set to open it", name) } plain, err := s.sealer.Open(ctx, v) if err != nil { return "", fmt.Errorf("the store's secret %q: %w", name, err) } return string(plain), nil } // Kept plain from before a master key: sealed now, the value unchanged. if s.sealer != nil { if sealed, err := s.sealer.Seal(ctx, []byte(v)); err == nil { _, _ = s.db.ExecContext(ctx, `UPDATE cluster_secrets SET value = ? WHERE name = ? AND value = ?`, sealed, name, v) } } return v, nil } // SetSealer seals the store's own secrets with s from now on; values kept // plain are sealed as they are next read. func (s *DB) SetSealer(x secrets.Sealer) { s.sealer = x }