From 5a568e2d7fdafc5754d1a14eb33dd354372a6da1 Mon Sep 17 00:00:00 2001 From: Nikolay Petrov Date: Sat, 14 Feb 2026 15:28:24 -0500 Subject: [PATCH 1/6] independent client conns --- server/control/clients.go | 120 +++++++++++++++++++++++++------------- server/control/store.go | 11 ++-- 2 files changed, 83 insertions(+), 48 deletions(-) diff --git a/server/control/clients.go b/server/control/clients.go index 30a81ea..d58f442 100644 --- a/server/control/clients.go +++ b/server/control/clients.go @@ -17,6 +17,7 @@ import ( "github.com/connet-dev/connet/model" "github.com/connet-dev/connet/pkg/iterc" "github.com/connet-dev/connet/pkg/logc" + "github.com/connet-dev/connet/pkg/netc" "github.com/connet-dev/connet/pkg/quicc" "github.com/connet-dev/connet/pkg/reliable" "github.com/connet-dev/connet/pkg/slogc" @@ -69,9 +70,11 @@ func newClientServer( return nil, fmt.Errorf("client snapshot: %w", err) } - reactivate := map[ClientConnKey][]ClientPeerKey{} + reactivate := map[ClientID]reactivateValue{} for _, msg := range connsMsgs { - reactivate[msg.Key] = []ClientPeerKey{} + v := reactivate[msg.Key.ID] + v.conns = append(v.conns, msg.Key) + reactivate[msg.Key.ID] = v } peersMsgs, peersOffset, err := peers.Snapshot() @@ -79,16 +82,17 @@ func newClientServer( return nil, fmt.Errorf("client peers snapshot: %w", err) } - peersCache := map[cacheKey][]*pbclient.RemotePeer{} + peersCache := map[cacheKey][]cachePeer{} for _, msg := range peersMsgs { - if reactivePeers, ok := reactivate[ClientConnKey{msg.Key.ID}]; ok { + if reactivePeers, ok := reactivate[msg.Key.ID]; ok { key := cacheKey{msg.Key.Endpoint, msg.Key.Role} - peersCache[key] = append(peersCache[key], &pbclient.RemotePeer{ + peersCache[key] = append(peersCache[key], cachePeer{msg.Key.ConnID, msg.Offset, &pbclient.RemotePeer{ Id: msg.Key.ID.string, Metadata: msg.Value.Metadata, Peer: msg.Value.Peer, - }) - reactivate[ClientConnKey{msg.Key.ID}] = append(reactivePeers, msg.Key) + }}) + reactivePeers.peers = append(reactivePeers.peers, msg.Key) + reactivate[msg.Key.ID] = reactivePeers } else { logger.Warn("peer without corresponding client, deleting", "endpoint", msg.Key.Endpoint, "role", msg.Key.Role, "id", msg.Key.ID) if err := peers.Del(msg.Key); err != nil { @@ -154,35 +158,51 @@ type clientServer struct { conns logc.KV[ClientConnKey, ClientConnValue] peers logc.KV[ClientPeerKey, ClientPeerValue] - peersCache map[cacheKey][]*pbclient.RemotePeer + peersCache map[cacheKey][]cachePeer peersOffset int64 peersMu sync.RWMutex - reactivate map[ClientConnKey][]ClientPeerKey + reactivate map[ClientID]reactivateValue reactivateMu sync.RWMutex } -func (s *clientServer) connected(id ClientID, auth ClientAuthentication, remote net.Addr, metadata string) error { +type cacheKey struct { + endpoint model.Endpoint + role model.Role +} + +type cachePeer struct { + connID ConnID + offset int64 + peer *pbclient.RemotePeer +} + +type reactivateValue struct { + conns []ClientConnKey + peers []ClientPeerKey +} + +func (s *clientServer) connected(id ClientID, connID ConnID, auth ClientAuthentication, remote net.Addr, metadata string) error { s.reactivateMu.Lock() - delete(s.reactivate, ClientConnKey{id}) + delete(s.reactivate, id) s.reactivateMu.Unlock() - return s.conns.Put(ClientConnKey{id}, ClientConnValue{auth, remote.String(), metadata}) + return s.conns.Put(ClientConnKey{id, connID}, ClientConnValue{auth, remote.String(), metadata}) } -func (s *clientServer) disconnected(id ClientID) error { - return s.conns.Del(ClientConnKey{id}) +func (s *clientServer) disconnected(id ClientID, connID ConnID) error { + return s.conns.Del(ClientConnKey{id, connID}) } -func (s *clientServer) announce(endpoint model.Endpoint, role model.Role, id ClientID, metadata string, peer *pbclient.Peer) error { - return s.peers.Put(ClientPeerKey{endpoint, role, id}, ClientPeerValue{peer, metadata}) +func (s *clientServer) announce(endpoint model.Endpoint, role model.Role, id ClientID, connID ConnID, metadata string, peer *pbclient.Peer) error { + return s.peers.Put(ClientPeerKey{endpoint, role, id, connID}, ClientPeerValue{peer, metadata}) } -func (s *clientServer) revoke(endpoint model.Endpoint, role model.Role, id ClientID) error { - return s.peers.Del(ClientPeerKey{endpoint, role, id}) +func (s *clientServer) revoke(endpoint model.Endpoint, role model.Role, id ClientID, connID ConnID) error { + return s.peers.Del(ClientPeerKey{endpoint, role, id, connID}) } -func (s *clientServer) cachedPeers(endpoint model.Endpoint, role model.Role) ([]*pbclient.RemotePeer, int64) { +func (s *clientServer) cachedPeers(endpoint model.Endpoint, role model.Role) ([]cachePeer, int64) { s.peersMu.RLock() defer s.peersMu.RUnlock() @@ -191,7 +211,15 @@ func (s *clientServer) cachedPeers(endpoint model.Endpoint, role model.Role) ([] func (s *clientServer) listen(ctx context.Context, endpoint model.Endpoint, role model.Role, notify func(peers []*pbclient.RemotePeer) error) error { peers, offset := s.cachedPeers(endpoint, role) - if err := notify(peers); err != nil { + doNotify := func() error { + uniquePeers := map[string]*pbclient.RemotePeer{} + for _, peer := range peers { + uniquePeers[peer.peer.Id] = peer.peer + } + + return notify(slices.Collect(maps.Values(uniquePeers))) + } + if err := doNotify(); err != nil { return err } @@ -208,16 +236,18 @@ func (s *clientServer) listen(ctx context.Context, endpoint model.Endpoint, role } if msg.Delete { - peers = slices.DeleteFunc(peers, func(peer *pbclient.RemotePeer) bool { - return peer.Id == msg.Key.ID.string + peers = slices.DeleteFunc(peers, func(peer cachePeer) bool { + return peer.peer.Id == msg.Key.ID.string && peer.connID == msg.Key.ConnID }) } else { - npeer := &pbclient.RemotePeer{ + npeer := cachePeer{msg.Key.ConnID, msg.Offset, &pbclient.RemotePeer{ Id: msg.Key.ID.string, Metadata: msg.Value.Metadata, Peer: msg.Value.Peer, - } - idx := slices.IndexFunc(peers, func(peer *pbclient.RemotePeer) bool { return peer.Id == msg.Key.ID.string }) + }} + idx := slices.IndexFunc(peers, func(peer cachePeer) bool { + return peer.peer.Id == msg.Key.ID.string && peer.connID == msg.Key.ConnID + }) if idx >= 0 { peers[idx] = npeer } else { @@ -230,7 +260,7 @@ func (s *clientServer) listen(ctx context.Context, endpoint model.Endpoint, role offset = nextOffset if changed { - if err := notify(peers); err != nil { + if err := doNotify(); err != nil { return err } } @@ -323,8 +353,8 @@ func (s *clientServer) runPeerCache(ctx context.Context) error { key := cacheKey{msg.Key.Endpoint, msg.Key.Role} peers := s.peersCache[key] if msg.Delete { - peers = slices.DeleteFunc(peers, func(peer *pbclient.RemotePeer) bool { - return peer.Id == msg.Key.ID.string + peers = slices.DeleteFunc(peers, func(peer cachePeer) bool { + return peer.peer.Id == msg.Key.ID.string && peer.connID == msg.Key.ConnID }) if len(peers) == 0 { delete(s.peersCache, key) @@ -332,12 +362,14 @@ func (s *clientServer) runPeerCache(ctx context.Context) error { s.peersCache[key] = peers } } else { - npeer := &pbclient.RemotePeer{ + npeer := cachePeer{msg.Key.ConnID, msg.Offset, &pbclient.RemotePeer{ Id: msg.Key.ID.string, Metadata: msg.Value.Metadata, Peer: msg.Value.Peer, - } - idx := slices.IndexFunc(peers, func(peer *pbclient.RemotePeer) bool { return peer.Id == msg.Key.ID.string }) + }} + idx := slices.IndexFunc(peers, func(peer cachePeer) bool { + return peer.peer.Id == msg.Key.ID.string && peer.connID == msg.Key.ConnID + }) if idx >= 0 { peers[idx] = npeer } else { @@ -384,13 +416,15 @@ func (s *clientServer) runCleaner(ctx context.Context) error { s.reactivateMu.Lock() defer s.reactivateMu.Unlock() - for key, peers := range s.reactivate { - s.logger.Warn("force disconnecting client", "id", key.ID) - if err := s.disconnected(key.ID); err != nil { - return err + for key, value := range s.reactivate { + s.logger.Warn("force disconnecting client", "id", key) + for _, conn := range value.conns { + if err := s.disconnected(conn.ID, conn.ConnID); err != nil { + return err + } } - for _, peer := range peers { - if err := s.revoke(peer.Endpoint, peer.Role, peer.ID); err != nil { + for _, peer := range value.peers { + if err := s.revoke(peer.Endpoint, peer.Role, peer.ID, peer.ConnID); err != nil { return err } } @@ -423,6 +457,7 @@ type clientConn struct { server *clientServer conn *quic.Conn logger *slog.Logger + connID ConnID clientConnAuth } @@ -460,16 +495,17 @@ func (c *clientConn) runErr(ctx context.Context) error { } else { c.clientConnAuth = *auth c.logger = c.logger.With("client-id", c.id) + c.connID = ConnID(netc.GenName()) } c.logger.Info("client connected", "addr", c.conn.RemoteAddr(), "metadata", c.metadata) defer c.logger.Info("client disconnected", "addr", c.conn.RemoteAddr(), "metadata", c.metadata) - if err := c.server.connected(c.id, c.auth, c.conn.RemoteAddr(), c.metadata); err != nil { + if err := c.server.connected(c.id, c.connID, c.auth, c.conn.RemoteAddr(), c.metadata); err != nil { return err } defer func() { - if err := c.server.disconnected(c.id); err != nil { + if err := c.server.disconnected(c.id, c.connID); err != nil { c.logger.Warn("failed to disconnect client", "id", c.id, "err", err) } }() @@ -622,11 +658,11 @@ func (s *clientStream) announce(ctx context.Context, req *pbclient.Request_Annou return err } - if err := s.conn.server.announce(endpoint, role, s.conn.id, s.conn.metadata, req.Peer); err != nil { + if err := s.conn.server.announce(endpoint, role, s.conn.id, s.conn.connID, s.conn.metadata, req.Peer); err != nil { return err } defer func() { - if err := s.conn.server.revoke(endpoint, role, s.conn.id); err != nil { + if err := s.conn.server.revoke(endpoint, role, s.conn.id, s.conn.connID); err != nil { s.conn.logger.Warn("failed to revoke client", "id", s.conn.id, "err", err) } }() @@ -655,7 +691,7 @@ func (s *clientStream) announce(ctx context.Context, req *pbclient.Request_Annou return err } - if err := s.conn.server.announce(endpoint, role, s.conn.id, s.conn.metadata, req.Announce.Peer); err != nil { + if err := s.conn.server.announce(endpoint, role, s.conn.id, s.conn.connID, s.conn.metadata, req.Announce.Peer); err != nil { return err } } diff --git a/server/control/store.go b/server/control/store.go index 0638047..00cbafd 100644 --- a/server/control/store.go +++ b/server/control/store.go @@ -73,8 +73,11 @@ type ConfigValue struct { Bytes []byte `json:"bytes,omitempty"` } +type ConnID string + type ClientConnKey struct { - ID ClientID `json:"id"` + ID ClientID `json:"id"` + ConnID ConnID `json:"conn_id"` } type ClientConnValue struct { @@ -87,6 +90,7 @@ type ClientPeerKey struct { Endpoint model.Endpoint `json:"endpoint"` Role model.Role `json:"role"` ID ClientID `json:"id"` // TODO consider using the server cert key or peer id + ConnID ConnID `json:"conn_id"` } type ClientPeerValue struct { @@ -94,11 +98,6 @@ type ClientPeerValue struct { Metadata string `json:"metadata"` } -type cacheKey struct { - endpoint model.Endpoint - role model.Role -} - type RelayConnKey struct { ID RelayID `json:"id"` } From d996fa64c45b10bd84bbed54c112e25ee5ee079c Mon Sep 17 00:00:00 2001 From: Nikolay Petrov Date: Sat, 14 Feb 2026 21:19:22 -0500 Subject: [PATCH 2/6] remove offset usage, slice order determines that --- server/control/clients.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/server/control/clients.go b/server/control/clients.go index d58f442..c45eebb 100644 --- a/server/control/clients.go +++ b/server/control/clients.go @@ -86,7 +86,7 @@ func newClientServer( for _, msg := range peersMsgs { if reactivePeers, ok := reactivate[msg.Key.ID]; ok { key := cacheKey{msg.Key.Endpoint, msg.Key.Role} - peersCache[key] = append(peersCache[key], cachePeer{msg.Key.ConnID, msg.Offset, &pbclient.RemotePeer{ + peersCache[key] = append(peersCache[key], cachePeer{msg.Key.ConnID, &pbclient.RemotePeer{ Id: msg.Key.ID.string, Metadata: msg.Value.Metadata, Peer: msg.Value.Peer, @@ -173,7 +173,6 @@ type cacheKey struct { type cachePeer struct { connID ConnID - offset int64 peer *pbclient.RemotePeer } @@ -240,7 +239,7 @@ func (s *clientServer) listen(ctx context.Context, endpoint model.Endpoint, role return peer.peer.Id == msg.Key.ID.string && peer.connID == msg.Key.ConnID }) } else { - npeer := cachePeer{msg.Key.ConnID, msg.Offset, &pbclient.RemotePeer{ + npeer := cachePeer{msg.Key.ConnID, &pbclient.RemotePeer{ Id: msg.Key.ID.string, Metadata: msg.Value.Metadata, Peer: msg.Value.Peer, @@ -362,7 +361,7 @@ func (s *clientServer) runPeerCache(ctx context.Context) error { s.peersCache[key] = peers } } else { - npeer := cachePeer{msg.Key.ConnID, msg.Offset, &pbclient.RemotePeer{ + npeer := cachePeer{msg.Key.ConnID, &pbclient.RemotePeer{ Id: msg.Key.ID.string, Metadata: msg.Value.Metadata, Peer: msg.Value.Peer, From a9339702c5f0d436131eb0b2f7ac96202eff94e5 Mon Sep 17 00:00:00 2001 From: Nikolay Petrov Date: Sat, 14 Feb 2026 21:33:29 -0500 Subject: [PATCH 3/6] Add connid status --- server/control/clients.go | 3 +-- server/control/conn_id.go | 24 ++++++++++++++++++++++++ server/control/server.go | 2 ++ server/control/store.go | 2 -- 4 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 server/control/conn_id.go diff --git a/server/control/clients.go b/server/control/clients.go index c45eebb..ac89774 100644 --- a/server/control/clients.go +++ b/server/control/clients.go @@ -17,7 +17,6 @@ import ( "github.com/connet-dev/connet/model" "github.com/connet-dev/connet/pkg/iterc" "github.com/connet-dev/connet/pkg/logc" - "github.com/connet-dev/connet/pkg/netc" "github.com/connet-dev/connet/pkg/quicc" "github.com/connet-dev/connet/pkg/reliable" "github.com/connet-dev/connet/pkg/slogc" @@ -494,7 +493,7 @@ func (c *clientConn) runErr(ctx context.Context) error { } else { c.clientConnAuth = *auth c.logger = c.logger.With("client-id", c.id) - c.connID = ConnID(netc.GenName()) + c.connID = NewConnID() } c.logger.Info("client connected", "addr", c.conn.RemoteAddr(), "metadata", c.metadata) diff --git a/server/control/conn_id.go b/server/control/conn_id.go new file mode 100644 index 0000000..b90e3e1 --- /dev/null +++ b/server/control/conn_id.go @@ -0,0 +1,24 @@ +package control + +import "github.com/connet-dev/connet/pkg/netc" + +type ConnID struct{ string } + +var ConnIDNil = ConnID{""} + +func NewConnID() ConnID { + return ConnID{netc.GenName()} +} + +func (k ConnID) String() string { + return k.string +} + +func (k ConnID) MarshalText() ([]byte, error) { + return []byte(k.string), nil +} + +func (k *ConnID) UnmarshalText(b []byte) error { + k.string = string(b) + return nil +} diff --git a/server/control/server.go b/server/control/server.go index ea32f64..7039af9 100644 --- a/server/control/server.go +++ b/server/control/server.go @@ -106,6 +106,7 @@ func (s *Server) getClients() (map[string]StatusClient, error) { for _, msg := range clientMsgs { clients[msg.Key.ID.string] = StatusClient{ ID: msg.Key.ID, + ConnID: msg.Key.ConnID, Address: msg.Value.Addr, Metadata: msg.Value.Metadata, } @@ -177,6 +178,7 @@ type StatusIngress struct { type StatusClient struct { ID ClientID `json:"id"` + ConnID ConnID `json:"conn_id"` Address string `json:"address"` Metadata string `json:"metadata"` } diff --git a/server/control/store.go b/server/control/store.go index 00cbafd..c347f69 100644 --- a/server/control/store.go +++ b/server/control/store.go @@ -73,8 +73,6 @@ type ConfigValue struct { Bytes []byte `json:"bytes,omitempty"` } -type ConnID string - type ClientConnKey struct { ID ClientID `json:"id"` ConnID ConnID `json:"conn_id"` From dcf3fec9dea8448c7ba9131bd2e9c69aa77d719c Mon Sep 17 00:00:00 2001 From: Nikolay Petrov Date: Sun, 15 Feb 2026 07:32:06 -0500 Subject: [PATCH 4/6] show endpoints fully --- server/control/server.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/server/control/server.go b/server/control/server.go index 7039af9..438fd7a 100644 --- a/server/control/server.go +++ b/server/control/server.go @@ -128,9 +128,9 @@ func (s *Server) getEndpoints() (map[string]StatusEndpoint, error) { switch msg.Key.Role { case model.Destination: - ep.Destinations = append(ep.Destinations, msg.Key.ID) + ep.Destinations = append(ep.Destinations, StatusEndpointRemote{msg.Key.ID, msg.Key.ConnID}) case model.Source: - ep.Sources = append(ep.Sources, msg.Key.ID) + ep.Sources = append(ep.Sources, StatusEndpointRemote{msg.Key.ID, msg.Key.ConnID}) default: return nil, fmt.Errorf("unknown role: %s", msg.Key.Role) } @@ -184,9 +184,14 @@ type StatusClient struct { } type StatusEndpoint struct { - Endpoint model.Endpoint `json:"endpoint"` - Destinations []ClientID `json:"destinations"` - Sources []ClientID `json:"sources"` + Endpoint model.Endpoint `json:"endpoint"` + Destinations []StatusEndpointRemote `json:"destinations"` + Sources []StatusEndpointRemote `json:"sources"` +} + +type StatusEndpointRemote struct { + ID ClientID `json:"id"` + ConnID ConnID `json:"conn_id"` } type StatusRelay struct { From 87b164304a0aba83cb5721f32b76688f2eb4b9f7 Mon Sep 17 00:00:00 2001 From: Nikolay Petrov Date: Sun, 15 Feb 2026 08:56:35 -0500 Subject: [PATCH 5/6] rename internals --- server/control/clients.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/server/control/clients.go b/server/control/clients.go index ac89774..6784d73 100644 --- a/server/control/clients.go +++ b/server/control/clients.go @@ -81,11 +81,11 @@ func newClientServer( return nil, fmt.Errorf("client peers snapshot: %w", err) } - peersCache := map[cacheKey][]cachePeer{} + peersCache := map[peerKey][]peerValue{} for _, msg := range peersMsgs { if reactivePeers, ok := reactivate[msg.Key.ID]; ok { - key := cacheKey{msg.Key.Endpoint, msg.Key.Role} - peersCache[key] = append(peersCache[key], cachePeer{msg.Key.ConnID, &pbclient.RemotePeer{ + key := peerKey{msg.Key.Endpoint, msg.Key.Role} + peersCache[key] = append(peersCache[key], peerValue{msg.Key.ConnID, &pbclient.RemotePeer{ Id: msg.Key.ID.string, Metadata: msg.Value.Metadata, Peer: msg.Value.Peer, @@ -157,7 +157,7 @@ type clientServer struct { conns logc.KV[ClientConnKey, ClientConnValue] peers logc.KV[ClientPeerKey, ClientPeerValue] - peersCache map[cacheKey][]cachePeer + peersCache map[peerKey][]peerValue peersOffset int64 peersMu sync.RWMutex @@ -165,12 +165,12 @@ type clientServer struct { reactivateMu sync.RWMutex } -type cacheKey struct { +type peerKey struct { endpoint model.Endpoint role model.Role } -type cachePeer struct { +type peerValue struct { connID ConnID peer *pbclient.RemotePeer } @@ -200,11 +200,11 @@ func (s *clientServer) revoke(endpoint model.Endpoint, role model.Role, id Clien return s.peers.Del(ClientPeerKey{endpoint, role, id, connID}) } -func (s *clientServer) cachedPeers(endpoint model.Endpoint, role model.Role) ([]cachePeer, int64) { +func (s *clientServer) cachedPeers(endpoint model.Endpoint, role model.Role) ([]peerValue, int64) { s.peersMu.RLock() defer s.peersMu.RUnlock() - return slices.Clone(s.peersCache[cacheKey{endpoint, role}]), s.peersOffset + return slices.Clone(s.peersCache[peerKey{endpoint, role}]), s.peersOffset } func (s *clientServer) listen(ctx context.Context, endpoint model.Endpoint, role model.Role, notify func(peers []*pbclient.RemotePeer) error) error { @@ -234,16 +234,16 @@ func (s *clientServer) listen(ctx context.Context, endpoint model.Endpoint, role } if msg.Delete { - peers = slices.DeleteFunc(peers, func(peer cachePeer) bool { + peers = slices.DeleteFunc(peers, func(peer peerValue) bool { return peer.peer.Id == msg.Key.ID.string && peer.connID == msg.Key.ConnID }) } else { - npeer := cachePeer{msg.Key.ConnID, &pbclient.RemotePeer{ + npeer := peerValue{msg.Key.ConnID, &pbclient.RemotePeer{ Id: msg.Key.ID.string, Metadata: msg.Value.Metadata, Peer: msg.Value.Peer, }} - idx := slices.IndexFunc(peers, func(peer cachePeer) bool { + idx := slices.IndexFunc(peers, func(peer peerValue) bool { return peer.peer.Id == msg.Key.ID.string && peer.connID == msg.Key.ConnID }) if idx >= 0 { @@ -348,10 +348,10 @@ func (s *clientServer) runPeerCache(ctx context.Context) error { s.peersMu.Lock() defer s.peersMu.Unlock() - key := cacheKey{msg.Key.Endpoint, msg.Key.Role} + key := peerKey{msg.Key.Endpoint, msg.Key.Role} peers := s.peersCache[key] if msg.Delete { - peers = slices.DeleteFunc(peers, func(peer cachePeer) bool { + peers = slices.DeleteFunc(peers, func(peer peerValue) bool { return peer.peer.Id == msg.Key.ID.string && peer.connID == msg.Key.ConnID }) if len(peers) == 0 { @@ -360,12 +360,12 @@ func (s *clientServer) runPeerCache(ctx context.Context) error { s.peersCache[key] = peers } } else { - npeer := cachePeer{msg.Key.ConnID, &pbclient.RemotePeer{ + npeer := peerValue{msg.Key.ConnID, &pbclient.RemotePeer{ Id: msg.Key.ID.string, Metadata: msg.Value.Metadata, Peer: msg.Value.Peer, }} - idx := slices.IndexFunc(peers, func(peer cachePeer) bool { + idx := slices.IndexFunc(peers, func(peer peerValue) bool { return peer.peer.Id == msg.Key.ID.string && peer.connID == msg.Key.ConnID }) if idx >= 0 { From 7adcd9038914b433f4a113e955fdc2fe315cc4cb Mon Sep 17 00:00:00 2001 From: Nikolay Petrov Date: Sun, 15 Feb 2026 09:23:46 -0500 Subject: [PATCH 6/6] timestamp names --- pkg/netc/name.go | 12 ++++++++++++ server/control/conn_id.go | 18 ++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/pkg/netc/name.go b/pkg/netc/name.go index 7e75fb9..d5d5125 100644 --- a/pkg/netc/name.go +++ b/pkg/netc/name.go @@ -3,9 +3,11 @@ package netc import ( "crypto/rand" "encoding/base32" + "encoding/binary" "fmt" "io" "strings" + "time" ) var DNSSECEncoding = base32.NewEncoding("0123456789abcdefghijklmnopqrstuv").WithPadding(base32.NoPadding) @@ -25,3 +27,13 @@ func GenName() string { } return DNSSECEncoding.EncodeToString(data) } + +func GenTimestampName() string { + data := make([]byte, 32) + if _, err := io.ReadFull(rand.Reader, data); err != nil { + panic(err) + } + nanos := time.Now().UnixNano() + binary.BigEndian.PutUint64(data, uint64(nanos)) + return DNSSECEncoding.EncodeToString(data) +} diff --git a/server/control/conn_id.go b/server/control/conn_id.go index b90e3e1..d941178 100644 --- a/server/control/conn_id.go +++ b/server/control/conn_id.go @@ -1,19 +1,33 @@ package control -import "github.com/connet-dev/connet/pkg/netc" +import ( + "encoding/binary" + "time" + + "github.com/connet-dev/connet/pkg/netc" +) type ConnID struct{ string } var ConnIDNil = ConnID{""} func NewConnID() ConnID { - return ConnID{netc.GenName()} + return ConnID{netc.GenTimestampName()} } func (k ConnID) String() string { return k.string } +func (k ConnID) Time() time.Time { + data, err := netc.DNSSECEncoding.DecodeString(k.string) + if err != nil { + return time.Time{} + } + nanos := binary.BigEndian.Uint64(data) + return time.Unix(0, int64(nanos)) +} + func (k ConnID) MarshalText() ([]byte, error) { return []byte(k.string), nil }