package store
import (
"time"
bolt "go.etcd.io/bbolt"
)
// TrustStatus tracks whether a remote instance's CA has been approved.
type TrustStatus string
const (
TrustPending TrustStatus = "pending"
TrustTrusted TrustStatus = "trusted"
)
// TrustedCA records a remote instance's CA public key and whether we trust
// it, per DESIGN.md §5.2.
type TrustedCA struct {
Domain string `json:"domain"`
CAPublicKey string `json:"ca_public_key"` // authorized_keys format
Status TrustStatus `json:"status"`
FirstSeenAt time.Time `json:"first_seen_at"`
ApprovedAt *time.Time `json:"approved_at,omitempty"`
}
func (s *Store) GetTrustedCA(domain string) (TrustedCA, error) {
var t TrustedCA
err := s.db.View(func(tx *bolt.Tx) error {
return getJSON(tx, bucketTrust, domain, &t)
})
return t, err
}
func (s *Store) PutTrustedCA(t TrustedCA) error {
return s.db.Update(func(tx *bolt.Tx) error {
return putJSON(tx, bucketTrust, t.Domain, t)
})
}
func (s *Store) ListTrustedCAs() ([]TrustedCA, error) {
var out []TrustedCA
err := s.db.View(func(tx *bolt.Tx) error {
var err error
out, err = listJSON[TrustedCA](tx, bucketTrust)
return err
})
return out, err
}
// CountPendingTrust is a cheap version of ListTrustedCAs for the admin nav
// badge (see cmd/gitfed-web/render.go) — computed on every page render for
// a logged-in admin, so it stays a count rather than the full list.
func (s *Store) CountPendingTrust() (int, error) {
all, err := s.ListTrustedCAs()
if err != nil {
return 0, err
}
n := 0
for _, t := range all {
if t.Status == TrustPending {
n++
}
}
return n, nil
}
// ApproveTrustedCA flips a pending CA record to trusted.
func (s *Store) ApproveTrustedCA(domain string) error {
return s.db.Update(func(tx *bolt.Tx) error {
var t TrustedCA
if err := getJSON(tx, bucketTrust, domain, &t); err != nil {
return err
}
now := time.Now().UTC()
t.Status = TrustTrusted
t.ApprovedAt = &now
return putJSON(tx, bucketTrust, domain, t)
})
}
func (s *Store) DeleteTrustedCA(domain string) error {
return s.db.Update(func(tx *bolt.Tx) error {
return deleteKey(tx, bucketTrust, domain)
})
}