Severity: Medium (cluster of related attach-path defects)
1. Hello-drop race on accept
internal/transport/ws/server.go:33 + connection.go:78: NewConnection starts readLoop before bindWSConnection (cmd/repram/main.go:949) registers the hello handler. A hello already buffered when readLoop wins the schedule is dispatched to an empty handler snapshot and lost → transient waits the full 10s attach timeout; substrate holds the socket until the 30s no-hello watchdog. Fix: allow handler registration before the read loop starts.
2. No initial-attach retry
cmd/repram/main.go:275-312: if every seed refuses the WS attach at boot, the goroutine logs "degraded — HTTP gossip only" and exits. The reattach loop only starts from a parent goodbye/close, which requires one successful attach first. A transient booting while substrates are briefly down never joins the tree until restart. Contrast HTTP isolation recovery (#85), which polls every 30s.
3. PUT echo to originating child + TTL reset
internal/tree/manager.go:671-696 + internal/cluster/node.go:405: BroadcastToChildren(msg) doesn't exclude the connection the PUT arrived on. A WS-relayed PUT is echoed back to its originator; an originator that didn't go through BroadcastToEnclave hasn't MarkSeen its own MessageID, so the echo re-stores the key (resetting CreatedAt/ExpiresAt to a fresh TTL) and triggers a pointless ForwardToEnclave. Latent until a transient sends PUTs up the pipe (#153).
4. Reattach thundering herd
internal/tree/manager.go:736-827: every child of a dying substrate gets the same ordered alternative list and dials immediately with no jitter; alternatives also include non-inbound peers (no inbound-capability flag in gossip.Node) whose /v1/ws returns 404, so candidates[:5] can be entirely useless.
Severity: Medium (cluster of related attach-path defects)
1. Hello-drop race on accept
internal/transport/ws/server.go:33+connection.go:78:NewConnectionstartsreadLoopbeforebindWSConnection(cmd/repram/main.go:949) registers the hello handler. A hello already buffered when readLoop wins the schedule is dispatched to an empty handler snapshot and lost → transient waits the full 10s attach timeout; substrate holds the socket until the 30s no-hello watchdog. Fix: allow handler registration before the read loop starts.2. No initial-attach retry
cmd/repram/main.go:275-312: if every seed refuses the WS attach at boot, the goroutine logs "degraded — HTTP gossip only" and exits. The reattach loop only starts from a parent goodbye/close, which requires one successful attach first. A transient booting while substrates are briefly down never joins the tree until restart. Contrast HTTP isolation recovery (#85), which polls every 30s.3. PUT echo to originating child + TTL reset
internal/tree/manager.go:671-696+internal/cluster/node.go:405:BroadcastToChildren(msg)doesn't exclude the connection the PUT arrived on. A WS-relayed PUT is echoed back to its originator; an originator that didn't go throughBroadcastToEnclavehasn'tMarkSeenits own MessageID, so the echo re-stores the key (resetting CreatedAt/ExpiresAt to a fresh TTL) and triggers a pointless ForwardToEnclave. Latent until a transient sends PUTs up the pipe (#153).4. Reattach thundering herd
internal/tree/manager.go:736-827: every child of a dying substrate gets the same ordered alternative list and dials immediately with no jitter; alternatives also include non-inbound peers (no inbound-capability flag ingossip.Node) whose/v1/wsreturns 404, socandidates[:5]can be entirely useless.