update chat; add persistent flag
Kim committed
Oct 22, 2025 at 11:51 UTC
c5348857d476e8051bf802176c16db7e19b6a52f
5 files changed
+197
-7
cmd/example_chat/main.go
+27
@@ -24,6 +24,7 @@ var (
24
flagServerURL string
25
flagPort int
26
flagName string
27
+ flagDataPath string
28
)
29
30
func init() {
@@ -31,6 +32,7 @@ func init() {
32
flags.StringVar(&flagServerURL, "server-url", "http://relaydns.gosuda.org", "relayserver base URL to auto-fetch multiaddrs from /health")
33
flags.IntVar(&flagPort, "port", 8091, "local chat HTTP port")
34
flags.StringVar(&flagName, "name", "example-chat", "backend display name")
35
+ flags.StringVar(&flagDataPath, "data-path", "", "optional directory to persist chat history via PebbleDB")
36
}
37
38
func main() {
@@ -49,6 +51,24 @@ func runChat(cmd *cobra.Command, args []string) error {
51
return fmt.Errorf("listen chat: %w", err)
52
}
53
hub := newHub()
54
+
55
+ // Optional: open persistent store and preload history
56
+ var store *messageStore
57
+ if flagDataPath != "" {
58
+ s, err := openMessageStore(flagDataPath)
59
+ if err != nil {
60
+ log.Warn().Err(err).Msg("[chat] open store failed; running in memory only")
61
+ } else {
62
+ store = s
63
+ if msgs, err := store.LoadAll(); err != nil {
64
+ log.Warn().Err(err).Msg("[chat] load history failed")
65
+ } else if len(msgs) > 0 {
66
+ hub.bootstrap(msgs)
67
+ log.Info().Msgf("[chat] loaded %d messages from store", len(msgs))
68
+ }
69
+ hub.attachStore(store)
70
+ }
71
+ }
72
srv := serveChatHTTP(ln, flagName, hub)
73
74
// 2) advertise over RelayDNS (HTTP tunneled via server /peer route)
@@ -91,6 +111,13 @@ func runChat(cmd *cobra.Command, args []string) error {
111
hub.closeAll()
112
hub.wait()
113
114
+ // 4. Close persistent store if opened
115
+ if store != nil {
116
+ if err := store.Close(); err != nil {
117
+ log.Warn().Err(err).Msg("[chat] store close error")
118
+ }
119
+ }
120
+
121
log.Info().Msg("[chat] shutdown complete")
122
return nil
123
}
cmd/example_chat/persist.go
new
+86
@@ -0,0 +1,86 @@
1
+package main
2
+
3
+import (
4
+ "encoding/binary"
5
+ "encoding/json"
6
+ "os"
7
+ "path/filepath"
8
+ "sync"
9
+
10
+ "github.com/cockroachdb/pebble"
11
+)
12
+
13
+// messageStore persists chat messages in a PebbleDB key-value store.
14
+// Keys are 8-byte big-endian sequence numbers increasing monotonically.
15
+type messageStore struct {
16
+ db *pebble.DB
17
+ mu sync.Mutex
18
+ next uint64
19
+}
20
+
21
+func openMessageStore(dir string) (*messageStore, error) {
22
+ if dir == "" {
23
+ return nil, nil
24
+ }
25
+ if err := os.MkdirAll(dir, 0o755); err != nil {
26
+ return nil, err
27
+ }
28
+ // Pebble database lives directly at the provided directory.
29
+ // Accept default options for simplicity.
30
+ db, err := pebble.Open(filepath.Clean(dir), &pebble.Options{})
31
+ if err != nil {
32
+ return nil, err
33
+ }
34
+ s := &messageStore{db: db}
35
+ // Discover next sequence by reading the last key.
36
+ it, err := db.NewIter(nil)
37
+ if err != nil {
38
+ return nil, err
39
+ }
40
+ defer it.Close()
41
+ if it.Last() {
42
+ if len(it.Key()) >= 8 {
43
+ s.next = binary.BigEndian.Uint64(it.Key()[:8]) + 1
44
+ }
45
+ }
46
+ return s, nil
47
+}
48
+
49
+func (s *messageStore) Append(m message) error {
50
+ if s == nil || s.db == nil {
51
+ return nil
52
+ }
53
+ s.mu.Lock()
54
+ defer s.mu.Unlock()
55
+ key := make([]byte, 8)
56
+ binary.BigEndian.PutUint64(key, s.next)
57
+ s.next++
58
+ val, _ := json.Marshal(m)
59
+ return s.db.Set(key, val, pebble.Sync)
60
+}
61
+
62
+func (s *messageStore) LoadAll() ([]message, error) {
63
+ if s == nil || s.db == nil {
64
+ return nil, nil
65
+ }
66
+ it, err := s.db.NewIter(nil)
67
+ if err != nil {
68
+ return nil, err
69
+ }
70
+ defer it.Close()
71
+ out := make([]message, 0, 256)
72
+ for it.First(); it.Valid(); it.Next() {
73
+ var m message
74
+ if err := json.Unmarshal(it.Value(), &m); err == nil {
75
+ out = append(out, m)
76
+ }
77
+ }
78
+ return out, nil
79
+}
80
+
81
+func (s *messageStore) Close() error {
82
+ if s == nil || s.db == nil {
83
+ return nil
84
+ }
85
+ return s.db.Close()
86
+}
cmd/example_chat/view.go
+43
-7
@@ -20,6 +20,7 @@ type hub struct {
20
conns map[*websocket.Conn]struct{}
21
names map[*websocket.Conn]string
22
wg sync.WaitGroup
23
+ store *messageStore
24
}
25
26
type message struct {
@@ -41,6 +42,11 @@ func (h *hub) broadcast(m message) {
42
conns = append(conns, c)
43
}
44
h.mu.Unlock()
45
+ if h.store != nil {
46
+ if err := h.store.Append(m); err != nil {
47
+ log.Debug().Err(err).Msg("persist message")
48
+ }
49
+ }
50
for _, c := range conns {
51
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
52
_ = wsjson.Write(ctx, c, m)
@@ -48,6 +54,20 @@ func (h *hub) broadcast(m message) {
54
}
55
}
56
57
+// attachStore connects a persistent store to the hub.
58
+func (h *hub) attachStore(s *messageStore) {
59
+ h.mu.Lock()
60
+ h.store = s
61
+ h.mu.Unlock()
62
+}
63
+
64
+// bootstrap preloads history into the in-memory buffer.
65
+func (h *hub) bootstrap(msgs []message) {
66
+ h.mu.Lock()
67
+ h.messages = append(h.messages, msgs...)
68
+ h.mu.Unlock()
69
+}
70
+
71
// closeAll force-closes all active websocket connections (used during shutdown).
72
func (h *hub) closeAll() {
73
h.mu.Lock()
@@ -80,9 +100,7 @@ func handleWS(w http.ResponseWriter, r *http.Request, h *hub) {
100
h.conns[conn] = struct{}{}
101
backlog := append([]message(nil), h.messages...)
102
h.mu.Unlock()
83
- if len(backlog) > 20 {
84
- backlog = backlog[len(backlog)-20:]
85
- }
103
+
104
for _, m := range backlog {
105
_ = wsjson.Write(connCtx, conn, m)
106
}
@@ -174,11 +192,13 @@ var indexTmpl = template.Must(template.New("chat").Parse(`<!DOCTYPE html>
192
--cursor: #22c55e;
193
}
194
*{ box-sizing: border-box }
195
+ /* Prefer locally-installed D2Coding for Korean monospaced rendering */
196
+ @font-face { font-family: 'D2Coding'; src: local('D2Coding'), local('D2Coding Ligature'), local('D2Coding Nerd'); font-display: swap; }
197
body { margin:0; padding:24px; background:var(--bg); color:var(--fg); font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial }
198
.wrap { max-width: 920px; margin: 0 auto }
199
h1 { margin:0 0 12px 0; font-weight:700 }
200
.term { border:1px solid var(--border); border-radius:10px; background:var(--panel); overflow:hidden }
181
- .termbar { display:flex; align-items:center; justify-content:space-between; padding:10px 12px; border-bottom:1px solid var(--border); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:14px }
201
+ .termbar { display:flex; align-items:center; justify-content:space-between; padding:10px 12px; border-bottom:1px solid var(--border); font-family: 'D2Coding', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:14px }
202
.dots { display:flex; gap:6px }
203
.dot { width:10px; height:10px; border-radius:50%; }
204
.dot.red{ background:#ef4444 }
@@ -187,14 +207,27 @@ var indexTmpl = template.Must(template.New("chat").Parse(`<!DOCTYPE html>
207
.nick { display:flex; align-items:center; gap:8px }
208
.nick input{ background:transparent; border:1px solid var(--border); color:var(--fg); padding:6px 8px; border-radius:6px; font-family:inherit; font-size:13px; width:180px }
209
.nick button{ background:transparent; border:1px solid var(--border); color:var(--fg); padding:6px 8px; border-radius:6px; font-family:inherit; font-size:13px; cursor:pointer }
190
- .screen { height:420px; overflow:auto; padding:14px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:14px; line-height:1.5; }
210
+ .screen { height:420px; overflow:auto; padding:14px; font-family: 'D2Coding', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:14px; line-height:1.5; }
211
.line { white-space: pre-wrap; word-break: break-word }
212
.ts { color:var(--muted) }
213
.usr { color:#60a5fa }
194
- .promptline { display:flex; align-items:center; gap:8px; padding:12px 14px; border-top:1px solid var(--border); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
214
+ .promptline { display:flex; align-items:center; gap:8px; padding:12px 14px; border-top:1px solid var(--border); font-family: 'D2Coding', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
215
#prompt { color:var(--accent) }
216
#cmd { flex:1; background:transparent; border:none; outline:none; color:var(--fg); font-family: inherit; font-size:14px; caret-color: var(--cursor) }
217
small{ color:var(--muted); display:block; margin-top:10px }
218
+
219
+ /* Mobile responsiveness */
220
+ @media (max-width: 640px) {
221
+ body { padding: 12px; }
222
+ .wrap { max-width: 100%; }
223
+ h1 { font-size: 18px; }
224
+ .termbar { flex-wrap: wrap; gap: 8px; }
225
+ .nick input { width: 100%; font-size: 16px; }
226
+ .nick button { font-size: 16px; }
227
+ .screen { height: 50vh; font-size: 13px; }
228
+ .promptline { flex-wrap: wrap; gap: 6px; }
229
+ #cmd { font-size: 16px; }
230
+ }
231
</style>
232
</head>
233
<body>
@@ -305,8 +338,11 @@ var indexTmpl = template.Must(template.New("chat").Parse(`<!DOCTYPE html>
338
ws.send(JSON.stringify(payload));
339
cmd.value='';
340
}
341
+ // Handle IME composition properly to avoid duplicated last character
342
+ // on Enter when using Korean/Japanese/Chinese input methods.
343
cmd.addEventListener('keydown', e => {
309
- if(e.key === 'Enter') { e.preventDefault(); send(); }
344
+ if (e.isComposing || e.keyCode === 229) { return; }
345
+ if (e.key === 'Enter') { e.preventDefault(); send(); }
346
});
347
// Focus command line on load
348
setTimeout(()=>cmd.focus(), 0);
go.mod
+13
@@ -4,6 +4,7 @@ go 1.25.0
4
5
require (
6
github.com/cockroachdb/crlib v0.0.0-20251001180057-2a49e1873587
7
+ github.com/cockroachdb/pebble v1.1.5
8
github.com/coder/websocket v1.8.14
9
github.com/libp2p/go-libp2p v0.44.0
10
github.com/libp2p/go-libp2p-pubsub v0.15.0
@@ -13,14 +14,22 @@ require (
14
)
15
16
require (
17
+ github.com/DataDog/zstd v1.4.5 // indirect
18
github.com/benbjohnson/clock v1.3.5 // indirect
19
github.com/beorn7/perks v1.0.1 // indirect
20
github.com/cespare/xxhash/v2 v2.3.0 // indirect
21
+ github.com/cockroachdb/errors v1.11.3 // indirect
22
+ github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
23
+ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
24
+ github.com/cockroachdb/redact v1.1.5 // indirect
25
+ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
26
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
27
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
28
github.com/flynn/noise v1.1.0 // indirect
29
github.com/francoispqt/gojay v1.2.13 // indirect
30
+ github.com/getsentry/sentry-go v0.27.0 // indirect
31
github.com/gogo/protobuf v1.3.2 // indirect
32
+ github.com/golang/snappy v0.0.4 // indirect
33
github.com/google/uuid v1.6.0 // indirect
34
github.com/gorilla/websocket v1.5.3 // indirect
35
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
@@ -32,6 +41,8 @@ require (
41
github.com/klauspost/compress v1.18.0 // indirect
42
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
43
github.com/koron/go-ssdp v0.0.6 // indirect
44
+ github.com/kr/pretty v0.3.1 // indirect
45
+ github.com/kr/text v0.2.0 // indirect
46
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
47
github.com/libp2p/go-flow-metrics v0.2.0 // indirect
48
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
@@ -77,6 +88,7 @@ require (
88
github.com/pion/transport/v3 v3.0.7 // indirect
89
github.com/pion/turn/v4 v4.0.2 // indirect
90
github.com/pion/webrtc/v4 v4.1.2 // indirect
91
+ github.com/pkg/errors v0.9.1 // indirect
92
github.com/prometheus/client_golang v1.22.0 // indirect
93
github.com/prometheus/client_model v0.6.2 // indirect
94
github.com/prometheus/common v0.64.0 // indirect
@@ -84,6 +96,7 @@ require (
96
github.com/quic-go/qpack v0.5.1 // indirect
97
github.com/quic-go/quic-go v0.55.0 // indirect
98
github.com/quic-go/webtransport-go v0.9.0 // indirect
99
+ github.com/rogpeppe/go-internal v1.10.0 // indirect
100
github.com/spaolacci/murmur3 v1.1.0 // indirect
101
github.com/spf13/pflag v1.0.9 // indirect
102
github.com/wlynxg/anet v0.0.5 // indirect
go.sum
+28
@@ -8,6 +8,8 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1
8
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
9
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
10
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
11
+github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
12
+github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
13
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
14
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
15
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@@ -21,11 +23,26 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
23
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
24
github.com/cockroachdb/crlib v0.0.0-20251001180057-2a49e1873587 h1:qjG2TrBrPbGRVYp5obcAi8OSsuFJ8s1AElDImHLV9tY=
25
github.com/cockroachdb/crlib v0.0.0-20251001180057-2a49e1873587/go.mod h1:ae57yNis2F1FThSNdPdoXfiPOVi8G1TLreCBQYPOdqo=
26
+github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
27
+github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
28
+github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
29
+github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
30
+github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
31
+github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
32
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
33
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
34
+github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw=
35
+github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo=
36
+github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
37
+github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
38
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
39
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
40
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
41
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
42
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
43
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
44
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
45
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
46
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
47
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
48
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -42,9 +59,13 @@ github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwU
59
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
60
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
61
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
62
+github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
63
+github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
64
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
65
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
66
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
67
+github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
68
+github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
69
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
70
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
71
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -55,6 +76,8 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
76
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
77
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
78
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
79
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
80
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
81
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
82
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
83
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
@@ -184,6 +207,8 @@ github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a
207
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
208
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
209
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
210
+github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
211
+github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
212
github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o=
213
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M=
214
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
@@ -226,7 +251,9 @@ github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps=
251
github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs=
252
github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54=
253
github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U=
254
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
255
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
256
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
257
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
258
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
259
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -248,6 +275,7 @@ github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9M
275
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
276
github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70=
277
github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao=
278
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
279
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
280
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
281
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=