This repo had no version control prior to this commit. The import is a
straight snapshot of the working tree at 2026-05-03; the deployed
binary on fihelvop01 was being rebuilt from this source via `make
build` + scp into place, with no upstream review path.
The snapshot already includes one in-flight fix made on 2026-05-03 to
internal/service/persona.go:GetSelfModel — the handler queried
`source` and `strength` columns plus an `is_active = true` filter on
persona.persona_commitments, none of which exist on that table (its
shape is session-bound commitments with `status`, `commitment_meta`,
etc.). The query returned a 500 every time SynapseHub bootstrapped a
persona's self-model, dropping the IdentityConstraints / Commitments /
ConscienceStandards layer from the assembled prompt. The patched
query reads existing columns only (commitment_text, commitment_type),
filters on `status='active'`, and synthesises Source="learned" /
Strength=1.0 to keep the SelfModel response shape stable for callers.
Verified live: `GET /api/v1/personas/70f7cfd9-.../self-model` now
returns 200 with `{identityConstraints:[],commitments:[],
conscienceStandards:[]}` instead of 500.
Future changes go through PRs against this repo — no more bin-only
deploys.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
111 lines
2.3 KiB
Go
111 lines
2.3 KiB
Go
package service
|
|
|
|
import (
|
|
"bufio"
|
|
"strings"
|
|
|
|
"github.com/rs/zerolog"
|
|
|
|
"github.com/gosec/gsc-ops-api/internal/client"
|
|
"github.com/gosec/gsc-ops-api/pkg/types"
|
|
)
|
|
|
|
// PGPService handles Hockeypuck PGP key operations
|
|
type PGPService struct {
|
|
client *client.HockeypuckClient
|
|
logger zerolog.Logger
|
|
}
|
|
|
|
// NewPGPService creates a new PGP service
|
|
func NewPGPService(hkpClient *client.HockeypuckClient, logger zerolog.Logger) *PGPService {
|
|
return &PGPService{
|
|
client: hkpClient,
|
|
logger: logger.With().Str("service", "pgp").Logger(),
|
|
}
|
|
}
|
|
|
|
// SearchKeys searches for PGP keys
|
|
func (s *PGPService) SearchKeys(query string) ([]types.PGPKey, error) {
|
|
result, err := s.client.SearchKeys(query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if result == "" {
|
|
return []types.PGPKey{}, nil
|
|
}
|
|
|
|
return parseMachineReadableIndex(result), nil
|
|
}
|
|
|
|
// GetKey retrieves a PGP key by key ID
|
|
func (s *PGPService) GetKey(keyID string) (*types.PGPKey, error) {
|
|
armoredKey, err := s.client.GetKey(keyID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if armoredKey == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
return &types.PGPKey{
|
|
KeyID: keyID,
|
|
ArmoredKey: armoredKey,
|
|
}, nil
|
|
}
|
|
|
|
// UploadKey uploads a PGP public key
|
|
func (s *PGPService) UploadKey(keyText string) error {
|
|
return s.client.UploadKey(keyText)
|
|
}
|
|
|
|
// DeleteKey deletes a PGP key
|
|
func (s *PGPService) DeleteKey(keyID string) error {
|
|
return s.client.DeleteKey(keyID)
|
|
}
|
|
|
|
// parseMachineReadableIndex parses the HKP machine-readable index format
|
|
func parseMachineReadableIndex(data string) []types.PGPKey {
|
|
keys := []types.PGPKey{}
|
|
var current *types.PGPKey
|
|
|
|
scanner := bufio.NewScanner(strings.NewReader(data))
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
fields := strings.Split(line, ":")
|
|
|
|
if len(fields) < 2 {
|
|
continue
|
|
}
|
|
|
|
switch fields[0] {
|
|
case "pub":
|
|
if current != nil {
|
|
keys = append(keys, *current)
|
|
}
|
|
current = &types.PGPKey{}
|
|
if len(fields) > 1 {
|
|
current.KeyID = fields[1]
|
|
}
|
|
if len(fields) > 2 {
|
|
current.Algorithm = fields[2]
|
|
}
|
|
if len(fields) > 4 {
|
|
current.Created = fields[4]
|
|
}
|
|
if len(fields) > 5 {
|
|
current.Expires = fields[5]
|
|
}
|
|
case "uid":
|
|
if current != nil && len(fields) > 1 {
|
|
current.UIDs = append(current.UIDs, fields[1])
|
|
}
|
|
}
|
|
}
|
|
|
|
if current != nil {
|
|
keys = append(keys, *current)
|
|
}
|
|
|
|
return keys
|
|
}
|