From 779e36aaef24f17259ef0391daf1a72dac4c51d6 Mon Sep 17 00:00:00 2001 From: mr Date: Tue, 24 Feb 2026 14:31:37 +0100 Subject: [PATCH] Pass + Doc --- daemons/node/common/common_stream.go | 18 +- daemons/node/nats.go | 114 ++++++- daemons/node/node.go | 6 +- daemons/node/pubsub/handler.go | 3 +- daemons/node/pubsub/publish.go | 21 +- daemons/node/pubsub/subscribe.go | 4 +- daemons/node/stream/handler.go | 77 ++++- daemons/node/stream/publish.go | 13 +- daemons/node/stream/service.go | 40 ++- docs/diagrams/01_node_init.mmd | 56 +++ docs/diagrams/01_node_init.puml | 58 ++++ docs/diagrams/02_node_claim.mmd | 38 +++ docs/diagrams/02_node_claim.puml | 40 +++ docs/diagrams/03_indexer_heartbeat.mmd | 47 +++ docs/diagrams/03_indexer_heartbeat.puml | 49 +++ docs/diagrams/04_indexer_publish.mmd | 41 +++ docs/diagrams/04_indexer_publish.puml | 43 +++ docs/diagrams/05_indexer_get.mmd | 49 +++ docs/diagrams/05_indexer_get.puml | 51 +++ docs/diagrams/06_native_registration.mmd | 39 +++ docs/diagrams/06_native_registration.puml | 41 +++ docs/diagrams/07_native_get_consensus.mmd | 60 ++++ docs/diagrams/07_native_get_consensus.puml | 62 ++++ docs/diagrams/08_nats_create_resource.mmd | 49 +++ docs/diagrams/08_nats_create_resource.puml | 50 +++ docs/diagrams/09_nats_propagation.mmd | 66 ++++ docs/diagrams/09_nats_propagation.puml | 68 ++++ docs/diagrams/10_pubsub_search.mmd | 52 +++ docs/diagrams/10_pubsub_search.puml | 54 +++ docs/diagrams/11_stream_search.mmd | 52 +++ docs/diagrams/11_stream_search.puml | 54 +++ docs/diagrams/12_partner_heartbeat.mmd | 58 ++++ docs/diagrams/12_partner_heartbeat.puml | 60 ++++ docs/diagrams/13_planner_flow.mmd | 49 +++ docs/diagrams/13_planner_flow.puml | 51 +++ docs/diagrams/14_native_offload_gc.mmd | 59 ++++ docs/diagrams/14_native_offload_gc.puml | 61 ++++ docs/diagrams/README.md | 43 +++ go.mod | 56 +-- go.sum | 376 ++++++--------------- 40 files changed, 1875 insertions(+), 353 deletions(-) create mode 100644 docs/diagrams/01_node_init.mmd create mode 100644 docs/diagrams/01_node_init.puml create mode 100644 docs/diagrams/02_node_claim.mmd create mode 100644 docs/diagrams/02_node_claim.puml create mode 100644 docs/diagrams/03_indexer_heartbeat.mmd create mode 100644 docs/diagrams/03_indexer_heartbeat.puml create mode 100644 docs/diagrams/04_indexer_publish.mmd create mode 100644 docs/diagrams/04_indexer_publish.puml create mode 100644 docs/diagrams/05_indexer_get.mmd create mode 100644 docs/diagrams/05_indexer_get.puml create mode 100644 docs/diagrams/06_native_registration.mmd create mode 100644 docs/diagrams/06_native_registration.puml create mode 100644 docs/diagrams/07_native_get_consensus.mmd create mode 100644 docs/diagrams/07_native_get_consensus.puml create mode 100644 docs/diagrams/08_nats_create_resource.mmd create mode 100644 docs/diagrams/08_nats_create_resource.puml create mode 100644 docs/diagrams/09_nats_propagation.mmd create mode 100644 docs/diagrams/09_nats_propagation.puml create mode 100644 docs/diagrams/10_pubsub_search.mmd create mode 100644 docs/diagrams/10_pubsub_search.puml create mode 100644 docs/diagrams/11_stream_search.mmd create mode 100644 docs/diagrams/11_stream_search.puml create mode 100644 docs/diagrams/12_partner_heartbeat.mmd create mode 100644 docs/diagrams/12_partner_heartbeat.puml create mode 100644 docs/diagrams/13_planner_flow.mmd create mode 100644 docs/diagrams/13_planner_flow.puml create mode 100644 docs/diagrams/14_native_offload_gc.mmd create mode 100644 docs/diagrams/14_native_offload_gc.puml create mode 100644 docs/diagrams/README.md diff --git a/daemons/node/common/common_stream.go b/daemons/node/common/common_stream.go index 1eecf70..22ba3a3 100644 --- a/daemons/node/common/common_stream.go +++ b/daemons/node/common/common_stream.go @@ -469,8 +469,18 @@ func SendHeartbeat(ctx context.Context, proto protocol.ID, name string, h host.H }() } -func TempStream(h host.Host, ad pp.AddrInfo, proto protocol.ID, did string, streams ProtocolStream, mu *sync.RWMutex) (ProtocolStream, error) { - if ctxTTL, err := context.WithTimeout(context.Background(), 2*time.Second); err == nil { +type ProtocolInfo struct { + PersistantStream bool + WaitResponse bool + TTL time.Duration +} + +func TempStream(h host.Host, ad pp.AddrInfo, proto protocol.ID, did string, streams ProtocolStream, pts map[protocol.ID]*ProtocolInfo, mu *sync.RWMutex) (ProtocolStream, error) { + expiry := 2 * time.Second + if pts[proto] != nil { + expiry = pts[proto].TTL + } + if ctxTTL, err := context.WithTimeout(context.Background(), expiry); err == nil { if h.Network().Connectedness(ad.ID) != network.Connected { if err := h.Connect(ctxTTL, ad); err != nil { return streams, err @@ -484,7 +494,7 @@ func TempStream(h host.Host, ad pp.AddrInfo, proto protocol.ID, did string, stre streams[proto] = map[pp.ID]*Stream{} } mu.Unlock() - time.AfterFunc(2*time.Second, func() { + time.AfterFunc(expiry, func() { mu.Lock() defer mu.Unlock() delete(streams[proto], ad.ID) @@ -492,7 +502,7 @@ func TempStream(h host.Host, ad pp.AddrInfo, proto protocol.ID, did string, stre streams[ProtocolPublish][ad.ID] = &Stream{ DID: did, Stream: s, - Expiry: time.Now().UTC().Add(2 * time.Second), + Expiry: time.Now().UTC().Add(expiry), } mu.Unlock() return streams, nil diff --git a/daemons/node/nats.go b/daemons/node/nats.go index 220cb48..8bb40d6 100644 --- a/daemons/node/nats.go +++ b/daemons/node/nats.go @@ -5,14 +5,24 @@ import ( "encoding/json" "fmt" "oc-discovery/daemons/node/common" + "oc-discovery/daemons/node/stream" oclib "cloud.o-forge.io/core/oc-lib" "cloud.o-forge.io/core/oc-lib/config" "cloud.o-forge.io/core/oc-lib/models/peer" "cloud.o-forge.io/core/oc-lib/tools" pp "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" ) +type configPayload struct { + PeerID string `json:"source_peer_id"` +} + +type executionConsidersPayload struct { + PeerIDs []string `json:"peer_ids"` +} + func ListenNATS(n *Node) { tools.NewNATSCaller().ListenNats(map[tools.NATSMethod]func(tools.NATSResponse){ /*tools.VERIFY_RESOURCE: func(resp tools.NATSResponse) { @@ -46,7 +56,7 @@ func ListenNATS(n *Node) { } },*/ tools.CREATE_RESOURCE: func(resp tools.NATSResponse) { - if resp.FromApp == config.GetAppName() && resp.Datatype != tools.PEER { + if resp.FromApp == config.GetAppName() && resp.Datatype != tools.PEER && resp.Datatype != tools.WORKFLOW { return } logger := oclib.GetLogger() @@ -86,6 +96,9 @@ func ListenNATS(n *Node) { }, tools.PROPALGATION_EVENT: func(resp tools.NATSResponse) { + if resp.FromApp == config.GetAppName() { + return + } var propalgation tools.PropalgationMessage err := json.Unmarshal(resp.Payload, &propalgation) var dt *tools.DataType @@ -95,6 +108,20 @@ func ListenNATS(n *Node) { } if err == nil { switch propalgation.Action { + case tools.PB_ADMIRALTY_CONFIG: + case tools.PB_MINIO_CONFIG: + var m configPayload + var proto protocol.ID = stream.ProtocolAdmiraltyConfigResource + if propalgation.Action == tools.PB_MINIO_CONFIG { + proto = stream.ProtocolMinioConfigResource + } + if err := json.Unmarshal(resp.Payload, &m); err == nil { + peers, _ := n.GetPeerRecord(context.Background(), m.PeerID) + for _, p := range peers { + n.StreamService.PublishCommon(&resp.Datatype, resp.User, + p.PeerID, proto, resp.Payload) + } + } case tools.PB_CREATE: case tools.PB_UPDATE: case tools.PB_DELETE: @@ -104,16 +131,83 @@ func ListenNATS(n *Node) { dt, resp.User, propalgation.Payload, ) - case tools.PB_SEARCH: + case tools.PB_CONSIDERS: + switch resp.Datatype { + case tools.BOOKING: + case tools.PURCHASE_RESOURCE: + case tools.WORKFLOW_EXECUTION: + var m executionConsidersPayload + if err := json.Unmarshal(resp.Payload, &m); err == nil { + for _, p := range m.PeerIDs { + peers, _ := n.GetPeerRecord(context.Background(), p) + for _, pp := range peers { + n.StreamService.PublishCommon(&resp.Datatype, resp.User, + pp.PeerID, stream.ProtocolConsidersResource, resp.Payload) + } + } + } + } + case tools.PB_PLANNER: m := map[string]interface{}{} - json.Unmarshal(propalgation.Payload, &m) - n.PubSubService.SearchPublishEvent( - context.Background(), - dt, - fmt.Sprintf("%v", m["type"]), - resp.User, - fmt.Sprintf("%v", m["search"]), - ) + if err := json.Unmarshal(resp.Payload, &m); err == nil { + b := []byte{} + if len(m) > 1 { + b = resp.Payload + } + if m["peer_id"] == nil { // send to every active stream + n.StreamService.Mu.Lock() + if n.StreamService.Streams[stream.ProtocolSendPlanner] != nil { + for pid := range n.StreamService.Streams[stream.ProtocolSendPlanner] { + n.StreamService.PublishCommon(nil, resp.User, pid.String(), stream.ProtocolSendPlanner, b) + } + } + } else { + n.StreamService.PublishCommon(nil, resp.User, fmt.Sprintf("%v", m["peer_id"]), stream.ProtocolSendPlanner, b) + } + n.StreamService.Mu.Unlock() + } + case tools.PB_CLOSE_PLANNER: + m := map[string]interface{}{} + if err := json.Unmarshal(resp.Payload, &m); err == nil { + n.StreamService.Mu.Lock() + if pid, err := pp.Decode(fmt.Sprintf("%v", m["peer_id"])); err == nil { + if n.StreamService.Streams[stream.ProtocolSendPlanner] != nil && n.StreamService.Streams[stream.ProtocolSendPlanner][pid] != nil { + n.StreamService.Streams[stream.ProtocolSendPlanner][pid].Stream.Close() + delete(n.StreamService.Streams[stream.ProtocolSendPlanner], pid) + } + } + n.StreamService.Mu.Unlock() + } + case tools.PB_SEARCH: + if propalgation.DataType == int(tools.PEER) { + m := map[string]interface{}{} + if err := json.Unmarshal(propalgation.Payload, &m); err == nil { + if peers, err := n.GetPeerRecord(context.Background(), fmt.Sprintf("%v", m["search"])); err == nil { + for _, p := range peers { + if b, err := json.Marshal(p); err == nil { + go tools.NewNATSCaller().SetNATSPub(tools.SEARCH_EVENT, tools.NATSResponse{ + FromApp: "oc-discovery", + Datatype: tools.DataType(tools.PEER), + Method: int(tools.SEARCH_EVENT), + Payload: b, + }) + } + } + } + } + + } else { + m := map[string]interface{}{} + if err := json.Unmarshal(propalgation.Payload, &m); err == nil { + n.PubSubService.SearchPublishEvent( + context.Background(), + dt, + fmt.Sprintf("%v", m["type"]), + resp.User, + fmt.Sprintf("%v", m["search"]), + ) + } + } } } }, diff --git a/daemons/node/node.go b/daemons/node/node.go index c20e0f8..04d9a3a 100644 --- a/daemons/node/node.go +++ b/daemons/node/node.go @@ -22,6 +22,7 @@ import ( pubsubs "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/crypto" pp "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" ) type Node struct { @@ -128,7 +129,8 @@ func (d *Node) publishPeerRecord( } for _, ad := range common.StaticIndexers { var err error - if common.StreamIndexers, err = common.TempStream(d.Host, *ad, common.ProtocolPublish, "", common.StreamIndexers, &common.StreamMuIndexes); err != nil { + if common.StreamIndexers, err = common.TempStream(d.Host, *ad, common.ProtocolPublish, "", common.StreamIndexers, map[protocol.ID]*common.ProtocolInfo{}, + &common.StreamMuIndexes); err != nil { continue } stream := common.StreamIndexers[common.ProtocolPublish][ad.ID] @@ -165,7 +167,7 @@ func (d *Node) GetPeerRecord( var info map[string]indexer.PeerRecord for _, ad := range common.StaticIndexers { if common.StreamIndexers, err = common.TempStream(d.Host, *ad, common.ProtocolGet, "", - common.StreamIndexers, &common.StreamMuIndexes); err != nil { + common.StreamIndexers, map[protocol.ID]*common.ProtocolInfo{}, &common.StreamMuIndexes); err != nil { continue } pidR, err := pp.Decode(pid) diff --git a/daemons/node/pubsub/handler.go b/daemons/node/pubsub/handler.go index 50f4bad..0f2b42a 100644 --- a/daemons/node/pubsub/handler.go +++ b/daemons/node/pubsub/handler.go @@ -20,7 +20,7 @@ func (ps *PubSubService) handleEventSearch( // only : on partner followings. 3 c evt *common.Event, action tools.PubSubAction, ) error { - if !(action == tools.PB_SEARCH_RESPONSE || action == tools.PB_SEARCH) { + if !(action == tools.PB_SEARCH) { return nil } if p, err := ps.Node.GetPeerRecord(ctx, evt.From); err == nil && len(p) > 0 { // peerFrom is Unique @@ -32,7 +32,6 @@ func (ps *PubSubService) handleEventSearch( // only : on partner followings. 3 c if err := ps.StreamService.SendResponse(p[0], evt); err != nil { return err } - default: return nil } diff --git a/daemons/node/pubsub/publish.go b/daemons/node/pubsub/publish.go index a614a8f..747ec20 100644 --- a/daemons/node/pubsub/publish.go +++ b/daemons/node/pubsub/publish.go @@ -30,25 +30,12 @@ func (ps *PubSubService) SearchPublishEvent( func (ps *PubSubService) searchPublishEvent( ctx context.Context, dt *tools.DataType, user string, payload []byte) error { - id, err := oclib.GenerateNodeID() - if err != nil { - return err - } - if err := ps.subscribeEvents(ctx, dt, tools.PB_SEARCH_RESPONSE, id, 60); err != nil { // TODO Catpure Event ! - return err - } - return ps.publishEvent(ctx, dt, tools.PB_SEARCH, user, "", payload, false) + return ps.publishEvent(ctx, dt, tools.PB_SEARCH, user, payload) } func (ps *PubSubService) publishEvent( - ctx context.Context, dt *tools.DataType, action tools.PubSubAction, user string, - peerID string, payload []byte, chanNamedByDt bool, + ctx context.Context, dt *tools.DataType, action tools.PubSubAction, user string, payload []byte, ) error { - name := action.String() + "#" + peerID - if chanNamedByDt && dt != nil { // if a datatype is precised then : app.action.datatype#peerID - name = action.String() + "." + (*dt).String() + "#" + peerID - } - from, err := oclib.GenerateNodeID() if err != nil { return err @@ -57,8 +44,8 @@ func (ps *PubSubService) publishEvent( if err != nil { return err } - msg, _ := json.Marshal(models.NewEvent(name, from, dt, user, payload, priv)) - topic, err := ps.PS.Join(name) + msg, _ := json.Marshal(models.NewEvent(action.String(), from, dt, user, payload, priv)) + topic, err := ps.PS.Join(action.String()) if err != nil { return err } diff --git a/daemons/node/pubsub/subscribe.go b/daemons/node/pubsub/subscribe.go index 3c8f3c5..19fbfb2 100644 --- a/daemons/node/pubsub/subscribe.go +++ b/daemons/node/pubsub/subscribe.go @@ -10,7 +10,7 @@ import ( ) func (ps *PubSubService) initSubscribeEvents(ctx context.Context) error { - if err := ps.subscribeEvents(ctx, nil, tools.PB_SEARCH, "", -1); err != nil { + if err := ps.subscribeEvents(ctx, nil, tools.PB_SEARCH, ""); err != nil { return err } return nil @@ -18,7 +18,7 @@ func (ps *PubSubService) initSubscribeEvents(ctx context.Context) error { // generic function to subscribe to DHT flow of event func (ps *PubSubService) subscribeEvents( - ctx context.Context, dt *tools.DataType, action tools.PubSubAction, peerID string, timeout int, + ctx context.Context, dt *tools.DataType, action tools.PubSubAction, peerID string, ) error { logger := oclib.GetLogger() // define a name app.action#peerID diff --git a/daemons/node/stream/handler.go b/daemons/node/stream/handler.go index eeb04b0..79e96c7 100644 --- a/daemons/node/stream/handler.go +++ b/daemons/node/stream/handler.go @@ -8,6 +8,7 @@ import ( "oc-discovery/daemons/node/common" oclib "cloud.o-forge.io/core/oc-lib" + "cloud.o-forge.io/core/oc-lib/models/booking/planner" "cloud.o-forge.io/core/oc-lib/models/peer" "cloud.o-forge.io/core/oc-lib/models/resources" "cloud.o-forge.io/core/oc-lib/tools" @@ -30,11 +31,31 @@ func (ps *StreamService) handleEvent(protocol string, evt *common.Event) error { return err } }*/ + if protocol == ProtocolSendPlanner { + if err := ps.sendPlanner(evt); err != nil { + return err + } + } if protocol == ProtocolSearchResource && evt.DataType > -1 { if err := ps.retrieveResponse(evt); err != nil { return err } } + if protocol == ProtocolConsidersResource && evt.DataType > -1 { + if err := ps.pass(evt, tools.PB_CONSIDERS); err != nil { + return err + } + } + if protocol == ProtocolAdmiraltyConfigResource { + if err := ps.pass(evt, tools.PB_ADMIRALTY_CONFIG); err != nil { + return err + } + } + if protocol == ProtocolMinioConfigResource { + if err := ps.pass(evt, tools.PB_MINIO_CONFIG); err != nil { + return err + } + } return errors.New("no action authorized available : " + protocol) } @@ -56,7 +77,41 @@ func (abs *StreamService) verifyResponse(event *common.Event) error { // } } if b, err := json.Marshal(verify); err == nil { - abs.PublishVerifyResources(nil, "", event.From, b) + abs.PublishCommon(nil, "", event.From, ProtocolVerifyResource, b) + } + return nil +} + +func (abs *StreamService) sendPlanner(event *common.Event) error { // + if len(event.Payload) == 0 { + if plan, err := planner.GenerateShallow(&tools.APIRequest{Admin: true}); err == nil { + if b, err := json.Marshal(plan); err == nil { + abs.PublishCommon(nil, event.User, event.From, ProtocolSendPlanner, b) + } else { + return err + } + } else { + m := map[string]interface{}{} + if err := json.Unmarshal(event.Payload, &m); err == nil { + m["peer_id"] = event.From + if pl, err := json.Marshal(m); err == nil { + if b, err := json.Marshal(tools.PropalgationMessage{ + DataType: -1, + Action: tools.PB_PLANNER, + Payload: pl, + }); err == nil { + go tools.NewNATSCaller().SetNATSPub(tools.PROPALGATION_EVENT, tools.NATSResponse{ + FromApp: "oc-discovery", + Datatype: tools.DataType(oclib.BOOKING), + Method: int(tools.PROPALGATION_EVENT), + Payload: b, + }) + } + } + } + } + } else { + } return nil } @@ -67,15 +122,31 @@ func (abs *StreamService) retrieveResponse(event *common.Event) error { // return nil } b, err := json.Marshal(res.Serialize(res)) - go tools.NewNATSCaller().SetNATSPub(tools.CATALOG_SEARCH_EVENT, tools.NATSResponse{ + go tools.NewNATSCaller().SetNATSPub(tools.SEARCH_EVENT, tools.NATSResponse{ FromApp: "oc-discovery", Datatype: tools.DataType(event.DataType), - Method: int(tools.CATALOG_SEARCH_EVENT), + Method: int(tools.SEARCH_EVENT), Payload: b, }) return nil } +func (abs *StreamService) pass(event *common.Event, action tools.PubSubAction) error { // + if b, err := json.Marshal(&tools.PropalgationMessage{ + Action: action, + DataType: int(event.DataType), + Payload: event.Payload, + }); err == nil { + go tools.NewNATSCaller().SetNATSPub(tools.PROPALGATION_EVENT, tools.NATSResponse{ + FromApp: "oc-discovery", + Datatype: tools.DataType(event.DataType), + Method: int(tools.PROPALGATION_EVENT), + Payload: b, + }) + } + return nil +} + func (ps *StreamService) handleEventFromPartner(evt *common.Event, protocol string) error { resource, err := resources.ToResource(int(evt.DataType), evt.Payload) if err != nil { diff --git a/daemons/node/stream/publish.go b/daemons/node/stream/publish.go index 6bf6965..8643721 100644 --- a/daemons/node/stream/publish.go +++ b/daemons/node/stream/publish.go @@ -15,7 +15,7 @@ import ( "github.com/libp2p/go-libp2p/core/protocol" ) -func (ps *StreamService) PublishVerifyResources(dt *tools.DataType, user string, toPeerID string, resource []byte) (*common.Stream, error) { +func (ps *StreamService) PublishCommon(dt *tools.DataType, user string, toPeerID string, proto protocol.ID, resource []byte) (*common.Stream, error) { access := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), nil) p := access.LoadOne(toPeerID) if p.Err != "" { @@ -25,7 +25,7 @@ func (ps *StreamService) PublishVerifyResources(dt *tools.DataType, user string, if err != nil { return nil, err } - return ps.write(toPeerID, ad, dt, user, resource, ProtocolVerifyResource) + return ps.write(toPeerID, ad, dt, user, resource, proto) } } @@ -144,8 +144,15 @@ func (s *StreamService) write( proto protocol.ID) (*common.Stream, error) { logger := oclib.GetLogger() var err error + pts := map[protocol.ID]*common.ProtocolInfo{} + for k, v := range protocols { + pts[k] = v + } + for k, v := range protocolsPartners { + pts[k] = v + } // should create a very temp stream - if s.Streams, err = common.TempStream(s.Host, *peerID, proto, did, s.Streams, &s.Mu); err != nil { + if s.Streams, err = common.TempStream(s.Host, *peerID, proto, did, s.Streams, pts, &s.Mu); err != nil { return nil, errors.New("no stream available for protocol " + fmt.Sprintf("%v", proto) + " from PID " + peerID.ID.String()) } diff --git a/daemons/node/stream/service.go b/daemons/node/stream/service.go index e723aaf..53378f4 100644 --- a/daemons/node/stream/service.go +++ b/daemons/node/stream/service.go @@ -22,28 +22,32 @@ import ( ma "github.com/multiformats/go-multiaddr" ) +const ProtocolConsidersResource = "/opencloud/resource/considers/1.0" +const ProtocolMinioConfigResource = "/opencloud/minio/config/1.0" +const ProtocolAdmiraltyConfigResource = "/opencloud/admiralty/config/1.0" + const ProtocolSearchResource = "/opencloud/resource/search/1.0" const ProtocolCreateResource = "/opencloud/resource/create/1.0" const ProtocolUpdateResource = "/opencloud/resource/update/1.0" const ProtocolDeleteResource = "/opencloud/resource/delete/1.0" +const ProtocolSendPlanner = "/opencloud/resource/planner/1.0" const ProtocolVerifyResource = "/opencloud/resource/verify/1.0" const ProtocolHeartbeatPartner = "/opencloud/resource/heartbeat/partner/1.0" -type ProtocolInfo struct { - PersistantStream bool - WaitResponse bool +var protocols = map[protocol.ID]*common.ProtocolInfo{ + ProtocolConsidersResource: {WaitResponse: false, TTL: 3 * time.Second}, + ProtocolSendPlanner: {WaitResponse: true, TTL: 24 * time.Hour}, + ProtocolSearchResource: {WaitResponse: true, TTL: 1 * time.Minute}, + ProtocolVerifyResource: {WaitResponse: true, TTL: 1 * time.Minute}, + ProtocolMinioConfigResource: {WaitResponse: true, TTL: 1 * time.Minute}, + ProtocolAdmiraltyConfigResource: {WaitResponse: true, TTL: 1 * time.Minute}, } -var protocols = map[protocol.ID]*ProtocolInfo{ - ProtocolSearchResource: {WaitResponse: true}, - ProtocolVerifyResource: {WaitResponse: true}, -} - -var protocolsPartners = map[protocol.ID]*ProtocolInfo{ - ProtocolCreateResource: {}, - ProtocolUpdateResource: {}, - ProtocolDeleteResource: {}, +var protocolsPartners = map[protocol.ID]*common.ProtocolInfo{ + ProtocolCreateResource: {TTL: 3 * time.Second}, + ProtocolUpdateResource: {TTL: 3 * time.Second}, + ProtocolDeleteResource: {TTL: 3 * time.Second}, } type StreamService struct { @@ -82,9 +86,17 @@ func (s *StreamService) HandleResponse(stream network.Stream) { if s.Streams[stream.Protocol()] == nil { s.Streams[stream.Protocol()] = map[pp.ID]*common.Stream{} } + expiry := 1 * time.Minute + + if protocols[stream.Protocol()] != nil { + expiry = protocols[stream.Protocol()].TTL + } else if protocolsPartners[stream.Protocol()] != nil { + expiry = protocolsPartners[stream.Protocol()].TTL + } + s.Streams[stream.Protocol()][stream.Conn().RemotePeer()] = &common.Stream{ Stream: stream, - Expiry: time.Now().UTC().Add(1 * time.Minute), + Expiry: time.Now().UTC().Add(expiry + 1*time.Minute), } s.Mu.Unlock() @@ -221,7 +233,7 @@ func (s *StreamService) gc() { } } -func (ps *StreamService) readLoop(s *common.Stream, id pp.ID, proto protocol.ID, protocolInfo *ProtocolInfo) { +func (ps *StreamService) readLoop(s *common.Stream, id pp.ID, proto protocol.ID, protocolInfo *common.ProtocolInfo) { defer s.Stream.Close() defer func() { ps.Mu.Lock() diff --git a/docs/diagrams/01_node_init.mmd b/docs/diagrams/01_node_init.mmd new file mode 100644 index 0000000..fe917b0 --- /dev/null +++ b/docs/diagrams/01_node_init.mmd @@ -0,0 +1,56 @@ +sequenceDiagram + title Node Initialization — Pair A (InitNode) + + participant MainA as main (Pair A) + participant NodeA as Node A + participant libp2pA as libp2p (Pair A) + participant DBA as DB Pair A (oc-lib) + participant NATSA as NATS A + participant IndexerA as Indexer (partagé) + participant StreamA as StreamService A + participant PubSubA as PubSubService A + + MainA->>NodeA: InitNode(isNode, isIndexer, isNativeIndexer) + + NodeA->>NodeA: LoadKeyFromFilePrivate() → priv + NodeA->>NodeA: LoadPSKFromFile() → psk + + NodeA->>libp2pA: New(PrivateNetwork(psk), Identity(priv), ListenAddr:4001) + libp2pA-->>NodeA: host A (PeerID_A) + + Note over NodeA: isNode == true + + NodeA->>libp2pA: NewGossipSub(ctx, host) + libp2pA-->>NodeA: ps (GossipSub) + + NodeA->>IndexerA: ConnectToIndexers → SendHeartbeat /opencloud/heartbeat/1.0 + Note over IndexerA: Heartbeat long-lived établi
Score qualité calculé (bw + uptime + diversité) + IndexerA-->>NodeA: OK + + NodeA->>NodeA: claimInfo(name, hostname) + NodeA->>IndexerA: TempStream /opencloud/record/publish/1.0 + NodeA->>IndexerA: json.Encode(PeerRecord A signé) + IndexerA->>IndexerA: DHT.PutValue("/node/"+DID_A, record) + + NodeA->>DBA: NewRequestAdmin(PEER).Search(SELF) + DBA-->>NodeA: peer A local (ou UUID généré) + + NodeA->>NodeA: StartGC(30s) — GC sur StreamRecords + + NodeA->>StreamA: InitStream(ctx, host, PeerID_A, 1000, nodeA) + StreamA->>StreamA: SetStreamHandler(heartbeat/partner, search, planner, ...) + StreamA->>DBA: Search(PEER, PARTNER) → liste partenaires + DBA-->>StreamA: [] (aucun partenaire au démarrage) + StreamA-->>NodeA: StreamService A + + NodeA->>PubSubA: InitPubSub(ctx, host, ps, nodeA, streamA) + PubSubA->>PubSubA: subscribeEvents(PB_SEARCH, timeout=-1) + PubSubA-->>NodeA: PubSubService A + + NodeA->>NodeA: SubscribeToSearch(ps, callback) + Note over NodeA: callback: GetPeerRecord(evt.From)
→ StreamService.SendResponse + + NodeA->>NATSA: ListenNATS(nodeA) + Note over NATSA: Enregistre handlers:
CREATE_RESOURCE, PROPALGATION_EVENT + + NodeA-->>MainA: *Node A prêt diff --git a/docs/diagrams/01_node_init.puml b/docs/diagrams/01_node_init.puml new file mode 100644 index 0000000..2653851 --- /dev/null +++ b/docs/diagrams/01_node_init.puml @@ -0,0 +1,58 @@ +@startuml +title Node Initialization — Pair A (InitNode) + +participant "main (Pair A)" as MainA +participant "Node A" as NodeA +participant "libp2p (Pair A)" as libp2pA +participant "DB Pair A (oc-lib)" as DBA +participant "NATS A" as NATSA +participant "Indexer (partagé)" as IndexerA +participant "StreamService A" as StreamA +participant "PubSubService A" as PubSubA + +MainA -> NodeA: InitNode(isNode, isIndexer, isNativeIndexer) + +NodeA -> NodeA: LoadKeyFromFilePrivate() → priv +NodeA -> NodeA: LoadPSKFromFile() → psk + +NodeA -> libp2pA: New(PrivateNetwork(psk), Identity(priv), ListenAddr:4001) +libp2pA --> NodeA: host A (PeerID_A) + +note over NodeA: isNode == true + +NodeA -> libp2pA: NewGossipSub(ctx, host) +libp2pA --> NodeA: ps (GossipSub) + +NodeA -> IndexerA: ConnectToIndexers → SendHeartbeat /opencloud/heartbeat/1.0 +note over IndexerA: Heartbeat long-lived établi\nScore qualité calculé (bw + uptime + diversité) +IndexerA --> NodeA: OK + +NodeA -> NodeA: claimInfo(name, hostname) +NodeA -> IndexerA: TempStream /opencloud/record/publish/1.0 +NodeA -> IndexerA: json.Encode(PeerRecord A signé) +IndexerA -> IndexerA: DHT.PutValue("/node/"+DID_A, record) + +NodeA -> DBA: NewRequestAdmin(PEER).Search(SELF) +DBA --> NodeA: peer A local (ou UUID généré) + +NodeA -> NodeA: StartGC(30s) — GC sur StreamRecords + +NodeA -> StreamA: InitStream(ctx, host, PeerID_A, 1000, nodeA) +StreamA -> StreamA: SetStreamHandler(heartbeat/partner, search, planner, ...) +StreamA -> DBA: Search(PEER, PARTNER) → liste partenaires +DBA --> StreamA: [] (aucun partenaire au démarrage) +StreamA --> NodeA: StreamService A + +NodeA -> PubSubA: InitPubSub(ctx, host, ps, nodeA, streamA) +PubSubA -> PubSubA: subscribeEvents(PB_SEARCH, timeout=-1) +PubSubA --> NodeA: PubSubService A + +NodeA -> NodeA: SubscribeToSearch(ps, callback) +note over NodeA: callback: GetPeerRecord(evt.From)\n→ StreamService.SendResponse + +NodeA -> NATSA: ListenNATS(nodeA) +note over NATSA: Enregistre handlers:\nCREATE_RESOURCE, PROPALGATION_EVENT + +NodeA --> MainA: *Node A prêt + +@enduml diff --git a/docs/diagrams/02_node_claim.mmd b/docs/diagrams/02_node_claim.mmd new file mode 100644 index 0000000..10ad27c --- /dev/null +++ b/docs/diagrams/02_node_claim.mmd @@ -0,0 +1,38 @@ +sequenceDiagram + title Node Claim — Pair A publie son PeerRecord (claimInfo + publishPeerRecord) + + participant DBA as DB Pair A (oc-lib) + participant NodeA as Node A + participant IndexerA as Indexer (partagé) + participant DHT as DHT Kademlia + participant NATSA as NATS A + + NodeA->>DBA: NewRequestAdmin(PEER).Search(SELF) + DBA-->>NodeA: existing peer (DID_A) ou nouveau UUID + + NodeA->>NodeA: LoadKeyFromFilePrivate() → priv A + NodeA->>NodeA: LoadKeyFromFilePublic() → pub A + NodeA->>NodeA: crypto.MarshalPublicKey(pub A) → pubBytes + + NodeA->>NodeA: Build PeerRecord A {
Name, DID, PubKey,
PeerID: PeerID_A,
APIUrl: hostname,
StreamAddress: /ip4/.../tcp/4001/p2p/PeerID_A,
NATSAddress, WalletAddress
} + + NodeA->>NodeA: sha256(json(rec)) → hash + NodeA->>NodeA: priv.Sign(hash) → signature + NodeA->>NodeA: rec.ExpiryDate = now + 150s + + loop Pour chaque StaticIndexer (Indexer A, B, …) + NodeA->>IndexerA: TempStream /opencloud/record/publish/1.0 + NodeA->>IndexerA: json.Encode(PeerRecord A signé) + + IndexerA->>IndexerA: Verify signature + IndexerA->>IndexerA: Check heartbeat stream actif pour PeerID_A + IndexerA->>DHT: PutValue("/node/"+DID_A, PeerRecord A) + DHT-->>IndexerA: ok + end + + NodeA->>NodeA: rec.ExtractPeer(DID_A, DID_A, pub A) + NodeA->>NATSA: SetNATSPub(CREATE_RESOURCE, {PEER, Peer A JSON}) + NATSA->>DBA: Upsert Peer A (SearchAttr: peer_id) + DBA-->>NATSA: ok + + NodeA-->>NodeA: *peer.Peer A (SELF) diff --git a/docs/diagrams/02_node_claim.puml b/docs/diagrams/02_node_claim.puml new file mode 100644 index 0000000..cfed09d --- /dev/null +++ b/docs/diagrams/02_node_claim.puml @@ -0,0 +1,40 @@ +@startuml +title Node Claim — Pair A publie son PeerRecord (claimInfo + publishPeerRecord) + +participant "DB Pair A (oc-lib)" as DBA +participant "Node A" as NodeA +participant "Indexer (partagé)" as IndexerA +participant "DHT Kademlia" as DHT +participant "NATS A" as NATSA + +NodeA -> DBA: NewRequestAdmin(PEER).Search(SELF) +DBA --> NodeA: existing peer (DID_A) ou nouveau UUID + +NodeA -> NodeA: LoadKeyFromFilePrivate() → priv A +NodeA -> NodeA: LoadKeyFromFilePublic() → pub A +NodeA -> NodeA: crypto.MarshalPublicKey(pub A) → pubBytes + +NodeA -> NodeA: Build PeerRecord A {\n Name, DID, PubKey,\n PeerID: PeerID_A,\n APIUrl: hostname,\n StreamAddress: /ip4/.../tcp/4001/p2p/PeerID_A,\n NATSAddress, WalletAddress\n} + +NodeA -> NodeA: sha256(json(rec)) → hash +NodeA -> NodeA: priv.Sign(hash) → signature +NodeA -> NodeA: rec.ExpiryDate = now + 150s + +loop Pour chaque StaticIndexer (Indexer A, B, ...) + NodeA -> IndexerA: TempStream /opencloud/record/publish/1.0 + NodeA -> IndexerA: json.Encode(PeerRecord A signé) + + IndexerA -> IndexerA: Verify signature + IndexerA -> IndexerA: Check heartbeat stream actif pour PeerID_A + IndexerA -> DHT: PutValue("/node/"+DID_A, PeerRecord A) + DHT --> IndexerA: ok +end + +NodeA -> NodeA: rec.ExtractPeer(DID_A, DID_A, pub A) +NodeA -> NATSA: SetNATSPub(CREATE_RESOURCE, {PEER, Peer A JSON}) +NATSA -> DBA: Upsert Peer A (SearchAttr: peer_id) +DBA --> NATSA: ok + +NodeA --> NodeA: *peer.Peer A (SELF) + +@enduml diff --git a/docs/diagrams/03_indexer_heartbeat.mmd b/docs/diagrams/03_indexer_heartbeat.mmd new file mode 100644 index 0000000..b035280 --- /dev/null +++ b/docs/diagrams/03_indexer_heartbeat.mmd @@ -0,0 +1,47 @@ +sequenceDiagram + title Indexer — Heartbeat double (Pair A + Pair B → Indexer partagé) + + participant NodeA as Node A + participant NodeB as Node B + participant Indexer as IndexerService (partagé) + + Note over NodeA,NodeB: Chaque pair tick toutes les 20s + + par Pair A heartbeat + NodeA->>Indexer: NewStream /opencloud/heartbeat/1.0 + NodeA->>Indexer: json.Encode(Heartbeat A {Name, DID_A, PeerID_A, IndexersBinded}) + + Indexer->>Indexer: CheckHeartbeat(host, stream, streams, mu, maxNodes) + Note over Indexer: len(peers) < maxNodes ? + + Indexer->>Indexer: getBandwidthChallenge(512–2048 bytes, stream) + Indexer->>NodeA: Write(random payload) + NodeA->>Indexer: Echo(same payload) + Indexer->>Indexer: Mesure round-trip → Mbps A + + Indexer->>Indexer: getDiversityRate(host, IndexersBinded_A) + Note over Indexer: /24 subnet diversity des indexeurs liés + + Indexer->>Indexer: ComputeIndexerScore(uptimeA%, MbpsA%, diversityA%) + Note over Indexer: Score = 0.4×uptime + 0.4×bpms + 0.2×diversity + + alt Score A < 75 + Indexer->>NodeA: (close stream) + else Score A ≥ 75 + Indexer->>Indexer: StreamRecord[PeerID_A] = {DID_A, Heartbeat, UptimeTracker} + end + and Pair B heartbeat + NodeB->>Indexer: NewStream /opencloud/heartbeat/1.0 + NodeB->>Indexer: json.Encode(Heartbeat B {Name, DID_B, PeerID_B, IndexersBinded}) + + Indexer->>Indexer: CheckHeartbeat → getBandwidthChallenge + Indexer->>NodeB: Write(random payload) + NodeB->>Indexer: Echo(same payload) + Indexer->>Indexer: ComputeIndexerScore(uptimeB%, MbpsB%, diversityB%) + + alt Score B ≥ 75 + Indexer->>Indexer: StreamRecord[PeerID_B] = {DID_B, Heartbeat, UptimeTracker} + end + end + + Note over Indexer: Les deux pairs sont désormais
enregistrés avec leurs streams actifs diff --git a/docs/diagrams/03_indexer_heartbeat.puml b/docs/diagrams/03_indexer_heartbeat.puml new file mode 100644 index 0000000..2ecc458 --- /dev/null +++ b/docs/diagrams/03_indexer_heartbeat.puml @@ -0,0 +1,49 @@ +@startuml +title Indexer — Heartbeat double (Pair A + Pair B → Indexer partagé) + +participant "Node A" as NodeA +participant "Node B" as NodeB +participant "IndexerService (partagé)" as Indexer + +note over NodeA,NodeB: Chaque pair tick toutes les 20s + +par Pair A heartbeat + NodeA -> Indexer: NewStream /opencloud/heartbeat/1.0 + NodeA -> Indexer: json.Encode(Heartbeat A {Name, DID_A, PeerID_A, IndexersBinded}) + + Indexer -> Indexer: CheckHeartbeat(host, stream, streams, mu, maxNodes) + note over Indexer: len(peers) < maxNodes ? + + Indexer -> Indexer: getBandwidthChallenge(512-2048 bytes, stream) + Indexer -> NodeA: Write(random payload) + NodeA -> Indexer: Echo(same payload) + Indexer -> Indexer: Mesure round-trip → Mbps A + + Indexer -> Indexer: getDiversityRate(host, IndexersBinded_A) + note over Indexer: /24 subnet diversity des indexeurs liés + + Indexer -> Indexer: ComputeIndexerScore(uptimeA%, MbpsA%, diversityA%) + note over Indexer: Score = 0.4×uptime + 0.4×bpms + 0.2×diversity + + alt Score A < 75 + Indexer -> NodeA: (close stream) + else Score A >= 75 + Indexer -> Indexer: StreamRecord[PeerID_A] = {DID_A, Heartbeat, UptimeTracker} + end +else Pair B heartbeat + NodeB -> Indexer: NewStream /opencloud/heartbeat/1.0 + NodeB -> Indexer: json.Encode(Heartbeat B {Name, DID_B, PeerID_B, IndexersBinded}) + + Indexer -> Indexer: CheckHeartbeat → getBandwidthChallenge + Indexer -> NodeB: Write(random payload) + NodeB -> Indexer: Echo(same payload) + Indexer -> Indexer: ComputeIndexerScore(uptimeB%, MbpsB%, diversityB%) + + alt Score B >= 75 + Indexer -> Indexer: StreamRecord[PeerID_B] = {DID_B, Heartbeat, UptimeTracker} + end +end par + +note over Indexer: Les deux pairs sont désormais\nenregistrés avec leurs streams actifs + +@enduml diff --git a/docs/diagrams/04_indexer_publish.mmd b/docs/diagrams/04_indexer_publish.mmd new file mode 100644 index 0000000..e708a24 --- /dev/null +++ b/docs/diagrams/04_indexer_publish.mmd @@ -0,0 +1,41 @@ +sequenceDiagram + title Indexer — Pair A publie, Pair B publie (handleNodePublish → DHT) + + participant NodeA as Node A + participant NodeB as Node B + participant Indexer as IndexerService (partagé) + participant DHT as DHT Kademlia + + Note over NodeA: Après claimInfo ou refresh TTL + + par Pair A publie son PeerRecord + NodeA->>Indexer: TempStream /opencloud/record/publish/1.0 + NodeA->>Indexer: json.Encode(PeerRecord A {DID_A, PeerID_A, PubKey_A, Expiry, Sig_A}) + + Indexer->>Indexer: Verify sig_A (reconstruit rec minimal, pubKey_A.Verify) + Indexer->>Indexer: Check StreamRecords[Heartbeat][PeerID_A] existe + + alt Heartbeat actif pour A + Indexer->>Indexer: StreamRecord A → DID_A, Record=PeerRecord A, LastSeen=now + Indexer->>DHT: PutValue("/node/"+DID_A, PeerRecord A JSON) + DHT-->>Indexer: ok + else Pas de heartbeat + Indexer->>NodeA: (erreur "no heartbeat", stream close) + end + and Pair B publie son PeerRecord + NodeB->>Indexer: TempStream /opencloud/record/publish/1.0 + NodeB->>Indexer: json.Encode(PeerRecord B {DID_B, PeerID_B, PubKey_B, Expiry, Sig_B}) + + Indexer->>Indexer: Verify sig_B + Indexer->>Indexer: Check StreamRecords[Heartbeat][PeerID_B] existe + + alt Heartbeat actif pour B + Indexer->>Indexer: StreamRecord B → DID_B, Record=PeerRecord B, LastSeen=now + Indexer->>DHT: PutValue("/node/"+DID_B, PeerRecord B JSON) + DHT-->>Indexer: ok + else Pas de heartbeat + Indexer->>NodeB: (erreur "no heartbeat", stream close) + end + end + + Note over DHT: DHT contient maintenant
"/node/DID_A" et "/node/DID_B" diff --git a/docs/diagrams/04_indexer_publish.puml b/docs/diagrams/04_indexer_publish.puml new file mode 100644 index 0000000..a0aeecc --- /dev/null +++ b/docs/diagrams/04_indexer_publish.puml @@ -0,0 +1,43 @@ +@startuml +title Indexer — Pair A publie, Pair B publie (handleNodePublish → DHT) + +participant "Node A" as NodeA +participant "Node B" as NodeB +participant "IndexerService (partagé)" as Indexer +participant "DHT Kademlia" as DHT + +note over NodeA: Après claimInfo ou refresh TTL + +par Pair A publie son PeerRecord + NodeA -> Indexer: TempStream /opencloud/record/publish/1.0 + NodeA -> Indexer: json.Encode(PeerRecord A {DID_A, PeerID_A, PubKey_A, Expiry, Sig_A}) + + Indexer -> Indexer: Verify sig_A (reconstruit rec minimal, pubKey_A.Verify) + Indexer -> Indexer: Check StreamRecords[Heartbeat][PeerID_A] existe + + alt Heartbeat actif pour A + Indexer -> Indexer: StreamRecord A → DID_A, Record=PeerRecord A, LastSeen=now + Indexer -> DHT: PutValue("/node/"+DID_A, PeerRecord A JSON) + DHT --> Indexer: ok + else Pas de heartbeat + Indexer -> NodeA: (erreur "no heartbeat", stream close) + end +else Pair B publie son PeerRecord + NodeB -> Indexer: TempStream /opencloud/record/publish/1.0 + NodeB -> Indexer: json.Encode(PeerRecord B {DID_B, PeerID_B, PubKey_B, Expiry, Sig_B}) + + Indexer -> Indexer: Verify sig_B + Indexer -> Indexer: Check StreamRecords[Heartbeat][PeerID_B] existe + + alt Heartbeat actif pour B + Indexer -> Indexer: StreamRecord B → DID_B, Record=PeerRecord B, LastSeen=now + Indexer -> DHT: PutValue("/node/"+DID_B, PeerRecord B JSON) + DHT --> Indexer: ok + else Pas de heartbeat + Indexer -> NodeB: (erreur "no heartbeat", stream close) + end +end par + +note over DHT: DHT contient maintenant\n"/node/DID_A" et "/node/DID_B" + +@enduml diff --git a/docs/diagrams/05_indexer_get.mmd b/docs/diagrams/05_indexer_get.mmd new file mode 100644 index 0000000..2714294 --- /dev/null +++ b/docs/diagrams/05_indexer_get.mmd @@ -0,0 +1,49 @@ +sequenceDiagram + title Indexer — Pair A résout Pair B (GetPeerRecord + handleNodeGet) + + participant NATSA as NATS A + participant DBA as DB Pair A (oc-lib) + participant NodeA as Node A + participant Indexer as IndexerService (partagé) + participant DHT as DHT Kademlia + participant NATSA2 as NATS A (retour) + + Note over NodeA: Déclenché par : NATS PB_SEARCH PEER
ou callback SubscribeToSearch + + NodeA->>DBA: NewRequestAdmin(PEER).Search(DID_B ou PeerID_B) + DBA-->>NodeA: Peer B local (si connu) → résout DID_B + PeerID_B
sinon utilise la valeur brute + + loop Pour chaque StaticIndexer + NodeA->>Indexer: TempStream /opencloud/record/get/1.0 + NodeA->>Indexer: json.Encode(GetValue{Key: DID_B, PeerID: PeerID_B}) + + Indexer->>Indexer: key = "/node/" + DID_B + Indexer->>DHT: SearchValue(ctx 10s, "/node/"+DID_B) + DHT-->>Indexer: channel de bytes (PeerRecord B) + + loop Pour chaque résultat DHT + Indexer->>Indexer: Unmarshal → PeerRecord B + alt PeerRecord.PeerID == PeerID_B + Indexer->>Indexer: resp.Found=true, resp.Records[PeerID_B]=PeerRecord B + Indexer->>Indexer: StreamRecord B.LastSeen = now (si heartbeat actif) + end + end + + Indexer->>NodeA: json.Encode(GetResponse{Found:true, Records:{PeerID_B: PeerRecord B}}) + end + + loop Pour chaque PeerRecord retourné + NodeA->>NodeA: rec.Verify() → valide signature de B + NodeA->>NodeA: rec.ExtractPeer(ourDID_A, DID_B, pubKey_B) + + alt ourDID_A == DID_B (c'est notre propre entrée) + Note over NodeA: Republier pour rafraîchir le TTL + NodeA->>Indexer: publishPeerRecord(rec) [refresh 2 min] + end + + NodeA->>NATSA2: SetNATSPub(CREATE_RESOURCE, {PEER, Peer B JSON,
SearchAttr:"peer_id"}) + NATSA2->>DBA: Upsert Peer B dans DB A + DBA-->>NATSA2: ok + end + + NodeA-->>NodeA: []*peer.Peer → [Peer B] diff --git a/docs/diagrams/05_indexer_get.puml b/docs/diagrams/05_indexer_get.puml new file mode 100644 index 0000000..f17f9c1 --- /dev/null +++ b/docs/diagrams/05_indexer_get.puml @@ -0,0 +1,51 @@ +@startuml +title Indexer — Pair A résout Pair B (GetPeerRecord + handleNodeGet) + +participant "NATS A" as NATSA +participant "DB Pair A (oc-lib)" as DBA +participant "Node A" as NodeA +participant "IndexerService (partagé)" as Indexer +participant "DHT Kademlia" as DHT +participant "NATS A (retour)" as NATSA2 + +note over NodeA: Déclenché par : NATS PB_SEARCH PEER\nou callback SubscribeToSearch + +NodeA -> DBA: NewRequestAdmin(PEER).Search(DID_B ou PeerID_B) +DBA --> NodeA: Peer B local (si connu) → résout DID_B + PeerID_B\nsinon utilise la valeur brute + +loop Pour chaque StaticIndexer + NodeA -> Indexer: TempStream /opencloud/record/get/1.0 + NodeA -> Indexer: json.Encode(GetValue{Key: DID_B, PeerID: PeerID_B}) + + Indexer -> Indexer: key = "/node/" + DID_B + Indexer -> DHT: SearchValue(ctx 10s, "/node/"+DID_B) + DHT --> Indexer: channel de bytes (PeerRecord B) + + loop Pour chaque résultat DHT + Indexer -> Indexer: Unmarshal → PeerRecord B + alt PeerRecord.PeerID == PeerID_B + Indexer -> Indexer: resp.Found=true, resp.Records[PeerID_B]=PeerRecord B + Indexer -> Indexer: StreamRecord B.LastSeen = now (si heartbeat actif) + end + end + + Indexer -> NodeA: json.Encode(GetResponse{Found:true, Records:{PeerID_B: PeerRecord B}}) +end + +loop Pour chaque PeerRecord retourné + NodeA -> NodeA: rec.Verify() → valide signature de B + NodeA -> NodeA: rec.ExtractPeer(ourDID_A, DID_B, pubKey_B) + + alt ourDID_A == DID_B (c'est notre propre entrée) + note over NodeA: Republier pour rafraîchir le TTL + NodeA -> Indexer: publishPeerRecord(rec) [refresh 2 min] + end + + NodeA -> NATSA2: SetNATSPub(CREATE_RESOURCE, {PEER, Peer B JSON,\nSearchAttr:"peer_id"}) + NATSA2 -> DBA: Upsert Peer B dans DB A + DBA --> NATSA2: ok +end + +NodeA --> NodeA: []*peer.Peer → [Peer B] + +@enduml diff --git a/docs/diagrams/06_native_registration.mmd b/docs/diagrams/06_native_registration.mmd new file mode 100644 index 0000000..b1a5114 --- /dev/null +++ b/docs/diagrams/06_native_registration.mmd @@ -0,0 +1,39 @@ +sequenceDiagram + title Native Indexer — Enregistrement d'un Indexer auprès du Native + + participant IndexerA as Indexer A + participant IndexerB as Indexer B + participant Native as Native Indexer (partagé) + participant DHT as DHT Kademlia + participant PubSub as GossipSub (oc-indexer-registry) + + Note over IndexerA,IndexerB: Au démarrage + toutes les 60s (StartNativeRegistration) + + par Indexer A s'enregistre + IndexerA->>IndexerA: Build IndexerRegistration{PeerID_A, Addr_A} + IndexerA->>Native: NewStream /opencloud/native/subscribe/1.0 + IndexerA->>Native: json.Encode(IndexerRegistration A) + + Native->>Native: Decode → liveIndexerEntry{PeerID_A, Addr_A, ExpiresAt=now+66s} + Native->>DHT: PutValue("/indexer/"+PeerID_A, entry A) + DHT-->>Native: ok + Native->>Native: liveIndexers[PeerID_A] = entry A + Native->>Native: knownPeerIDs[PeerID_A] = {} + + Native->>PubSub: topic.Publish([]byte(PeerID_A)) + Note over PubSub: Gossipé aux autres Natives
→ ils ajoutent PeerID_A à knownPeerIDs
→ refresh DHT au prochain tick 30s + IndexerA->>Native: stream.Close() + and Indexer B s'enregistre + IndexerB->>IndexerB: Build IndexerRegistration{PeerID_B, Addr_B} + IndexerB->>Native: NewStream /opencloud/native/subscribe/1.0 + IndexerB->>Native: json.Encode(IndexerRegistration B) + + Native->>Native: Decode → liveIndexerEntry{PeerID_B, Addr_B, ExpiresAt=now+66s} + Native->>DHT: PutValue("/indexer/"+PeerID_B, entry B) + DHT-->>Native: ok + Native->>Native: liveIndexers[PeerID_B] = entry B + Native->>PubSub: topic.Publish([]byte(PeerID_B)) + IndexerB->>Native: stream.Close() + end + + Note over Native: liveIndexers = {PeerID_A: entryA, PeerID_B: entryB} diff --git a/docs/diagrams/06_native_registration.puml b/docs/diagrams/06_native_registration.puml new file mode 100644 index 0000000..f28399f --- /dev/null +++ b/docs/diagrams/06_native_registration.puml @@ -0,0 +1,41 @@ +@startuml +title Native Indexer — Enregistrement d'un Indexer auprès du Native + +participant "Indexer A" as IndexerA +participant "Indexer B" as IndexerB +participant "Native Indexer (partagé)" as Native +participant "DHT Kademlia" as DHT +participant "GossipSub (oc-indexer-registry)" as PubSub + +note over IndexerA,IndexerB: Au démarrage + toutes les 60s (StartNativeRegistration) + +par Indexer A s'enregistre + IndexerA -> IndexerA: Build IndexerRegistration{PeerID_A, Addr_A} + IndexerA -> Native: NewStream /opencloud/native/subscribe/1.0 + IndexerA -> Native: json.Encode(IndexerRegistration A) + + Native -> Native: Decode → liveIndexerEntry{PeerID_A, Addr_A, ExpiresAt=now+66s} + Native -> DHT: PutValue("/indexer/"+PeerID_A, entry A) + DHT --> Native: ok + Native -> Native: liveIndexers[PeerID_A] = entry A + Native -> Native: knownPeerIDs[PeerID_A] = {} + + Native -> PubSub: topic.Publish([]byte(PeerID_A)) + note over PubSub: Gossipé aux autres Natives\n→ ils ajoutent PeerID_A à knownPeerIDs\n→ refresh DHT au prochain tick 30s + IndexerA -> Native: stream.Close() +else Indexer B s'enregistre + IndexerB -> IndexerB: Build IndexerRegistration{PeerID_B, Addr_B} + IndexerB -> Native: NewStream /opencloud/native/subscribe/1.0 + IndexerB -> Native: json.Encode(IndexerRegistration B) + + Native -> Native: Decode → liveIndexerEntry{PeerID_B, Addr_B, ExpiresAt=now+66s} + Native -> DHT: PutValue("/indexer/"+PeerID_B, entry B) + DHT --> Native: ok + Native -> Native: liveIndexers[PeerID_B] = entry B + Native -> PubSub: topic.Publish([]byte(PeerID_B)) + IndexerB -> Native: stream.Close() +end par + +note over Native: liveIndexers = {PeerID_A: entryA, PeerID_B: entryB} + +@enduml diff --git a/docs/diagrams/07_native_get_consensus.mmd b/docs/diagrams/07_native_get_consensus.mmd new file mode 100644 index 0000000..a04f59f --- /dev/null +++ b/docs/diagrams/07_native_get_consensus.mmd @@ -0,0 +1,60 @@ +sequenceDiagram + title Native — ConnectToNatives + Consensus (Pair A bootstrap) + + participant NodeA as Node A + participant Native1 as Native #1 (primary) + participant Native2 as Native #2 + participant NativeN as Native #N + participant DHT as DHT Kademlia + + Note over NodeA: NativeIndexerAddresses configuré
Appelé pendant InitNode → ConnectToIndexers + + NodeA->>NodeA: Parse NativeIndexerAddresses → StaticNatives + NodeA->>Native1: SendHeartbeat /opencloud/heartbeat/1.0 (20s tick) + NodeA->>Native2: SendHeartbeat /opencloud/heartbeat/1.0 (20s tick) + + %% Étape 1 : récupérer un pool initial + NodeA->>Native1: Connect + NewStream /opencloud/native/indexers/1.0 + NodeA->>Native1: json.Encode(GetIndexersRequest{Count: maxIndexer}) + + Native1->>Native1: reachableLiveIndexers() + Note over Native1: Filtre liveIndexers par TTL
ping chaque candidat (PeerIsAlive) + + alt Aucun indexer connu par Native1 + Native1->>Native1: selfDelegate(NodeA.PeerID, resp) + Note over Native1: IsSelfFallback=true
Indexers=[native1 addr] + Native1->>NodeA: GetIndexersResponse{IsSelfFallback:true, Indexers:[native1]} + NodeA->>NodeA: StaticIndexers[native1] = native1 + Note over NodeA: Pas de consensus — native1 utilisé directement comme indexeur + else Indexers disponibles + Native1->>NodeA: GetIndexersResponse{Indexers:[Addr_IndexerA, Addr_IndexerB, ...]} + + %% Étape 2 : consensus + Note over NodeA: clientSideConsensus(candidates) + + par Requêtes consensus parallèles + NodeA->>Native1: NewStream /opencloud/native/consensus/1.0 + NodeA->>Native1: ConsensusRequest{Candidates:[Addr_A, Addr_B]} + Native1->>Native1: Croiser avec liveIndexers propres + Native1->>NodeA: ConsensusResponse{Trusted:[Addr_A, Addr_B], Suggestions:[]} + and + NodeA->>Native2: NewStream /opencloud/native/consensus/1.0 + NodeA->>Native2: ConsensusRequest{Candidates:[Addr_A, Addr_B]} + Native2->>Native2: Croiser avec liveIndexers propres + Native2->>NodeA: ConsensusResponse{Trusted:[Addr_A], Suggestions:[Addr_C]} + and + NodeA->>NativeN: NewStream /opencloud/native/consensus/1.0 + NodeA->>NativeN: ConsensusRequest{Candidates:[Addr_A, Addr_B]} + NativeN->>NativeN: Croiser avec liveIndexers propres + NativeN->>NodeA: ConsensusResponse{Trusted:[Addr_A, Addr_B], Suggestions:[]} + end + + Note over NodeA: Aggrège les votes (timeout 4s)
Addr_A → 3/3 votes → confirmé ✓
Addr_B → 2/3 votes → confirmé ✓ + + alt confirmed < maxIndexer && suggestions disponibles + Note over NodeA: Round 2 — rechallenge avec suggestions + NodeA->>NodeA: clientSideConsensus(confirmed + sample(suggestions)) + end + + NodeA->>NodeA: StaticIndexers = adresses confirmées à majorité + end diff --git a/docs/diagrams/07_native_get_consensus.puml b/docs/diagrams/07_native_get_consensus.puml new file mode 100644 index 0000000..54618b0 --- /dev/null +++ b/docs/diagrams/07_native_get_consensus.puml @@ -0,0 +1,62 @@ +@startuml +title Native — ConnectToNatives + Consensus (Pair A bootstrap) + +participant "Node A" as NodeA +participant "Native #1 (primary)" as Native1 +participant "Native #2" as Native2 +participant "Native #N" as NativeN +participant "DHT Kademlia" as DHT + +note over NodeA: NativeIndexerAddresses configuré\nAppelé pendant InitNode → ConnectToIndexers + +NodeA -> NodeA: Parse NativeIndexerAddresses → StaticNatives +NodeA -> Native1: SendHeartbeat /opencloud/heartbeat/1.0 (20s tick) +NodeA -> Native2: SendHeartbeat /opencloud/heartbeat/1.0 (20s tick) + +' Étape 1 : récupérer un pool initial +NodeA -> Native1: Connect + NewStream /opencloud/native/indexers/1.0 +NodeA -> Native1: json.Encode(GetIndexersRequest{Count: maxIndexer}) + +Native1 -> Native1: reachableLiveIndexers() +note over Native1: Filtre liveIndexers par TTL\nping chaque candidat (PeerIsAlive) + +alt Aucun indexer connu par Native1 + Native1 -> Native1: selfDelegate(NodeA.PeerID, resp) + note over Native1: IsSelfFallback=true\nIndexers=[native1 addr] + Native1 -> NodeA: GetIndexersResponse{IsSelfFallback:true, Indexers:[native1]} + NodeA -> NodeA: StaticIndexers[native1] = native1 + note over NodeA: Pas de consensus — native1 utilisé directement comme indexeur +else Indexers disponibles + Native1 -> NodeA: GetIndexersResponse{Indexers:[Addr_IndexerA, Addr_IndexerB, ...]} + + ' Étape 2 : consensus + note over NodeA: clientSideConsensus(candidates) + + par Requêtes consensus parallèles + NodeA -> Native1: NewStream /opencloud/native/consensus/1.0 + NodeA -> Native1: ConsensusRequest{Candidates:[Addr_A, Addr_B]} + Native1 -> Native1: Croiser avec liveIndexers propres + Native1 -> NodeA: ConsensusResponse{Trusted:[Addr_A, Addr_B], Suggestions:[]} + else + NodeA -> Native2: NewStream /opencloud/native/consensus/1.0 + NodeA -> Native2: ConsensusRequest{Candidates:[Addr_A, Addr_B]} + Native2 -> Native2: Croiser avec liveIndexers propres + Native2 -> NodeA: ConsensusResponse{Trusted:[Addr_A], Suggestions:[Addr_C]} + else + NodeA -> NativeN: NewStream /opencloud/native/consensus/1.0 + NodeA -> NativeN: ConsensusRequest{Candidates:[Addr_A, Addr_B]} + NativeN -> NativeN: Croiser avec liveIndexers propres + NativeN -> NodeA: ConsensusResponse{Trusted:[Addr_A, Addr_B], Suggestions:[]} + end par + + note over NodeA: Aggrège les votes (timeout 4s)\nAddr_A → 3/3 votes → confirmé ✓\nAddr_B → 2/3 votes → confirmé ✓ + + alt confirmed < maxIndexer && suggestions disponibles + note over NodeA: Round 2 — rechallenge avec suggestions + NodeA -> NodeA: clientSideConsensus(confirmed + sample(suggestions)) + end + + NodeA -> NodeA: StaticIndexers = adresses confirmées à majorité +end + +@enduml diff --git a/docs/diagrams/08_nats_create_resource.mmd b/docs/diagrams/08_nats_create_resource.mmd new file mode 100644 index 0000000..3270ea8 --- /dev/null +++ b/docs/diagrams/08_nats_create_resource.mmd @@ -0,0 +1,49 @@ +sequenceDiagram + title NATS — CREATE_RESOURCE : Pair A découvre Pair B et établit le stream + + participant AppA as App Pair A (oc-api) + participant NATSA as NATS A + participant NodeA as Node A + participant StreamA as StreamService A + participant NodeB as Node B + participant StreamB as StreamService B + participant DBA as DB Pair A (oc-lib) + + Note over AppA: Pair B vient d'être découvert
(via indexeur ou manuel) + + AppA->>NATSA: Publish(CREATE_RESOURCE, {
FromApp:"oc-api",
Datatype:PEER,
Payload: Peer B {StreamAddress_B, Relation:PARTNER}
}) + + NATSA->>NodeA: ListenNATS callback → CREATE_RESOURCE + + NodeA->>NodeA: resp.FromApp == "oc-discovery" ? → Non, continuer + NodeA->>NodeA: json.Unmarshal(payload) → peer.Peer B + NodeA->>NodeA: pp.AddrInfoFromString(B.StreamAddress) + Note over NodeA: ad_B = {ID: PeerID_B, Addrs: [...]} + + NodeA->>StreamA: Mu.Lock() + + alt peer B.Relation == PARTNER + NodeA->>StreamA: ConnectToPartner(B.StreamAddress) + StreamA->>StreamA: AddrInfoFromString(B.StreamAddress) → ad_B + StreamA->>NodeB: Connect (libp2p) + StreamA->>NodeB: NewStream /opencloud/resource/heartbeat/partner/1.0 + StreamA->>NodeB: json.Encode(Heartbeat{Name_A, DID_A, PeerID_A}) + + NodeB->>StreamB: HandlePartnerHeartbeat(stream) + StreamB->>StreamB: CheckHeartbeat → bandwidth challenge + StreamB->>StreamA: Echo(payload) + StreamB->>StreamB: streams[ProtocolHeartbeatPartner][PeerID_A] = {DID_A, Expiry=now+10s} + + StreamA->>StreamA: streams[ProtocolHeartbeatPartner][PeerID_B] = {DID_B, Expiry=now+10s} + Note over StreamA,StreamB: Stream partner long-lived établi
dans les deux sens + + else peer B.Relation != PARTNER (révocation / blacklist) + Note over NodeA: Supprimer tous les streams vers Pair B + loop Pour chaque protocole dans Streams + NodeA->>StreamA: streams[proto][PeerID_B].Stream.Close() + NodeA->>StreamA: delete(streams[proto], PeerID_B) + end + end + + NodeA->>StreamA: Mu.Unlock() + NodeA->>DBA: (pas de write direct ici — géré par l'app source) diff --git a/docs/diagrams/08_nats_create_resource.puml b/docs/diagrams/08_nats_create_resource.puml new file mode 100644 index 0000000..ff5ef25 --- /dev/null +++ b/docs/diagrams/08_nats_create_resource.puml @@ -0,0 +1,50 @@ +@startuml +title NATS — CREATE_RESOURCE : Pair A découvre Pair B et établit le stream + +participant "App Pair A (oc-api)" as AppA +participant "NATS A" as NATSA +participant "Node A" as NodeA +participant "StreamService A" as StreamA +participant "Node B" as NodeB +participant "StreamService B" as StreamB +participant "DB Pair A (oc-lib)" as DBA + +note over AppA: Pair B vient d'être découvert\n(via indexeur ou manuel) + +AppA -> NATSA: Publish(CREATE_RESOURCE, {\n FromApp:"oc-api",\n Datatype:PEER,\n Payload: Peer B {StreamAddress_B, Relation:PARTNER}\n}) + +NATSA -> NodeA: ListenNATS callback → CREATE_RESOURCE + +NodeA -> NodeA: resp.FromApp == "oc-discovery" ? → Non, continuer +NodeA -> NodeA: json.Unmarshal(payload) → peer.Peer B +NodeA -> NodeA: pp.AddrInfoFromString(B.StreamAddress) +note over NodeA: ad_B = {ID: PeerID_B, Addrs: [...]} + +NodeA -> StreamA: Mu.Lock() + +alt peer B.Relation == PARTNER + NodeA -> StreamA: ConnectToPartner(B.StreamAddress) + StreamA -> StreamA: AddrInfoFromString(B.StreamAddress) → ad_B + StreamA -> NodeB: Connect (libp2p) + StreamA -> NodeB: NewStream /opencloud/resource/heartbeat/partner/1.0 + StreamA -> NodeB: json.Encode(Heartbeat{Name_A, DID_A, PeerID_A}) + + NodeB -> StreamB: HandlePartnerHeartbeat(stream) + StreamB -> StreamB: CheckHeartbeat → bandwidth challenge + StreamB -> StreamA: Echo(payload) + StreamB -> StreamB: streams[ProtocolHeartbeatPartner][PeerID_A] = {DID_A, Expiry=now+10s} + + StreamA -> StreamA: streams[ProtocolHeartbeatPartner][PeerID_B] = {DID_B, Expiry=now+10s} + note over StreamA,StreamB: Stream partner long-lived établi\ndans les deux sens +else peer B.Relation != PARTNER (révocation / blacklist) + note over NodeA: Supprimer tous les streams vers Pair B + loop Pour chaque protocole dans Streams + NodeA -> StreamA: streams[proto][PeerID_B].Stream.Close() + NodeA -> StreamA: delete(streams[proto], PeerID_B) + end +end + +NodeA -> StreamA: Mu.Unlock() +NodeA -> DBA: (pas de write direct ici — géré par l'app source) + +@enduml diff --git a/docs/diagrams/09_nats_propagation.mmd b/docs/diagrams/09_nats_propagation.mmd new file mode 100644 index 0000000..8b3bd73 --- /dev/null +++ b/docs/diagrams/09_nats_propagation.mmd @@ -0,0 +1,66 @@ +sequenceDiagram + title NATS — PROPALGATION_EVENT : Pair A propage vers Pair B + + participant AppA as App Pair A + participant NATSA as NATS A + participant NodeA as Node A + participant StreamA as StreamService A + participant NodeB as Node B + participant NATSB as NATS B + participant DBB as DB Pair B (oc-lib) + + AppA->>NATSA: Publish(PROPALGATION_EVENT, {Action, DataType, Payload}) + NATSA->>NodeA: ListenNATS callback → PROPALGATION_EVENT + NodeA->>NodeA: resp.FromApp != "oc-discovery" ? → continuer + NodeA->>NodeA: json.Unmarshal → PropalgationMessage{Action, DataType, Payload} + + alt Action == PB_DELETE + NodeA->>StreamA: ToPartnerPublishEvent(PB_DELETE, dt, user, payload) + StreamA->>StreamA: searchPeer(PARTNER) → [Pair B, ...] + StreamA->>NodeB: write(PeerID_B, addr_B, dt, user, payload, ProtocolDeleteResource) + Note over NodeB: /opencloud/resource/delete/1.0 + + NodeB->>NodeB: handleEventFromPartner(evt, ProtocolDeleteResource) + NodeB->>NATSB: SetNATSPub(REMOVE_RESOURCE, {DataType, resource JSON}) + NATSB->>DBB: Supprimer ressource dans DB B + + else Action == PB_UPDATE (via ProtocolUpdateResource) + NodeA->>StreamA: ToPartnerPublishEvent(PB_UPDATE, dt, user, payload) + StreamA->>NodeB: write → /opencloud/resource/update/1.0 + NodeB->>NATSB: SetNATSPub(CREATE_RESOURCE, {DataType, resource JSON}) + NATSB->>DBB: Upsert ressource dans DB B + + else Action == PB_CONSIDERS + WORKFLOW_EXECUTION + NodeA->>NodeA: Unmarshal → executionConsidersPayload{PeerIDs:[PeerID_B, ...]} + loop Pour chaque peer_id cible + NodeA->>StreamA: PublishCommon(dt, user, PeerID_B, ProtocolConsidersResource, payload) + StreamA->>NodeB: write → /opencloud/resource/considers/1.0 + NodeB->>NodeB: passConsidering(evt) + NodeB->>NATSB: SetNATSPub(PROPALGATION_EVENT, {PB_CONSIDERS, dt, payload}) + NATSB->>DBB: (traité par oc-workflow sur NATS B) + end + + else Action == PB_PLANNER (broadcast) + NodeA->>NodeA: Unmarshal → {peer_id: nil, ...payload} + loop Pour chaque stream ProtocolSendPlanner ouvert + NodeA->>StreamA: PublishCommon(nil, user, pid, ProtocolSendPlanner, payload) + StreamA->>NodeB: write → /opencloud/resource/planner/1.0 + end + + else Action == PB_CLOSE_PLANNER + NodeA->>NodeA: Unmarshal → {peer_id: PeerID_B} + NodeA->>StreamA: Streams[ProtocolSendPlanner][PeerID_B].Stream.Close() + NodeA->>StreamA: delete(Streams[ProtocolSendPlanner], PeerID_B) + + else Action == PB_SEARCH + DataType == PEER + NodeA->>NodeA: Unmarshal → {search: "..."} + NodeA->>NodeA: GetPeerRecord(ctx, search) + Note over NodeA: Résolution via DB A + Indexer + DHT + NodeA->>NATSA: SetNATSPub(SEARCH_EVENT, {PEER, PeerRecord JSON}) + NATSA->>NATSA: (AppA reçoit le résultat) + + else Action == PB_SEARCH + autre DataType + NodeA->>NodeA: Unmarshal → {type:"all"|"known"|"partner", search:"..."} + NodeA->>NodeA: PubSubService.SearchPublishEvent(ctx, dt, type, user, search) + Note over NodeA: Voir diagrammes 10 et 11 + end diff --git a/docs/diagrams/09_nats_propagation.puml b/docs/diagrams/09_nats_propagation.puml new file mode 100644 index 0000000..d5bc2f5 --- /dev/null +++ b/docs/diagrams/09_nats_propagation.puml @@ -0,0 +1,68 @@ +@startuml +title NATS — PROPALGATION_EVENT : Pair A propage vers Pair B + +participant "App Pair A" as AppA +participant "NATS A" as NATSA +participant "Node A" as NodeA +participant "StreamService A" as StreamA +participant "Node B" as NodeB +participant "NATS B" as NATSB +participant "DB Pair B (oc-lib)" as DBB + +AppA -> NATSA: Publish(PROPALGATION_EVENT, {Action, DataType, Payload}) +NATSA -> NodeA: ListenNATS callback → PROPALGATION_EVENT +NodeA -> NodeA: resp.FromApp != "oc-discovery" ? → continuer +NodeA -> NodeA: json.Unmarshal → PropalgationMessage{Action, DataType, Payload} + +alt Action == PB_DELETE + NodeA -> StreamA: ToPartnerPublishEvent(PB_DELETE, dt, user, payload) + StreamA -> StreamA: searchPeer(PARTNER) → [Pair B, ...] + StreamA -> NodeB: write(PeerID_B, addr_B, dt, user, payload, ProtocolDeleteResource) + note over NodeB: /opencloud/resource/delete/1.0 + + NodeB -> NodeB: handleEventFromPartner(evt, ProtocolDeleteResource) + NodeB -> NATSB: SetNATSPub(REMOVE_RESOURCE, {DataType, resource JSON}) + NATSB -> DBB: Supprimer ressource dans DB B + +else Action == PB_UPDATE (via ProtocolUpdateResource) + NodeA -> StreamA: ToPartnerPublishEvent(PB_UPDATE, dt, user, payload) + StreamA -> NodeB: write → /opencloud/resource/update/1.0 + NodeB -> NATSB: SetNATSPub(CREATE_RESOURCE, {DataType, resource JSON}) + NATSB -> DBB: Upsert ressource dans DB B + +else Action == PB_CONSIDERS + WORKFLOW_EXECUTION + NodeA -> NodeA: Unmarshal → executionConsidersPayload{PeerIDs:[PeerID_B, ...]} + loop Pour chaque peer_id cible + NodeA -> StreamA: PublishCommon(dt, user, PeerID_B, ProtocolConsidersResource, payload) + StreamA -> NodeB: write → /opencloud/resource/considers/1.0 + NodeB -> NodeB: passConsidering(evt) + NodeB -> NATSB: SetNATSPub(PROPALGATION_EVENT, {PB_CONSIDERS, dt, payload}) + NATSB -> DBB: (traité par oc-workflow sur NATS B) + end + +else Action == PB_PLANNER (broadcast) + NodeA -> NodeA: Unmarshal → {peer_id: nil, ...payload} + loop Pour chaque stream ProtocolSendPlanner ouvert + NodeA -> StreamA: PublishCommon(nil, user, pid, ProtocolSendPlanner, payload) + StreamA -> NodeB: write → /opencloud/resource/planner/1.0 + end + +else Action == PB_CLOSE_PLANNER + NodeA -> NodeA: Unmarshal → {peer_id: PeerID_B} + NodeA -> StreamA: Streams[ProtocolSendPlanner][PeerID_B].Stream.Close() + NodeA -> StreamA: delete(Streams[ProtocolSendPlanner], PeerID_B) + +else Action == PB_SEARCH + DataType == PEER + NodeA -> NodeA: Unmarshal → {search: "..."} + NodeA -> NodeA: GetPeerRecord(ctx, search) + note over NodeA: Résolution via DB A + Indexer + DHT + NodeA -> NATSA: SetNATSPub(SEARCH_EVENT, {PEER, PeerRecord JSON}) + NATSA -> NATSA: (AppA reçoit le résultat) + +else Action == PB_SEARCH + autre DataType + NodeA -> NodeA: Unmarshal → {type:"all"|"known"|"partner", search:"..."} + NodeA -> NodeA: PubSubService.SearchPublishEvent(ctx, dt, type, user, search) + note over NodeA: Voir diagrammes 10 et 11 +end + +@enduml diff --git a/docs/diagrams/10_pubsub_search.mmd b/docs/diagrams/10_pubsub_search.mmd new file mode 100644 index 0000000..267c64a --- /dev/null +++ b/docs/diagrams/10_pubsub_search.mmd @@ -0,0 +1,52 @@ +sequenceDiagram + title PubSub — Recherche gossip globale (type "all") : Pair A cherche, Pair B répond + + participant AppA as App Pair A + participant NATSA as NATS A + participant NodeA as Node A + participant PubSubA as PubSubService A + participant GossipSub as GossipSub libp2p (mesh) + participant NodeB as Node B + participant PubSubB as PubSubService B + participant DBB as DB Pair B (oc-lib) + participant StreamB as StreamService B + participant StreamA as StreamService A + + AppA->>NATSA: Publish(PROPALGATION_EVENT, {PB_SEARCH, type:"all", search:"gpu"}) + NATSA->>NodeA: ListenNATS → PB_SEARCH (type "all") + + NodeA->>PubSubA: SearchPublishEvent(ctx, dt, "all", user, "gpu") + PubSubA->>PubSubA: publishEvent(PB_SEARCH, user, {search:"gpu"}) + PubSubA->>PubSubA: GenerateNodeID() → from = DID_A + PubSubA->>PubSubA: priv_A.Sign(event body) → sig + PubSubA->>PubSubA: Build Event{Type:"search", From:DID_A, Payload:{search:"gpu"}, Sig} + + PubSubA->>GossipSub: topic.Join("search") + PubSubA->>GossipSub: topic.Publish(ctx, json(Event)) + + GossipSub-->>NodeB: Message propagé (gossip mesh) + + NodeB->>PubSubB: subscribeEvents écoute topic "search#" + PubSubB->>PubSubB: json.Unmarshal → Event{From: DID_A} + + PubSubB->>NodeB: GetPeerRecord(ctx, DID_A) + Note over NodeB: Résolution Pair A via DB B ou Indexer + NodeB-->>PubSubB: Peer A {PublicKey_A, Relation, ...} + + PubSubB->>PubSubB: event.Verify(Peer A) → valide sig_A + PubSubB->>PubSubB: handleEventSearch(ctx, evt, PB_SEARCH) + + PubSubB->>StreamB: SendResponse(Peer A, evt) + StreamB->>DBB: Search(COMPUTE + STORAGE + ..., filters{creator=self, access=PUBLIC OR partnerships[PeerID_A]}, search="gpu") + DBB-->>StreamB: [Resource1, Resource2, ...] + + loop Pour chaque ressource matchée + StreamB->>StreamB: write(PeerID_A, addr_A, dt, resource JSON, ProtocolSearchResource) + StreamB->>StreamA: NewStream /opencloud/resource/search/1.0 + StreamB->>StreamA: json.Encode(Event{Type:search, From:DID_B, DataType, Payload:resource}) + end + + StreamA->>StreamA: readLoop → handleEvent(ProtocolSearchResource, evt) + StreamA->>StreamA: retrieveResponse(evt) + StreamA->>NATSA: SetNATSPub(SEARCH_EVENT, {DataType, resource JSON}) + NATSA->>AppA: Résultats de recherche de Pair B diff --git a/docs/diagrams/10_pubsub_search.puml b/docs/diagrams/10_pubsub_search.puml new file mode 100644 index 0000000..1249b78 --- /dev/null +++ b/docs/diagrams/10_pubsub_search.puml @@ -0,0 +1,54 @@ +@startuml +title PubSub — Recherche gossip globale (type "all") : Pair A cherche, Pair B répond + +participant "App Pair A" as AppA +participant "NATS A" as NATSA +participant "Node A" as NodeA +participant "PubSubService A" as PubSubA +participant "GossipSub libp2p (mesh)" as GossipSub +participant "Node B" as NodeB +participant "PubSubService B" as PubSubB +participant "DB Pair B (oc-lib)" as DBB +participant "StreamService B" as StreamB +participant "StreamService A" as StreamA + +AppA -> NATSA: Publish(PROPALGATION_EVENT, {PB_SEARCH, type:"all", search:"gpu"}) +NATSA -> NodeA: ListenNATS → PB_SEARCH (type "all") + +NodeA -> PubSubA: SearchPublishEvent(ctx, dt, "all", user, "gpu") +PubSubA -> PubSubA: publishEvent(PB_SEARCH, user, {search:"gpu"}) +PubSubA -> PubSubA: GenerateNodeID() → from = DID_A +PubSubA -> PubSubA: priv_A.Sign(event body) → sig +PubSubA -> PubSubA: Build Event{Type:"search", From:DID_A, Payload:{search:"gpu"}, Sig} + +PubSubA -> GossipSub: topic.Join("search") +PubSubA -> GossipSub: topic.Publish(ctx, json(Event)) + +GossipSub --> NodeB: Message propagé (gossip mesh) + +NodeB -> PubSubB: subscribeEvents écoute topic "search#" +PubSubB -> PubSubB: json.Unmarshal → Event{From: DID_A} + +PubSubB -> NodeB: GetPeerRecord(ctx, DID_A) +note over NodeB: Résolution Pair A via DB B ou Indexer +NodeB --> PubSubB: Peer A {PublicKey_A, Relation, ...} + +PubSubB -> PubSubB: event.Verify(Peer A) → valide sig_A +PubSubB -> PubSubB: handleEventSearch(ctx, evt, PB_SEARCH) + +PubSubB -> StreamB: SendResponse(Peer A, evt) +StreamB -> DBB: Search(COMPUTE + STORAGE + ..., filters{creator=self, access=PUBLIC OR partnerships[PeerID_A]}, search="gpu") +DBB --> StreamB: [Resource1, Resource2, ...] + +loop Pour chaque ressource matchée + StreamB -> StreamB: write(PeerID_A, addr_A, dt, resource JSON, ProtocolSearchResource) + StreamB -> StreamA: NewStream /opencloud/resource/search/1.0 + StreamB -> StreamA: json.Encode(Event{Type:search, From:DID_B, DataType, Payload:resource}) +end + +StreamA -> StreamA: readLoop → handleEvent(ProtocolSearchResource, evt) +StreamA -> StreamA: retrieveResponse(evt) +StreamA -> NATSA: SetNATSPub(SEARCH_EVENT, {DataType, resource JSON}) +NATSA -> AppA: Résultats de recherche de Pair B + +@enduml diff --git a/docs/diagrams/11_stream_search.mmd b/docs/diagrams/11_stream_search.mmd new file mode 100644 index 0000000..2583444 --- /dev/null +++ b/docs/diagrams/11_stream_search.mmd @@ -0,0 +1,52 @@ +sequenceDiagram + title Stream — Recherche directe (type "known"/"partner") : Pair A → Pair B + + participant AppA as App Pair A + participant NATSA as NATS A + participant NodeA as Node A + participant PubSubA as PubSubService A + participant StreamA as StreamService A + participant DBA as DB Pair A (oc-lib) + participant NodeB as Node B + participant StreamB as StreamService B + participant DBB as DB Pair B (oc-lib) + + AppA->>NATSA: Publish(PROPALGATION_EVENT, {PB_SEARCH, type:"partner", search:"gpu"}) + NATSA->>NodeA: ListenNATS → PB_SEARCH (type "partner") + NodeA->>PubSubA: SearchPublishEvent(ctx, dt, "partner", user, "gpu") + + PubSubA->>StreamA: SearchPartnersPublishEvent(dt, user, "gpu") + StreamA->>DBA: Search(PEER, PARTNER) + PeerIDS config + DBA-->>StreamA: [Peer B, ...] + + loop Pour chaque pair partenaire (Pair B) + StreamA->>StreamA: json.Marshal({search:"gpu"}) → payload + StreamA->>StreamA: write(PeerID_B, addr_B, dt, user, payload, ProtocolSearchResource) + StreamA->>NodeB: TempStream /opencloud/resource/search/1.0 + StreamA->>NodeB: json.Encode(Event{Type:search, From:DID_A, DataType, Payload:{search:"gpu"}}) + + NodeB->>StreamB: HandleResponse(stream) → readLoop + StreamB->>StreamB: handleEvent(ProtocolSearchResource, evt) + StreamB->>StreamB: handleEventFromPartner(evt, ProtocolSearchResource) + + alt evt.DataType == -1 (toutes ressources) + StreamB->>DBA: Search(PEER, evt.From=DID_A) + Note over StreamB: Résolution locale ou via GetPeerRecord + StreamB->>StreamB: SendResponse(Peer A, evt) + StreamB->>DBB: Search(ALL_RESOURCES, filter{creator=B + public OR partner A + search:"gpu"}) + DBB-->>StreamB: [Resource1, Resource2, ...] + else evt.DataType spécifié + StreamB->>DBB: Search(DataType, filter{creator=B + access + search:"gpu"}) + DBB-->>StreamB: [Resource1, ...] + end + + loop Pour chaque ressource + StreamB->>StreamA: write(PeerID_A, addr_A, dt, resource JSON, ProtocolSearchResource) + StreamA->>StreamA: readLoop → handleEvent(ProtocolSearchResource, evt) + StreamA->>StreamA: retrieveResponse(evt) + StreamA->>NATSA: SetNATSPub(SEARCH_EVENT, {DataType, resource JSON}) + NATSA->>AppA: Résultat de Pair B + end + end + + Note over NATSA,DBA: Optionnel: App A persiste
les ressources découvertes dans DB A diff --git a/docs/diagrams/11_stream_search.puml b/docs/diagrams/11_stream_search.puml new file mode 100644 index 0000000..1ac14b4 --- /dev/null +++ b/docs/diagrams/11_stream_search.puml @@ -0,0 +1,54 @@ +@startuml +title Stream — Recherche directe (type "known"/"partner") : Pair A → Pair B + +participant "App Pair A" as AppA +participant "NATS A" as NATSA +participant "Node A" as NodeA +participant "PubSubService A" as PubSubA +participant "StreamService A" as StreamA +participant "DB Pair A (oc-lib)" as DBA +participant "Node B" as NodeB +participant "StreamService B" as StreamB +participant "DB Pair B (oc-lib)" as DBB + +AppA -> NATSA: Publish(PROPALGATION_EVENT, {PB_SEARCH, type:"partner", search:"gpu"}) +NATSA -> NodeA: ListenNATS → PB_SEARCH (type "partner") +NodeA -> PubSubA: SearchPublishEvent(ctx, dt, "partner", user, "gpu") + +PubSubA -> StreamA: SearchPartnersPublishEvent(dt, user, "gpu") +StreamA -> DBA: Search(PEER, PARTNER) + PeerIDS config +DBA --> StreamA: [Peer B, ...] + +loop Pour chaque pair partenaire (Pair B) + StreamA -> StreamA: json.Marshal({search:"gpu"}) → payload + StreamA -> StreamA: write(PeerID_B, addr_B, dt, user, payload, ProtocolSearchResource) + StreamA -> NodeB: TempStream /opencloud/resource/search/1.0 + StreamA -> NodeB: json.Encode(Event{Type:search, From:DID_A, DataType, Payload:{search:"gpu"}}) + + NodeB -> StreamB: HandleResponse(stream) → readLoop + StreamB -> StreamB: handleEvent(ProtocolSearchResource, evt) + StreamB -> StreamB: handleEventFromPartner(evt, ProtocolSearchResource) + + alt evt.DataType == -1 (toutes ressources) + StreamB -> DBA: Search(PEER, evt.From=DID_A) + note over StreamB: Résolution locale ou via GetPeerRecord + StreamB -> StreamB: SendResponse(Peer A, evt) + StreamB -> DBB: Search(ALL_RESOURCES, filter{creator=B + public OR partner A + search:"gpu"}) + DBB --> StreamB: [Resource1, Resource2, ...] + else evt.DataType spécifié + StreamB -> DBB: Search(DataType, filter{creator=B + access + search:"gpu"}) + DBB --> StreamB: [Resource1, ...] + end + + loop Pour chaque ressource + StreamB -> StreamA: write(PeerID_A, addr_A, dt, resource JSON, ProtocolSearchResource) + StreamA -> StreamA: readLoop → handleEvent(ProtocolSearchResource, evt) + StreamA -> StreamA: retrieveResponse(evt) + StreamA -> NATSA: SetNATSPub(SEARCH_EVENT, {DataType, resource JSON}) + NATSA -> AppA: Résultat de Pair B + end +end + +note over NATSA,DBA: Optionnel: App A persiste\nles ressources découvertes dans DB A + +@enduml diff --git a/docs/diagrams/12_partner_heartbeat.mmd b/docs/diagrams/12_partner_heartbeat.mmd new file mode 100644 index 0000000..2fc21e3 --- /dev/null +++ b/docs/diagrams/12_partner_heartbeat.mmd @@ -0,0 +1,58 @@ +sequenceDiagram + title Stream — Partner Heartbeat et propagation CRUD Pair A ↔ Pair B + + participant DBA as DB Pair A (oc-lib) + participant StreamA as StreamService A + participant NodeA as Node A + participant NodeB as Node B + participant StreamB as StreamService B + participant NATSB as NATS B + participant DBB as DB Pair B (oc-lib) + participant NATSA as NATS A + + Note over StreamA: Démarrage → connectToPartners() + + StreamA->>DBA: Search(PEER, PARTNER) + PeerIDS config + DBA-->>StreamA: [Peer B, ...] + + StreamA->>NodeB: Connect (libp2p) + StreamA->>NodeB: NewStream /opencloud/resource/heartbeat/partner/1.0 + StreamA->>NodeB: json.Encode(Heartbeat{Name_A, DID_A, PeerID_A, IndexersBinded_A}) + + NodeB->>StreamB: HandlePartnerHeartbeat(stream) + StreamB->>StreamB: CheckHeartbeat → bandwidth challenge + StreamB->>StreamA: Echo(payload) + StreamB->>StreamB: streams[ProtocolHeartbeatPartner][PeerID_A] = {DID_A, Expiry=now+10s} + + StreamA->>StreamA: streams[ProtocolHeartbeatPartner][PeerID_B] = {DID_B, Expiry=now+10s} + + Note over StreamA,StreamB: Stream partner long-lived établi
GC toutes les 8s (StreamService A)
GC toutes les 30s (StreamService B) + + Note over NATSA: Pair A reçoit PROPALGATION_EVENT{PB_DELETE, dt:"storage", payload:res} + + NATSA->>NodeA: ListenNATS → ToPartnerPublishEvent(PB_DELETE, dt, user, payload) + NodeA->>StreamA: ToPartnerPublishEvent(ctx, PB_DELETE, dt_storage, user, payload) + + alt dt == PEER (mise à jour relation partenaire) + StreamA->>StreamA: json.Unmarshal → peer.Peer B updated + alt B.Relation == PARTNER + StreamA->>NodeB: ConnectToPartner(B.StreamAddress) + Note over StreamA,NodeB: Reconnexion heartbeat si relation upgrade + else B.Relation != PARTNER + loop Tous les protocoles + StreamA->>StreamA: delete(streams[proto][PeerID_B]) + StreamA->>NodeB: (streams fermés) + end + end + else dt != PEER (ressource ordinaire) + StreamA->>DBA: Search(PEER, PARTNER) → [Pair B, ...] + loop Pour chaque protocole partner (Create/Update/Delete) + StreamA->>NodeB: write(PeerID_B, addr_B, dt, user, payload, ProtocolDeleteResource) + Note over NodeB: /opencloud/resource/delete/1.0 + + NodeB->>StreamB: HandleResponse → readLoop + StreamB->>StreamB: handleEventFromPartner(evt, ProtocolDeleteResource) + StreamB->>NATSB: SetNATSPub(REMOVE_RESOURCE, {DataType, resource JSON}) + NATSB->>DBB: Supprimer ressource dans DB B + end + end diff --git a/docs/diagrams/12_partner_heartbeat.puml b/docs/diagrams/12_partner_heartbeat.puml new file mode 100644 index 0000000..aabca38 --- /dev/null +++ b/docs/diagrams/12_partner_heartbeat.puml @@ -0,0 +1,60 @@ +@startuml +title Stream — Partner Heartbeat et propagation CRUD Pair A ↔ Pair B + +participant "DB Pair A (oc-lib)" as DBA +participant "StreamService A" as StreamA +participant "Node A" as NodeA +participant "Node B" as NodeB +participant "StreamService B" as StreamB +participant "NATS B" as NATSB +participant "DB Pair B (oc-lib)" as DBB +participant "NATS A" as NATSA + +note over StreamA: Démarrage → connectToPartners() + +StreamA -> DBA: Search(PEER, PARTNER) + PeerIDS config +DBA --> StreamA: [Peer B, ...] + +StreamA -> NodeB: Connect (libp2p) +StreamA -> NodeB: NewStream /opencloud/resource/heartbeat/partner/1.0 +StreamA -> NodeB: json.Encode(Heartbeat{Name_A, DID_A, PeerID_A, IndexersBinded_A}) + +NodeB -> StreamB: HandlePartnerHeartbeat(stream) +StreamB -> StreamB: CheckHeartbeat → bandwidth challenge +StreamB -> StreamA: Echo(payload) +StreamB -> StreamB: streams[ProtocolHeartbeatPartner][PeerID_A] = {DID_A, Expiry=now+10s} + +StreamA -> StreamA: streams[ProtocolHeartbeatPartner][PeerID_B] = {DID_B, Expiry=now+10s} + +note over StreamA,StreamB: Stream partner long-lived établi\nGC toutes les 8s (StreamService A)\nGC toutes les 30s (StreamService B) + +note over NATSA: Pair A reçoit PROPALGATION_EVENT{PB_DELETE, dt:"storage", payload:res} + +NATSA -> NodeA: ListenNATS → ToPartnerPublishEvent(PB_DELETE, dt, user, payload) +NodeA -> StreamA: ToPartnerPublishEvent(ctx, PB_DELETE, dt_storage, user, payload) + +alt dt == PEER (mise à jour relation partenaire) + StreamA -> StreamA: json.Unmarshal → peer.Peer B updated + alt B.Relation == PARTNER + StreamA -> NodeB: ConnectToPartner(B.StreamAddress) + note over StreamA,NodeB: Reconnexion heartbeat si relation upgrade + else B.Relation != PARTNER + loop Tous les protocoles + StreamA -> StreamA: delete(streams[proto][PeerID_B]) + StreamA -> NodeB: (streams fermés) + end + end +else dt != PEER (ressource ordinaire) + StreamA -> DBA: Search(PEER, PARTNER) → [Pair B, ...] + loop Pour chaque protocole partner (Create/Update/Delete) + StreamA -> NodeB: write(PeerID_B, addr_B, dt, user, payload, ProtocolDeleteResource) + note over NodeB: /opencloud/resource/delete/1.0 + + NodeB -> StreamB: HandleResponse → readLoop + StreamB -> StreamB: handleEventFromPartner(evt, ProtocolDeleteResource) + StreamB -> NATSB: SetNATSPub(REMOVE_RESOURCE, {DataType, resource JSON}) + NATSB -> DBB: Supprimer ressource dans DB B + end +end + +@enduml diff --git a/docs/diagrams/13_planner_flow.mmd b/docs/diagrams/13_planner_flow.mmd new file mode 100644 index 0000000..4d6f792 --- /dev/null +++ b/docs/diagrams/13_planner_flow.mmd @@ -0,0 +1,49 @@ +sequenceDiagram + title Stream — Session Planner : Pair A demande le plan de Pair B + + participant AppA as App Pair A (oc-booking) + participant NATSA as NATS A + participant NodeA as Node A + participant StreamA as StreamService A + participant NodeB as Node B + participant StreamB as StreamService B + participant DBB as DB Pair B (oc-lib) + participant NATSB as NATS B + + %% Ouverture session planner + AppA->>NATSA: Publish(PROPALGATION_EVENT, {PB_PLANNER, peer_id:PeerID_B, payload:{}}) + NATSA->>NodeA: ListenNATS → PB_PLANNER + + NodeA->>NodeA: Unmarshal → {peer_id: PeerID_B, payload: {}} + NodeA->>StreamA: PublishCommon(nil, user, PeerID_B, ProtocolSendPlanner, {}) + Note over StreamA: WaitResponse=true, TTL=24h
Stream long-lived vers Pair B + StreamA->>NodeB: TempStream /opencloud/resource/planner/1.0 + StreamA->>NodeB: json.Encode(Event{Type:planner, From:DID_A, Payload:{}}) + + NodeB->>StreamB: HandleResponse → readLoop(ProtocolSendPlanner) + StreamB->>StreamB: handleEvent(ProtocolSendPlanner, evt) + StreamB->>StreamB: sendPlanner(evt) + + alt evt.Payload vide (requête initiale) + StreamB->>DBB: planner.GenerateShallow(AdminRequest) + DBB-->>StreamB: plan (shallow booking plan de Pair B) + StreamB->>StreamA: PublishCommon(nil, user, DID_A, ProtocolSendPlanner, planJSON) + StreamA->>NodeA: json.Encode(Event{plan de B}) + NodeA->>NATSA: (forwardé à AppA via SEARCH_EVENT ou PLANNER event) + NATSA->>AppA: Plan de Pair B + else evt.Payload non vide (mise à jour planner) + StreamB->>StreamB: m["peer_id"] = evt.From (DID_A) + StreamB->>NATSB: SetNATSPub(PROPALGATION_EVENT, {PB_PLANNER, peer_id:DID_A, payload:plan}) + NATSB->>DBB: (oc-booking traite le plan sur NATS B) + end + + %% Fermeture session planner + AppA->>NATSA: Publish(PROPALGATION_EVENT, {PB_CLOSE_PLANNER, peer_id:PeerID_B}) + NATSA->>NodeA: ListenNATS → PB_CLOSE_PLANNER + + NodeA->>NodeA: Unmarshal → {peer_id: PeerID_B} + NodeA->>StreamA: Mu.Lock() + NodeA->>StreamA: Streams[ProtocolSendPlanner][PeerID_B].Stream.Close() + NodeA->>StreamA: delete(Streams[ProtocolSendPlanner], PeerID_B) + NodeA->>StreamA: Mu.Unlock() + Note over StreamA,NodeB: Stream planner fermé — session terminée diff --git a/docs/diagrams/13_planner_flow.puml b/docs/diagrams/13_planner_flow.puml new file mode 100644 index 0000000..00184aa --- /dev/null +++ b/docs/diagrams/13_planner_flow.puml @@ -0,0 +1,51 @@ +@startuml +title Stream — Session Planner : Pair A demande le plan de Pair B + +participant "App Pair A (oc-booking)" as AppA +participant "NATS A" as NATSA +participant "Node A" as NodeA +participant "StreamService A" as StreamA +participant "Node B" as NodeB +participant "StreamService B" as StreamB +participant "DB Pair B (oc-lib)" as DBB +participant "NATS B" as NATSB + +' Ouverture session planner +AppA -> NATSA: Publish(PROPALGATION_EVENT, {PB_PLANNER, peer_id:PeerID_B, payload:{}}) +NATSA -> NodeA: ListenNATS → PB_PLANNER + +NodeA -> NodeA: Unmarshal → {peer_id: PeerID_B, payload: {}} +NodeA -> StreamA: PublishCommon(nil, user, PeerID_B, ProtocolSendPlanner, {}) +note over StreamA: WaitResponse=true, TTL=24h\nStream long-lived vers Pair B +StreamA -> NodeB: TempStream /opencloud/resource/planner/1.0 +StreamA -> NodeB: json.Encode(Event{Type:planner, From:DID_A, Payload:{}}) + +NodeB -> StreamB: HandleResponse → readLoop(ProtocolSendPlanner) +StreamB -> StreamB: handleEvent(ProtocolSendPlanner, evt) +StreamB -> StreamB: sendPlanner(evt) + +alt evt.Payload vide (requête initiale) + StreamB -> DBB: planner.GenerateShallow(AdminRequest) + DBB --> StreamB: plan (shallow booking plan de Pair B) + StreamB -> StreamA: PublishCommon(nil, user, DID_A, ProtocolSendPlanner, planJSON) + StreamA -> NodeA: json.Encode(Event{plan de B}) + NodeA -> NATSA: (forwardé à AppA via SEARCH_EVENT ou PLANNER event) + NATSA -> AppA: Plan de Pair B +else evt.Payload non vide (mise à jour planner) + StreamB -> StreamB: m["peer_id"] = evt.From (DID_A) + StreamB -> NATSB: SetNATSPub(PROPALGATION_EVENT, {PB_PLANNER, peer_id:DID_A, payload:plan}) + NATSB -> DBB: (oc-booking traite le plan sur NATS B) +end + +' Fermeture session planner +AppA -> NATSA: Publish(PROPALGATION_EVENT, {PB_CLOSE_PLANNER, peer_id:PeerID_B}) +NATSA -> NodeA: ListenNATS → PB_CLOSE_PLANNER + +NodeA -> NodeA: Unmarshal → {peer_id: PeerID_B} +NodeA -> StreamA: Mu.Lock() +NodeA -> StreamA: Streams[ProtocolSendPlanner][PeerID_B].Stream.Close() +NodeA -> StreamA: delete(Streams[ProtocolSendPlanner], PeerID_B) +NodeA -> StreamA: Mu.Unlock() +note over StreamA,NodeB: Stream planner fermé — session terminée + +@enduml diff --git a/docs/diagrams/14_native_offload_gc.mmd b/docs/diagrams/14_native_offload_gc.mmd new file mode 100644 index 0000000..7a6ed14 --- /dev/null +++ b/docs/diagrams/14_native_offload_gc.mmd @@ -0,0 +1,59 @@ +sequenceDiagram + title Native Indexer — Boucles background (offload, DHT refresh, GC streams) + + participant IndexerA as Indexer A (enregistré) + participant IndexerB as Indexer B (enregistré) + participant Native as Native Indexer + participant DHT as DHT Kademlia + participant NodeA as Node A (responsible peer) + + Note over Native: runOffloadLoop — toutes les 30s + + loop Toutes les 30s + Native->>Native: len(responsiblePeers) > 0 ? + Note over Native: responsiblePeers = peers pour lesquels
le native a fait selfDelegate (aucun indexer dispo) + alt Des responsible peers existent (ex: Node A) + Native->>Native: reachableLiveIndexers() + Note over Native: Filtre liveIndexers par TTL
ping PeerIsAlive pour chaque candidat + alt Indexers A et B maintenant joignables + Native->>Native: responsiblePeers = {} (libère Node A et autres) + Note over Native: Node A se reconnectera
au prochain ConnectToNatives + else Toujours aucun indexer + Note over Native: Node A reste sous la responsabilité du native + end + end + end + + Note over Native: refreshIndexersFromDHT — toutes les 30s + + loop Toutes les 30s + Native->>Native: Collecter tous les knownPeerIDs
= {PeerID_A, PeerID_B, ...} + loop Pour chaque PeerID connu + Native->>Native: liveIndexers[PeerID] encore frais ? + alt Entrée manquante ou expirée + Native->>DHT: SearchValue(ctx 5s, "/indexer/"+PeerID) + DHT-->>Native: channel de bytes + loop Pour chaque résultat DHT + Native->>Native: Unmarshal → liveIndexerEntry + Native->>Native: Garder le meilleur (ExpiresAt le plus récent, valide) + end + Native->>Native: liveIndexers[PeerID] = best entry + Note over Native: "native: refreshed indexer from DHT" + end + end + end + + Note over Native: LongLivedStreamRecordedService GC — toutes les 30s + + loop Toutes les 30s + Native->>Native: gc() — lock StreamRecords[Heartbeat] + loop Pour chaque StreamRecord (Indexer A, B, ...) + Native->>Native: now > rec.Expiry ?
OU timeSince(LastSeen) > 2×TTL restant ? + alt Pair périmé (ex: Indexer B disparu) + Native->>Native: Supprimer Indexer B de TOUS les maps de protocoles + Note over Native: Stream heartbeat fermé
liveIndexers[PeerID_B] expirera naturellement + end + end + end + + Note over IndexerA: Indexer A continue à heartbeater normalement
et reste dans StreamRecords + liveIndexers diff --git a/docs/diagrams/14_native_offload_gc.puml b/docs/diagrams/14_native_offload_gc.puml new file mode 100644 index 0000000..972754f --- /dev/null +++ b/docs/diagrams/14_native_offload_gc.puml @@ -0,0 +1,61 @@ +@startuml +title Native Indexer — Boucles background (offload, DHT refresh, GC streams) + +participant "Indexer A (enregistré)" as IndexerA +participant "Indexer B (enregistré)" as IndexerB +participant "Native Indexer" as Native +participant "DHT Kademlia" as DHT +participant "Node A (responsible peer)" as NodeA + +note over Native: runOffloadLoop — toutes les 30s + +loop Toutes les 30s + Native -> Native: len(responsiblePeers) > 0 ? + note over Native: responsiblePeers = peers pour lesquels\nle native a fait selfDelegate (aucun indexer dispo) + alt Des responsible peers existent (ex: Node A) + Native -> Native: reachableLiveIndexers() + note over Native: Filtre liveIndexers par TTL\nping PeerIsAlive pour chaque candidat + alt Indexers A et B maintenant joignables + Native -> Native: responsiblePeers = {} (libère Node A et autres) + note over Native: Node A se reconnectera\nau prochain ConnectToNatives + else Toujours aucun indexer + note over Native: Node A reste sous la responsabilité du native + end + end +end + +note over Native: refreshIndexersFromDHT — toutes les 30s + +loop Toutes les 30s + Native -> Native: Collecter tous les knownPeerIDs\n= {PeerID_A, PeerID_B, ...} + loop Pour chaque PeerID connu + Native -> Native: liveIndexers[PeerID] encore frais ? + alt Entrée manquante ou expirée + Native -> DHT: SearchValue(ctx 5s, "/indexer/"+PeerID) + DHT --> Native: channel de bytes + loop Pour chaque résultat DHT + Native -> Native: Unmarshal → liveIndexerEntry + Native -> Native: Garder le meilleur (ExpiresAt le plus récent, valide) + end + Native -> Native: liveIndexers[PeerID] = best entry + note over Native: "native: refreshed indexer from DHT" + end + end +end + +note over Native: LongLivedStreamRecordedService GC — toutes les 30s + +loop Toutes les 30s + Native -> Native: gc() — lock StreamRecords[Heartbeat] + loop Pour chaque StreamRecord (Indexer A, B, ...) + Native -> Native: now > rec.Expiry ?\nOU timeSince(LastSeen) > 2×TTL restant ? + alt Pair périmé (ex: Indexer B disparu) + Native -> Native: Supprimer Indexer B de TOUS les maps de protocoles + note over Native: Stream heartbeat fermé\nliveIndexers[PeerID_B] expirera naturellement + end + end +end + +note over IndexerA: Indexer A continue à heartbeater normalement\net reste dans StreamRecords + liveIndexers + +@enduml diff --git a/docs/diagrams/README.md b/docs/diagrams/README.md new file mode 100644 index 0000000..7a8b391 --- /dev/null +++ b/docs/diagrams/README.md @@ -0,0 +1,43 @@ +# OC-Discovery — Diagrammes de séquence + +Tous les fichiers `.mmd` sont au format [Mermaid](https://mermaid.js.org/). +Rendu possible via VS Code (extension Mermaid Preview), IntelliJ, ou [mermaid.live](https://mermaid.live). + +## Vue d'ensemble des diagrammes + +| Fichier | Description | +|---------|-------------| +| `01_node_init.mmd` | Initialisation complète d'un Node (libp2p host, GossipSub, indexers, StreamService, PubSubService, NATS) | +| `02_node_claim.mmd` | Enregistrement du nœud auprès des indexeurs (`claimInfo` + `publishPeerRecord`) | +| `03_indexer_heartbeat.mmd` | Protocole heartbeat avec calcul du score qualité (bande passante, uptime, diversité) | +| `04_indexer_publish.mmd` | Publication d'un `PeerRecord` vers l'indexeur → DHT | +| `05_indexer_get.mmd` | Résolution d'un pair via l'indexeur (`GetPeerRecord` + `handleNodeGet` + DHT) | +| `06_native_registration.mmd` | Enregistrement d'un indexeur auprès d'un Native Indexer + gossip PubSub | +| `07_native_get_consensus.mmd` | `ConnectToNatives` : pool d'indexeurs + protocole de consensus (vote majoritaire) | +| `08_nats_create_resource.mmd` | Handler NATS `CREATE_RESOURCE` : connexion/déconnexion d'un partner | +| `09_nats_propagation.mmd` | Handler NATS `PROPALGATION_EVENT` : delete, considers, planner, search | +| `10_pubsub_search.mmd` | Recherche gossip globale (type `"all"`) via GossipSub | +| `11_stream_search.mmd` | Recherche directe par stream (type `"known"` ou `"partner"`) | +| `12_partner_heartbeat.mmd` | Heartbeat partner + propagation CRUD vers les partenaires | +| `13_planner_flow.mmd` | Session planner (ouverture, échange, fermeture) | +| `14_native_offload_gc.mmd` | Boucles background du Native Indexer (offload, DHT refresh, GC) | + +## Protocoles libp2p utilisés + +| Protocole | Description | +|-----------|-------------| +| `/opencloud/heartbeat/1.0` | Heartbeat node → indexeur (long-lived) | +| `/opencloud/heartbeat/indexer/1.0` | Heartbeat indexeur → native (long-lived) | +| `/opencloud/resource/heartbeat/partner/1.0` | Heartbeat node ↔ partner (long-lived) | +| `/opencloud/record/publish/1.0` | Publication `PeerRecord` vers indexeur | +| `/opencloud/record/get/1.0` | Requête `GetPeerRecord` vers indexeur | +| `/opencloud/native/subscribe/1.0` | Enregistrement indexeur auprès du native | +| `/opencloud/native/indexers/1.0` | Requête de pool d'indexeurs au native | +| `/opencloud/native/consensus/1.0` | Validation de pool d'indexeurs (consensus) | +| `/opencloud/resource/search/1.0` | Recherche de ressources entre peers | +| `/opencloud/resource/create/1.0` | Propagation création ressource vers partner | +| `/opencloud/resource/update/1.0` | Propagation mise à jour ressource vers partner | +| `/opencloud/resource/delete/1.0` | Propagation suppression ressource vers partner | +| `/opencloud/resource/planner/1.0` | Session planner (booking) | +| `/opencloud/resource/verify/1.0` | Vérification signature ressource | +| `/opencloud/resource/considers/1.0` | Transmission d'un "considers" d'exécution | diff --git a/go.mod b/go.mod index f70783b..3bf0704 100644 --- a/go.mod +++ b/go.mod @@ -1,28 +1,34 @@ module oc-discovery -go 1.24.6 +go 1.25.0 require ( - cloud.o-forge.io/core/oc-lib v0.0.0-20260218132556-0b41e2505e2f - github.com/beego/beego v1.12.13 - github.com/beego/beego/v2 v2.3.8 - github.com/go-redis/redis v6.15.9+incompatible - github.com/smartystreets/goconvey v1.7.2 - github.com/tidwall/gjson v1.17.3 + cloud.o-forge.io/core/oc-lib v0.0.0-20260224130821-ce8ef70516f7 + github.com/libp2p/go-libp2p v0.47.0 + github.com/libp2p/go-libp2p-record v0.3.1 + github.com/multiformats/go-multiaddr v0.16.1 ) require ( + github.com/beego/beego/v2 v2.3.8 // indirect github.com/benbjohnson/clock v1.3.5 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/dunglas/httpsfv v1.1.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/filecoin-project/go-clock v0.1.0 // indirect github.com/flynn/noise v1.1.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/gorilla/websocket v1.5.3 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/ipfs/boxo v0.35.2 // indirect @@ -32,29 +38,31 @@ require ( github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/koron/go-ssdp v0.0.6 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.3.0 // indirect - github.com/libp2p/go-libp2p v0.47.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect github.com/libp2p/go-libp2p-kbucket v0.8.0 // indirect - github.com/libp2p/go-libp2p-record v0.3.1 // indirect github.com/libp2p/go-libp2p-routing-helpers v0.7.5 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect github.com/libp2p/go-netroute v0.4.0 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v5 v5.0.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/miekg/dns v1.1.68 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr v0.16.1 // indirect github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multibase v0.2.0 // indirect @@ -89,6 +97,7 @@ require ( github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect github.com/wlynxg/anet v0.0.5 // indirect + github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/otel v1.39.0 // indirect go.opentelemetry.io/otel/metric v1.39.0 // indirect @@ -99,13 +108,28 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/mod v0.32.0 // indirect + golang.org/x/oauth2 v0.32.0 // indirect golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 // indirect + golang.org/x/term v0.39.0 // indirect golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.41.0 // indirect gonum.org/v1/gonum v0.17.0 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + k8s.io/api v0.35.1 // indirect + k8s.io/apimachinery v0.35.1 // indirect + k8s.io/client-go v0.35.1 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect lukechampine.com/blake3 v1.4.1 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) require ( @@ -117,13 +141,10 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.27.0 // indirect github.com/golang/snappy v1.0.0 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect + github.com/google/uuid v1.6.0 github.com/goraz/onion v0.1.3 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect - github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/klauspost/compress v1.18.0 // indirect - github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/libp2p/go-libp2p-kad-dht v0.37.1 github.com/libp2p/go-libp2p-pubsub v0.15.0 @@ -139,13 +160,8 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.17.0 // indirect - github.com/robfig/cron v1.2.0 // indirect - github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rs/zerolog v1.34.0 // indirect github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 // indirect - github.com/smartystreets/assertions v1.2.0 // indirect - github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.1 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect diff --git a/go.sum b/go.sum index 59e1c2d..baadcec 100644 --- a/go.sum +++ b/go.sum @@ -1,228 +1,139 @@ -cloud.o-forge.io/core/oc-lib v0.0.0-20250108155542-0f4adeea86be h1:1Yf8ihUxXjOEPqcfgtXJpJ/slxBUHhf7AgS7DZI3iUk= -cloud.o-forge.io/core/oc-lib v0.0.0-20250108155542-0f4adeea86be/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo= -cloud.o-forge.io/core/oc-lib v0.0.0-20250603080047-03dea551315b h1:yfXDZ0Pw5xTWstsbZWS+MV7G3ZTSvOCTwWQJWRn4Z5k= -cloud.o-forge.io/core/oc-lib v0.0.0-20250603080047-03dea551315b/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE= -cloud.o-forge.io/core/oc-lib v0.0.0-20250604083300-387785b40cb0 h1:iEm/Rf9I0OSCcncuFy61YOSZ3jdRlhJ/oLD97Pc2pCQ= -cloud.o-forge.io/core/oc-lib v0.0.0-20250604083300-387785b40cb0/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE= -cloud.o-forge.io/core/oc-lib v0.0.0-20250704084459-443546027b27 h1:iogk6pV3gybzQDBXMI6Qd/jvSA1h+3oRE+vLl1MRjew= -cloud.o-forge.io/core/oc-lib v0.0.0-20250704084459-443546027b27/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260126120055-055e6c70cdd7 h1:LAK86efqe2HNV1Tkym1TpvzL1Xsj3F0ClsK/snfejD0= -cloud.o-forge.io/core/oc-lib v0.0.0-20260126120055-055e6c70cdd7/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260127143728-3c052bf16572 h1:jrUHgs4DqNWLnLcb5nd4lrJim77+aGkJFACUfMogiu8= -cloud.o-forge.io/core/oc-lib v0.0.0-20260127143728-3c052bf16572/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128140632-d098d253d8e2 h1:B3TO9nXdpGuPXL4X3QFrRMJ1C4zXCQlLh4XR9aSZoKg= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128140632-d098d253d8e2/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128140807-1c9d7b63c0b3 h1:zAT4ZulAaX+l28QdCMvuXh5XQxn+fU8x6YNJ1zmA7+Q= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128140807-1c9d7b63c0b3/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128152242-743f4a6ff742 h1:vGlUqBhj3G5hvskL1NzfecKCUMH8bL3xx7JkLpv/04M= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128152242-743f4a6ff742/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128152919-7911cf29def8 h1:HT1+PP04wu5DcQ5PA3LtSJ5PcWEyL4FlZB62+v9eLWo= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128152919-7911cf29def8/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128154447-d26789d64e33 h1:WdmHeRtEWV3RsXaEe4HnItGNYLFvMNFggfq9/KtPho0= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128154447-d26789d64e33/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128160440-c0d89ea9e1e8 h1:h7VHJktaTT8TxO4ld3Xjw3LzMsivr3m7mzbNxb44zes= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128160440-c0d89ea9e1e8/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128162702-97cf629e27ec h1:/uvrtEt7A5rwqFPHH8yjujlC33HMjQHhWDIK6I08DrA= -cloud.o-forge.io/core/oc-lib v0.0.0-20260128162702-97cf629e27ec/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260129121215-c1519f6b26b8 h1:gvUbTwHnYM0Ezzvoa9ylTt+o1lAhS0U79OogbsZ+Pl8= -cloud.o-forge.io/core/oc-lib v0.0.0-20260129121215-c1519f6b26b8/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260129122033-186ba3e689c7 h1:NRFGRqN+j5g3DrtXMYN5T5XSYICG+OU2DisjBdID3j8= -cloud.o-forge.io/core/oc-lib v0.0.0-20260129122033-186ba3e689c7/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260203074447-30e6c9a6183c h1:c19lIseiUk5Hp+06EowfEbMWH1pK8AC/hvQ4ryWgJtY= -cloud.o-forge.io/core/oc-lib v0.0.0-20260203074447-30e6c9a6183c/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= -cloud.o-forge.io/core/oc-lib v0.0.0-20260203150123-4258f6b58083 h1:nKiU4AfeX+axS4HkaX8i2PJyhSFfRJvzT+CgIv6Jl2o= -cloud.o-forge.io/core/oc-lib v0.0.0-20260203150123-4258f6b58083/go.mod h1:T0UCxRd8w+qCVVC0NEyDiWIGC5ADwEbQ7hFcvftd4Ks= -cloud.o-forge.io/core/oc-lib v0.0.0-20260203150531-ef916fe2d995 h1:ZDRvnzTTNHgMm5hYmseHdEPqQ6rn/4v+P9f/JIxPaNw= -cloud.o-forge.io/core/oc-lib v0.0.0-20260203150531-ef916fe2d995/go.mod h1:T0UCxRd8w+qCVVC0NEyDiWIGC5ADwEbQ7hFcvftd4Ks= -cloud.o-forge.io/core/oc-lib v0.0.0-20260205131048-425cd2a9ba2f h1:Ku6u+SeoNXHMBzckekGyXCHLDJPh20Y8GayO6fXEcZE= -cloud.o-forge.io/core/oc-lib v0.0.0-20260205131048-425cd2a9ba2f/go.mod h1:T0UCxRd8w+qCVVC0NEyDiWIGC5ADwEbQ7hFcvftd4Ks= -cloud.o-forge.io/core/oc-lib v0.0.0-20260205131630-342451db2581 h1:V9eANWFEkoEPg3nWCvYXnLYbKDdAm3/Y7uCw1nt22Cc= -cloud.o-forge.io/core/oc-lib v0.0.0-20260205131630-342451db2581/go.mod h1:T0UCxRd8w+qCVVC0NEyDiWIGC5ADwEbQ7hFcvftd4Ks= -cloud.o-forge.io/core/oc-lib v0.0.0-20260209090340-c2aa2fedaa02 h1:sPVOuXArsUhtBecqyu8PB+/UJUsLHJfzX8tkFtkGbTs= -cloud.o-forge.io/core/oc-lib v0.0.0-20260209090340-c2aa2fedaa02/go.mod h1:T0UCxRd8w+qCVVC0NEyDiWIGC5ADwEbQ7hFcvftd4Ks= -cloud.o-forge.io/core/oc-lib v0.0.0-20260209095010-bafeee0d0590 h1:SPw0rHNwgSKtcvzUSCz97zV11iKO8bDBCAokqkTMpvw= -cloud.o-forge.io/core/oc-lib v0.0.0-20260209095010-bafeee0d0590/go.mod h1:T0UCxRd8w+qCVVC0NEyDiWIGC5ADwEbQ7hFcvftd4Ks= -cloud.o-forge.io/core/oc-lib v0.0.0-20260209095536-b767afb30168 h1:HHmfg0ktsJ5aTIXjmMhY8s6Pxb3F94OuVPQl+vhQ5Xs= -cloud.o-forge.io/core/oc-lib v0.0.0-20260209095536-b767afb30168/go.mod h1:T0UCxRd8w+qCVVC0NEyDiWIGC5ADwEbQ7hFcvftd4Ks= -cloud.o-forge.io/core/oc-lib v0.0.0-20260209113703-b9c9b6678099 h1:HczicbRtjiU51McjpDkmCsrQVs406bHybbLd+ZkqTo0= -cloud.o-forge.io/core/oc-lib v0.0.0-20260209113703-b9c9b6678099/go.mod h1:jmyBwmsac/4V7XPL347qawF60JsBCDmNAMfn/ySXKYo= -cloud.o-forge.io/core/oc-lib v0.0.0-20260212123952-403913d8cf13 h1:DNIPQ7C+7wjbj5RUx29wLxuIe/wiSOcuUMlLRIv6Fvs= -cloud.o-forge.io/core/oc-lib v0.0.0-20260212123952-403913d8cf13/go.mod h1:jmyBwmsac/4V7XPL347qawF60JsBCDmNAMfn/ySXKYo= -cloud.o-forge.io/core/oc-lib v0.0.0-20260218132556-0b41e2505e2f h1:OFuJhi23D/UNwn8Jo30HDt/Sm2Ea1ljUk6IVicYSuAQ= -cloud.o-forge.io/core/oc-lib v0.0.0-20260218132556-0b41e2505e2f/go.mod h1:jmyBwmsac/4V7XPL347qawF60JsBCDmNAMfn/ySXKYo= +cloud.o-forge.io/core/oc-lib v0.0.0-20260224130821-ce8ef70516f7 h1:p9uJjMY+QkE4neA+xRmIRtAm9us94EKZqgajDdLOd0Y= +cloud.o-forge.io/core/oc-lib v0.0.0-20260224130821-ce8ef70516f7/go.mod h1:+ENuvBfZdESSvecoqGY/wSvRlT3vinEolxKgwbOhUpA= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= -github.com/beego/beego v1.12.13 h1:g39O1LGLTiPejWVqQKK/TFGrroW9BCZQz6/pf4S8IRM= -github.com/beego/beego v1.12.13/go.mod h1:QURFL1HldOcCZAxnc1cZ7wrplsYR5dKPHFjmk6WkLAs= -github.com/beego/beego/v2 v2.3.1 h1:7MUKMpJYzOXtCUsTEoXOxsDV/UcHw6CPbaWMlthVNsc= -github.com/beego/beego/v2 v2.3.1/go.mod h1:5cqHsOHJIxkq44tBpRvtDe59GuVRVv/9/tyVDxd5ce4= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/beego/beego/v2 v2.3.8 h1:wplhB1pF4TxR+2SS4PUej8eDoH4xGfxuHfS7wAk9VBc= github.com/beego/beego/v2 v2.3.8/go.mod h1:8vl9+RrXqvodrl9C8yivX1e6le6deCK6RWeq8R7gTTg= -github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ= -github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/biter777/countries v1.7.5 h1:MJ+n3+rSxWQdqVJU8eBy9RqcdH6ePPn4PJHocVWUa+Q= github.com/biter777/countries v1.7.5/go.mod h1:1HSpZ526mYqKJcpT5Ti1kcGQ0L0SrXWIaptUWjFfv2E= -github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= -github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/couchbase/go-couchbase v0.0.0-20201216133707-c04035124b17/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A= -github.com/couchbase/gomemcached v0.1.2-0.20201224031647-c432ccf49f32/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo= -github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= +github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= +github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/dunglas/httpsfv v1.1.0 h1:Jw76nAyKWKZKFrpMMcL76y35tOpYHqQPzHQiwDvpe54= github.com/dunglas/httpsfv v1.1.0/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg= -github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI= -github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw= github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= +github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/etcd-io/etcd v3.3.17+incompatible/go.mod h1:cdZ77EstHBwVtD6iTgzgvogwcjo9m4iOqoijouPJ4bs= github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9gd6MPfXbKVU= github.com/filecoin-project/go-clock v0.1.0/go.mod h1:4uB/O4PvOjlx1VCMdZ9MyDZXRm//gkj1ELEbxfI1AZs= github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= -github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= -github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= -github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= -github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= -github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= -github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= -github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/goraz/onion v0.1.3 h1:KhyvbDA2b70gcz/d5izfwTiOH8SmrvV43AsVzpng3n0= github.com/goraz/onion v0.1.3/go.mod h1:XEmz1XoBz+wxTgWB8NwuvRm4RAu3vKxvrmYtzK+XCuQ= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/ipfs/boxo v0.35.2 h1:0QZJJh6qrak28abENOi5OA8NjBnZM4p52SxeuIDqNf8= github.com/ipfs/boxo v0.35.2/go.mod h1:bZn02OFWwJtY8dDW9XLHaki59EC5o+TGDECXEbe1w8U= +github.com/ipfs/go-block-format v0.2.3 h1:mpCuDaNXJ4wrBJLrtEaGFGXkferrw5eqVvzaHhtFKQk= +github.com/ipfs/go-block-format v0.2.3/go.mod h1:WJaQmPAKhD3LspLixqlqNFxiZ3BZ3xgqxxoSR/76pnA= github.com/ipfs/go-cid v0.6.0 h1:DlOReBV1xhHBhhfy/gBNNTSyfOM6rLiIx9J7A4DGf30= github.com/ipfs/go-cid v0.6.0/go.mod h1:NC4kS1LZjzfhK40UGmpXv5/qD2kcMzACYJNntCUiDhQ= github.com/ipfs/go-datastore v0.9.0 h1:WocriPOayqalEsueHv6SdD4nPVl4rYMfYGLD4bqCZ+w= github.com/ipfs/go-datastore v0.9.0/go.mod h1:uT77w/XEGrvJWwHgdrMr8bqCN6ZTW9gzmi+3uK+ouHg= +github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-log/v2 v2.9.1 h1:3JXwHWU31dsCpvQ+7asz6/QsFJHqFr4gLgQ0FWteujk= github.com/ipfs/go-log/v2 v2.9.1/go.mod h1:evFx7sBiohUN3AG12mXlZBw5hacBQld3ZPHrowlJYoo= +github.com/ipfs/go-test v0.2.3 h1:Z/jXNAReQFtCYyn7bsv/ZqUwS6E7iIcSpJ2CuzCvnrc= +github.com/ipfs/go-test v0.2.3/go.mod h1:QW8vSKkwYvWFwIZQLGQXdkt9Ud76eQXRQ9Ao2H+cA1o= github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.6 h1:Jb0h04599eq/CY7rB5YEqPS83HmRfHP2azkxMN2rFtU= github.com/koron/go-ssdp v0.0.6/go.mod h1:0R9LfRJGek1zWTjN3JUNlm5INCDYGpRDfAptnct63fI= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -232,10 +143,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= @@ -256,6 +165,8 @@ github.com/libp2p/go-libp2p-record v0.3.1 h1:cly48Xi5GjNw5Wq+7gmjfBiG9HCzQVkiZOU github.com/libp2p/go-libp2p-record v0.3.1/go.mod h1:T8itUkLcWQLCYMqtX7Th6r7SexyUJpIyPgks757td/E= github.com/libp2p/go-libp2p-routing-helpers v0.7.5 h1:HdwZj9NKovMx0vqq6YNPTh6aaNzey5zHD7HeLJtq6fI= github.com/libp2p/go-libp2p-routing-helpers v0.7.5/go.mod h1:3YaxrwP0OBPDD7my3D0KxfR89FlcX/IEbxDEDfAmj98= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= github.com/libp2p/go-netroute v0.4.0 h1:sZZx9hyANYUx9PZyqcgE/E1GUG3iEtTZHUEvdtXT7/Q= @@ -265,9 +176,12 @@ github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8S github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfYRSg= github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/marcopolo/simnet v0.0.4 h1:50Kx4hS9kFGSRIbrt9xUS3NJX33EyPqHVmpXvaKLqrY= +github.com/marcopolo/simnet v0.0.4/go.mod h1:tfQF1u2DmaB6WHODMtQaLtClEf3a296CKQLq5gAsIS0= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= @@ -275,10 +189,9 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= @@ -292,9 +205,13 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -324,29 +241,21 @@ github.com/multiformats/go-varint v0.1.0 h1:i2wqFp4sdl3IcIxfAonHQV9qU5OsZ4Ts9IOo github.com/multiformats/go-varint v0.1.0/go.mod h1:5KVAVXegtfmNQQm/lCY+ATvDzvJJhSkUlGQV9wgObdI= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE= -github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nats.go v1.43.0 h1:uRFZ2FEoRvP64+UUhaTokyS18XBCR/xM2vQZKO4i8ug= github.com/nats-io/nats.go v1.43.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g= -github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= -github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0= github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= -github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= -github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= @@ -389,46 +298,17 @@ github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54= github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.57.0 h1:Ro/rKjwdq9mZn1K5QPctzh+MA4Lp0BuYk5ZZEVhoNcY= -github.com/prometheus/common v0.57.0/go.mod h1:7uRPFSUTbfZWsJ7MHY56sqt7hLQu3bxXHDnNhl8E9qI= -github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4= -github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= @@ -437,28 +317,15 @@ github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SA github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/quic-go/webtransport-go v0.10.0 h1:LqXXPOXuETY5Xe8ITdGisBzTYmUOy5eSj+9n4hLTjHI= github.com/quic-go/webtransport-go v0.10.0/go.mod h1:LeGIXr5BQKE3UsynwVBeQrU1TPrbh73MGoC6jd+V7ow= -github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= -github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= -github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= -github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg= github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 h1:v9ezJDHA1XGxViAUSIoO/Id7Fl63u6d0YmsAm+/p2hs= github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02/go.mod h1:RF16/A3L0xSa0oSERcnhd8Pu3IXSDZSK2gmGIMsttFE= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= -github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s= -github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/skarademir/naturalsort v0.0.0-20150715044055-69a5d87bef62/go.mod h1:oIdVclZaltY1Nf7OQUkg1/2jImBJ+ZfKZuDIRSwk3p0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= @@ -468,37 +335,32 @@ github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hg github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= -github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= -github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/wendal/errors v0.0.0-20181209125328-7f31f4b264ec/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= @@ -510,11 +372,6 @@ github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfS github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= -go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4l8= -go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= -go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ= -go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw= go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= @@ -529,6 +386,8 @@ go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -537,25 +396,19 @@ go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= @@ -568,11 +421,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -584,43 +434,22 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= +golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -634,15 +463,10 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 h1:O1cMQHRfwNpDfDJerqRoE2oD+AFlyid87D40L/OkkJo= golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -650,6 +474,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -658,12 +484,6 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= @@ -684,38 +504,42 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.35.1 h1:0PO/1FhlK/EQNVK5+txc4FuhQibV25VLSdLMmGpDE/Q= +k8s.io/api v0.35.1/go.mod h1:28uR9xlXWml9eT0uaGo6y71xK86JBELShLy4wR1XtxM= +k8s.io/apimachinery v0.35.1 h1:yxO6gV555P1YV0SANtnTjXYfiivaTPvCTKX6w6qdDsU= +k8s.io/apimachinery v0.35.1/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/client-go v0.35.1 h1:+eSfZHwuo/I19PaSxqumjqZ9l5XiTEKbIaJ+j1wLcLM= +k8s.io/client-go v0.35.1/go.mod h1:1p1KxDt3a0ruRfc/pG4qT/3oHmUj1AhSHEcxNSGg+OA= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=