From e4fecfb4cb27b7c3009e7fa518e2a8879dcb7b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Monnom?= Date: Thu, 20 Aug 2026 14:40:12 -0700 Subject: [PATCH] agent/endpoint: Manifest.Version() digest for skew detection Order-independent fnv64a of the route set so a multi-node layer can tell whether the nodes serving a deployment agree on the manifest (safe to route without inspecting the path) or are mid rolling-deploy (fall back to a path-aware resolve). --- pkg/agent/endpoint/manifest.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/pkg/agent/endpoint/manifest.go b/pkg/agent/endpoint/manifest.go index 6d827d448..93797a86a 100644 --- a/pkg/agent/endpoint/manifest.go +++ b/pkg/agent/endpoint/manifest.go @@ -16,7 +16,9 @@ package endpoint import ( "fmt" + "hash/fnv" "slices" + "strconv" "strings" "github.com/livekit/protocol/livekit" @@ -37,6 +39,27 @@ type Manifest struct { routes []Route } +// Version is a stable fnv64a digest of the route set, used to detect manifest +// skew across nodes serving the same deployment (all-agree => safe to route +// without inspecting the path). It is order-independent: two workers on the +// same revision hash identically regardless of route declaration order. +func (m *Manifest) Version() uint64 { + lines := make([]string, 0, len(m.routes)) + for i := range m.routes { + r := &m.routes[i] + methods := append([]string(nil), r.Methods...) + slices.Sort(methods) + lines = append(lines, strconv.Itoa(int(r.Kind))+" "+r.Template.String()+" "+strings.Join(methods, ",")+" "+strconv.FormatBool(r.Public)) + } + slices.Sort(lines) + h := fnv.New64a() + for _, l := range lines { + _, _ = h.Write([]byte(l)) + _, _ = h.Write([]byte{0}) + } + return h.Sum64() +} + // MatchResult mirrors starlette's Match enum: a FULL match selects the route, a // PARTIAL match (path matched, method didn't) yields 405 only after the whole // table has been scanned, so a later route with the right method still wins.