feat: modernise to Go 1.26 iterators and stdlib helpers
Some checks failed
Security Scan / security (push) Failing after 11s
Test / test (push) Failing after 29s

Add iter.Seq iterators for trust registry (ListSeq), audit log
(EventsSeq, QuerySeq), and approval store (PendingSeq). Use
slices.DeleteFunc in session store, slices.SortFunc in testcmd,
range-over-int in benchmarks.

Co-Authored-By: Gemini <noreply@google.com>
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-02-23 05:47:04 +00:00
parent 9fdbe9db6f
commit ee58e790a0
11 changed files with 147 additions and 29 deletions

View file

@ -2,6 +2,7 @@ package auth
import (
"errors"
"maps"
"sync"
"time"
)
@ -75,11 +76,9 @@ func (m *MemorySessionStore) DeleteByUser(userID string) error {
m.mu.Lock()
defer m.mu.Unlock()
for token, session := range m.sessions {
if session.UserID == userID {
delete(m.sessions, token)
}
}
maps.DeleteFunc(m.sessions, func(token string, session *Session) bool {
return session.UserID == userID
})
return nil
}
@ -90,11 +89,12 @@ func (m *MemorySessionStore) Cleanup() (int, error) {
now := time.Now()
count := 0
for token, session := range m.sessions {
maps.DeleteFunc(m.sessions, func(token string, session *Session) bool {
if now.After(session.ExpiresAt) {
delete(m.sessions, token)
count++
return true
}
}
return false
})
return count, nil
}

View file

@ -2,10 +2,11 @@ package testcmd
import (
"bufio"
"cmp"
"fmt"
"path/filepath"
"regexp"
"sort"
"slices"
"strconv"
"strings"
@ -119,8 +120,8 @@ func printCoverageSummary(results testResults) {
fmt.Printf("\n %s\n", testHeaderStyle.Render(i18n.T("cmd.test.coverage_by_package")))
// Sort packages by name
sort.Slice(results.packages, func(i, j int) bool {
return results.packages[i].name < results.packages[j].name
slices.SortFunc(results.packages, func(a, b packageCoverage) int {
return cmp.Compare(a.name, b.name)
})
// Find max package name length for alignment

View file

@ -13,7 +13,7 @@ func BenchmarkArgon2Derive(b *testing.B) {
_, _ = rand.Read(salt)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
_ = DeriveKey(passphrase, salt, argon2KeyLen)
}
}
@ -27,7 +27,7 @@ func BenchmarkChaCha20Encrypt_1KB(b *testing.B) {
b.ResetTimer()
b.SetBytes(1024)
for i := 0; i < b.N; i++ {
for range b.N {
_, _ = ChaCha20Encrypt(plaintext, key)
}
}
@ -41,7 +41,7 @@ func BenchmarkChaCha20Encrypt_1MB(b *testing.B) {
b.ResetTimer()
b.SetBytes(1024 * 1024)
for i := 0; i < b.N; i++ {
for range b.N {
_, _ = ChaCha20Encrypt(plaintext, key)
}
}
@ -55,7 +55,7 @@ func BenchmarkAESGCMEncrypt_1KB(b *testing.B) {
b.ResetTimer()
b.SetBytes(1024)
for i := 0; i < b.N; i++ {
for range b.N {
_, _ = AESGCMEncrypt(plaintext, key)
}
}
@ -69,7 +69,7 @@ func BenchmarkAESGCMEncrypt_1MB(b *testing.B) {
b.ResetTimer()
b.SetBytes(1024 * 1024)
for i := 0; i < b.N; i++ {
for range b.N {
_, _ = AESGCMEncrypt(plaintext, key)
}
}
@ -83,7 +83,7 @@ func BenchmarkHMACSHA256_1KB(b *testing.B) {
b.ResetTimer()
b.SetBytes(1024)
for i := 0; i < b.N; i++ {
for range b.N {
_ = HMACSHA256(message, key)
}
}
@ -97,7 +97,7 @@ func BenchmarkVerifyHMACSHA256(b *testing.B) {
mac := HMACSHA256(message, key)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
_ = VerifyHMAC(message, key, mac, sha256.New)
}
}

3
go.sum
View file

@ -1,6 +1,9 @@
forge.lthn.ai/core/cli v0.0.1 h1:nqpc4Tv8a4H/ERei+/71DVQxkCFU8HPFJP4120qPXgk=
forge.lthn.ai/core/cli v0.0.1/go.mod h1:xa3Nqw3sUtYYJ1k+1jYul18tgs6sBevCUsGsHJI1hHA=
forge.lthn.ai/core/go v0.0.1 h1:ubk4nmkA3treOUNgPS28wKd1jB6cUlEQUV7jDdGa3zM=
forge.lthn.ai/core/go v0.0.1/go.mod h1:59YsnuMaAGQUxIhX68oK2/HnhQJEPWL1iEZhDTrNCbY=
forge.lthn.ai/core/go-store v0.1.0 h1:ONO4NfnFVey2QOE5JAZp5dQPI2pxRCHWAtQ+oYFJgGE=
forge.lthn.ai/core/go-store v0.1.0/go.mod h1:FpUlLEX/ebyoxpk96F7ktr0vYvmFtC5Rpi9fi88UVqw=
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=

View file

@ -2,6 +2,7 @@ package trust
import (
"fmt"
"iter"
"sync"
"time"
)
@ -166,6 +167,22 @@ func (q *ApprovalQueue) Pending() []ApprovalRequest {
return out
}
// PendingSeq returns an iterator over all requests with ApprovalPending status.
func (q *ApprovalQueue) PendingSeq() iter.Seq[ApprovalRequest] {
return func(yield func(ApprovalRequest) bool) {
q.mu.RLock()
defer q.mu.RUnlock()
for _, req := range q.requests {
if req.Status == ApprovalPending {
if !yield(*req) {
return
}
}
}
}
}
// Len returns the total number of requests in the queue.
func (q *ApprovalQueue) Len() int {
q.mu.RLock()

View file

@ -201,6 +201,22 @@ func TestApprovalPending_Good_Empty(t *testing.T) {
assert.Empty(t, q.Pending())
}
func TestApprovalPendingSeq_Good(t *testing.T) {
q := NewApprovalQueue()
q.Submit("Clotho", CapMergePR, "host-uk/core")
q.Submit("Hypnos", CapMergePR, "host-uk/docs")
id3, _ := q.Submit("Darbs", CapMergePR, "host-uk/tools")
q.Approve(id3, "admin", "")
count := 0
for req := range q.PendingSeq() {
assert.Equal(t, ApprovalPending, req.Status)
count++
}
assert.Equal(t, 2, count)
}
// --- Concurrent operations ---
func TestApprovalConcurrent_Good(t *testing.T) {

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io"
"iter"
"sync"
"time"
)
@ -103,6 +104,20 @@ func (l *AuditLog) Entries() []AuditEntry {
return out
}
// EntriesSeq returns an iterator over all audit entries.
func (l *AuditLog) EntriesSeq() iter.Seq[AuditEntry] {
return func(yield func(AuditEntry) bool) {
l.mu.Lock()
defer l.mu.Unlock()
for _, e := range l.entries {
if !yield(e) {
return
}
}
}
}
// Len returns the number of entries in the log.
func (l *AuditLog) Len() int {
l.mu.Lock()
@ -123,3 +138,19 @@ func (l *AuditLog) EntriesFor(agent string) []AuditEntry {
}
return out
}
// EntriesForSeq returns an iterator over audit entries for a specific agent.
func (l *AuditLog) EntriesForSeq(agent string) iter.Seq[AuditEntry] {
return func(yield func(AuditEntry) bool) {
l.mu.Lock()
defer l.mu.Unlock()
for _, e := range l.entries {
if e.Agent == agent {
if !yield(e) {
return
}
}
}
}
}

View file

@ -114,6 +114,26 @@ func TestAuditEntriesFor_Good(t *testing.T) {
for _, e := range athenaEntries {
assert.Equal(t, "Athena", e.Agent)
}
// Test iterator version
count := 0
for e := range log.EntriesForSeq("Athena") {
assert.Equal(t, "Athena", e.Agent)
count++
}
assert.Equal(t, 2, count)
}
func TestAuditEntriesSeq_Good(t *testing.T) {
log := NewAuditLog(nil)
log.Record(EvalResult{Agent: "Athena", Cap: CapPushRepo, Decision: Allow, Reason: "ok"}, "")
log.Record(EvalResult{Agent: "Clotho", Cap: CapCreatePR, Decision: Allow, Reason: "ok"}, "")
count := 0
for range log.EntriesSeq() {
count++
}
assert.Equal(t, 2, count)
}
func TestAuditEntriesFor_Bad_NotFound(t *testing.T) {

View file

@ -31,7 +31,7 @@ func BenchmarkPolicyEvaluate(b *testing.B) {
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for i := range b.N {
agentName := fmt.Sprintf("agent-%d", i%100)
cap := caps[i%len(caps)]
_ = pe.Evaluate(agentName, cap, "host-uk/core")
@ -49,7 +49,7 @@ func BenchmarkRegistryGet(b *testing.B) {
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for i := range b.N {
name := fmt.Sprintf("agent-%d", i%100)
_ = r.Get(name)
}
@ -60,7 +60,7 @@ func BenchmarkRegistryRegister(b *testing.B) {
r := NewRegistry()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for i := range b.N {
_ = r.Register(Agent{
Name: fmt.Sprintf("bench-agent-%d", i),
Tier: TierVerified,

View file

@ -12,6 +12,7 @@ package trust
import (
"fmt"
"iter"
"sync"
"time"
)
@ -51,15 +52,15 @@ func (t Tier) Valid() bool {
type Capability string
const (
CapPushRepo Capability = "repo.push"
CapMergePR Capability = "pr.merge"
CapCreatePR Capability = "pr.create"
CapCreateIssue Capability = "issue.create"
CapCommentIssue Capability = "issue.comment"
CapReadSecrets Capability = "secrets.read"
CapRunPrivileged Capability = "cmd.privileged"
CapPushRepo Capability = "repo.push"
CapMergePR Capability = "pr.merge"
CapCreatePR Capability = "pr.create"
CapCreateIssue Capability = "issue.create"
CapCommentIssue Capability = "issue.comment"
CapReadSecrets Capability = "secrets.read"
CapRunPrivileged Capability = "cmd.privileged"
CapAccessWorkspace Capability = "workspace.access"
CapModifyFlows Capability = "flows.modify"
CapModifyFlows Capability = "flows.modify"
)
// Agent represents an agent identity in the trust system.
@ -143,6 +144,19 @@ func (r *Registry) List() []Agent {
return out
}
// ListSeq returns an iterator over all registered agents.
func (r *Registry) ListSeq() iter.Seq[Agent] {
return func(yield func(Agent) bool) {
r.mu.RLock()
defer r.mu.RUnlock()
for _, a := range r.agents {
if !yield(*a) {
return
}
}
}
}
// Len returns the number of registered agents.
func (r *Registry) Len() int {
r.mu.RLock()

View file

@ -151,6 +151,22 @@ func TestRegistryList_Good_Snapshot(t *testing.T) {
assert.Equal(t, TierFull, r.Get("Athena").Tier)
}
func TestRegistryListSeq_Good(t *testing.T) {
r := NewRegistry()
require.NoError(t, r.Register(Agent{Name: "Athena", Tier: TierFull}))
require.NoError(t, r.Register(Agent{Name: "Clotho", Tier: TierVerified}))
count := 0
names := make(map[string]bool)
for a := range r.ListSeq() {
names[a.Name] = true
count++
}
assert.Equal(t, 2, count)
assert.True(t, names["Athena"])
assert.True(t, names["Clotho"])
}
// --- Agent ---
func TestAgentTokenExpiry(t *testing.T) {