From 2c0ab3a592883aaaf5793f0a0b2864f0cb223c24 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 26 Oct 2026 20:19:54 +0100 Subject: [PATCH] =?UTF-8?q?A=20have=20carries=20the=20values=20a=20self-co?= =?UTF-8?q?mposing=20node's=20running=20composition=20holds=20sealed=20to?= =?UTF-8?q?=20its=20key,=20so=20its=20composer=20reuses=20them=20after=20a?= =?UTF-8?q?=20restart=20(review=20of=20L2,=20round=202;=20issue=201126,=20?= =?UTF-8?q?ADR=200547=20=C2=A73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go/delivery/delivery.go | 13 ++++++++++++- go/delivery/delivery_test.go | 11 +++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/go/delivery/delivery.go b/go/delivery/delivery.go index 1966d1b..d4f6288 100644 --- a/go/delivery/delivery.go +++ b/go/delivery/delivery.go @@ -48,7 +48,9 @@ import ( "encoding/json" "errors" "fmt" + "reflect" "regexp" + "slices" "strings" "time" ) @@ -348,6 +350,11 @@ type Have struct { // composition that waits for a person's yes (ADR 0547 §3); empty otherwise. Judgement Judgement `json:"judgement,omitempty"` Waits string `json:"waits,omitempty"` + // Sealed is, for a module the node composes itself, each value its running composition carries sealed to the node's + // key (a secret, a credential, a vault box), as applied (ADR 0547 §3, issue 1126): what the node's composer reuses + // after a restart while it opens to the vault's value, so a value sealed again moves no composition. Openable by the + // node alone, as the vault hands them over the bus. Empty on every other have. + Sealed []string `json:"sealed,omitempty"` } // UserListWritten is the bus's user list as the node-engine of the bus node wrote it to the bus module's file, applied @@ -533,7 +540,11 @@ func DecodePulse(data []byte) (Pulse, error) { func SameHave(a, b Have) bool { ua, ub := a.UserList, b.UserList a.UserList, b.UserList = nil, nil - if a != b || (ua == nil) != (ub == nil) { + if !slices.Equal(a.Sealed, b.Sealed) { + return false + } + a.Sealed, b.Sealed = nil, nil + if !reflect.DeepEqual(a, b) || (ua == nil) != (ub == nil) { return false } if ua == nil { diff --git a/go/delivery/delivery_test.go b/go/delivery/delivery_test.go index a831685..8c7fc19 100644 --- a/go/delivery/delivery_test.go +++ b/go/delivery/delivery_test.go @@ -421,6 +421,17 @@ func TestSame(t *testing.T) { if SameHave(a, b) { t.Error("different haves same") } + // The sealed values a self-composing node runs are part of its have (issue 1126). + sa, sb := a, a + sa.Sealed, sb.Sealed = []string{`{"box":1}`}, []string{`{"box":1}`} + if !SameHave(sa, sb) || SameHave(sa, a) { + t.Error("equal sealed values differ, or one said and one not are the same") + } + if raw, err := EncodeHave(sa); err != nil { + t.Error(err) + } else if back, err := DecodeHave(raw); err != nil || !SameHave(back, sa) { + t.Errorf("sealed values do not travel: %+v, %v", back, err) + } // The user list is compared by what it says, never by where it is held (ADR 0547 §8). at := time.Date(2026, 10, 25, 21, 0, 0, 0, time.UTC) ua, ub := a, a