From c186c875259ad09b1eb7439adc1cafe6cfb63b7c Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Fri, 9 Oct 2026 17:01:22 +0200 Subject: [PATCH] Use sha256-based deterministic block IDs - NextBlockID now uses sha256(sessionID:agentID:counter)[:8] for deterministic, collision-resistant block IDs across sessions and agents - Remove blockCounter reset on Clear() to maintain ID stability --- cmd/olliesrv/internal/agent/agent.go | 3 +-- cmd/olliesrv/internal/agent/chat.go | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cmd/olliesrv/internal/agent/agent.go b/cmd/olliesrv/internal/agent/agent.go index e3588d4..144e1da 100644 --- a/cmd/olliesrv/internal/agent/agent.go +++ b/cmd/olliesrv/internal/agent/agent.go @@ -77,7 +77,7 @@ type Agent struct { chatSignalMu sync.Mutex chatSignalCh chan struct{} // closed on new chat data; replaced with fresh channel plan []byte // agent's plan file contents - blockCounter uint64 // monotonic counter for generating stable block IDs + blockCounter uint64 // monotonic counter for block ID generation } // Backend returns the active backend from the runtime. @@ -316,7 +316,6 @@ func (ag *Agent) Clear() error { ag.chatLog = nil ag.chatStart = 0 ag.chatVers++ - ag.blockCounter = 0 ag.chatMu.Unlock() ag.chatCond.Broadcast() if ag.onClear != nil { diff --git a/cmd/olliesrv/internal/agent/chat.go b/cmd/olliesrv/internal/agent/chat.go index bcdd0b2..4d45f63 100644 --- a/cmd/olliesrv/internal/agent/chat.go +++ b/cmd/olliesrv/internal/agent/chat.go @@ -8,6 +8,8 @@ package agent import ( "bytes" + "crypto/sha256" + "encoding/hex" "fmt" "sync" "sync/atomic" @@ -17,11 +19,19 @@ import ( const maxChatLogBytes = 16 * 1024 * 1024 -// NextBlockID returns the next block ID and increments the counter. -// Block IDs are sequential integers formatted as hex strings. +// NextBlockID returns the next deterministic block ID and increments the counter. +// Block IDs are sha256(sessionID + agentID + counter)[:8] — deterministic given +// the same session, agent, and sequence. Counter starts at 0 for fresh agents, +// or at the replayed block count after session restore. func (ag *Agent) NextBlockID() string { - id := atomic.AddUint64(&ag.blockCounter, 1) - 1 - return fmt.Sprintf("%x", id) + n := atomic.AddUint64(&ag.blockCounter, 1) - 1 + h := sha256.New() + h.Write([]byte(ag.sessionID)) + h.Write([]byte(":")) + h.Write([]byte(ag.id)) + h.Write([]byte(":")) + h.Write([]byte(fmt.Sprintf("%d", n))) + return hex.EncodeToString(h.Sum(nil))[:8] } // AppendChat appends data to the chat log and notifies stream readers.