{"id":19642054,"url":"https://github.com/mmadfox/go-wirenet","last_synced_at":"2026-06-13T17:31:46.469Z","repository":{"id":88182463,"uuid":"260121011","full_name":"mmadfox/go-wirenet","owner":"mmadfox","description":"Simple bidirectional stream server","archived":false,"fork":false,"pushed_at":"2020-08-27T06:47:52.000Z","size":188,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-26T23:16:40.388Z","etag":null,"topics":["bidirectional","bidirectional-server","nat-traversal","stream","stream-server","streams","tcp"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mmadfox.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-30T05:25:10.000Z","updated_at":"2023-01-01T17:00:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"d9206e85-01fc-4b22-81cc-f87515911059","html_url":"https://github.com/mmadfox/go-wirenet","commit_stats":null,"previous_names":["mediabuyerbot/go-wirenet"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/mmadfox/go-wirenet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmadfox%2Fgo-wirenet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmadfox%2Fgo-wirenet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmadfox%2Fgo-wirenet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmadfox%2Fgo-wirenet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mmadfox","download_url":"https://codeload.github.com/mmadfox/go-wirenet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmadfox%2Fgo-wirenet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34294408,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bidirectional","bidirectional-server","nat-traversal","stream","stream-server","streams","tcp"],"created_at":"2024-11-11T14:11:22.160Z","updated_at":"2026-06-13T17:31:46.452Z","avatar_url":"https://github.com/mmadfox.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-wirenet\nSimple  bidirectional TCP stream server. Useful for NAT traversal.\n---\n[![Coverage Status](https://coveralls.io/repos/github/mediabuyerbot/go-wirenet/badge.svg?branch=master\u00262)](https://coveralls.io/github/mediabuyerbot/go-wirenet?branch=master)\n[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs]\n[![Go Report Card](https://goreportcard.com/badge/mediabuyerbot/go-wirenet)](https://goreportcard.com/report/mediabuyerbot/go-wirenet)\n\n[godocs]: https://godoc.org/github.com/mediabuyerbot/go-wirenet \n\n## Design\n![Design](assets/design.jpg)\n##### Client-Server\n```\n// client \u003c-\u003e server\nclient1 join  to the server  ---------NAT------\u003e server\nclient2 join to the server   ---------NAT------\u003e server\nclient3 join to the server   ---------NAT------\u003e server\nclient4 join to the server   ---------NAT------\u003e server\n\n// call from the server\ncall client1 from the server ---------NAT------\u003e client1\ncall client 2 from the server --------NAT------\u003e client2  \ncall client 3 from the server --------NAT------\u003e client3  \ncall client 4 from the server --------NAT------\u003e client4\n\n// call from the client\ncall server from the client1 ---------NAT------\u003e server\ncall server from the client2 ---------NAT------\u003e server  \ncall server from the client3  --------NAT------\u003e server  \ncall server from the client4  --------NAT------\u003e server\n```\n##### Client-Hub\n```\n// clients \u003c-\u003e hub\nclient1 join to the hub  ---------NAT------\u003e hub\nclient2 join to the hub  ---------NAT------\u003e hub\nclient3 join to the hub  ---------NAT------\u003e hub\nclient4 join to the hub  ---------NAT------\u003e hub\n\n// call from the client\ncall client2 from the client1 ---------NAT------\u003e client2\ncall client1 from the client2 ---------NAT------\u003e client1 \ncall client2 from the client3  --------NAT------\u003e client2  \ncall client1 from the client4  --------NAT------\u003e client1\n```\n\n## Table of contents\n- [Installation](#installation)\n- [Examples](#examples)\n    + [Creating connection](#creating-connection)\n    + [Stream handling](#stream-handling)\n    + [Stream opening](#stream-opening)\n    + [Writing to stream](#writing-to-stream)\n    + [Reading from stream](#reading-from-stream)\n    + [Using authentication](#using-authentication)\n    + [Using SSL/TLS certs](#using-ssltls-certs)\n    + [Shutdown](#shutdown)\n    + [KeepAlive](#keepalive)\n    + [Hub mode](#hub-mode)\n- [Options](#options)    \n     \n### Installation\n```ssh\ngo get github.com/mediabuyerbot/go-wirenet\n```\n### Examples\n#### Creating connection \n```go\nimport \"github.com/mediabuyerbot/go-wirenet\"\n\n// make server side\nwire, err := wirenet.Mount(\":8989\", nil)\nif err != nil {\n    handleError(err)\n}\nif err := wire.Connect(); err != nil {\n    handleError(err)\n}\n\n// OR make client side \nwire, err := wirenet.Join(\":8989\", nil)\nif err != nil {\n    handleError(err)\n}\n\n// connection\nif err := wire.Connect(); err != nil {\n    handleError(err)\n}\n```\n\n#### Stream handling \n```go\nimport \"github.com/mediabuyerbot/go-wirenet\"\n\n// server side\nwire, err := wirenet.Mount(\":8989\", nil)\nif err != nil {\n    handleError(err)\n}\n// or client side\nwire, err := wirenet.Join(\":8989\", nil)\nif err != nil {\n    handleError(err)\n}\n\nbackupStream := func(ctx context.Context, stream wirenet.Stream) {\n    file, err := os.Open(\"/backup.log\")\n    ...\n    // write to stream\n    n, err := stream.ReadFrom(file)\n    ...\n    stream.Close()\n}\n\nopenChromeStream := func(ctx context.Context, stream wirenet.Stream) {\n      // read from stream\n      n, err := stream.WriteTo(os.Stdout)\n} \n\nwire.Stream(\"backup\", backupStream)\nwire.Stream(\"openChrome\", openChromeStream)\n\nif err := wire.Connect(); err != nil {\n    handleError(err)\n}\n```\n\n#### Stream opening \n```go\n// make options\nopts := []wirenet.Option{\n   wirenet.WithSessionOpenHook(func(session wirenet.Session) {\n   \t\t     hub.registerSession(session)\t\n   \t\t}),\n   \t\twirenet.WithSessionCloseHook(func(session wirenet.Session) {\n   \t\t\t hub.unregisterSession(session)\n        }),\n}\n// make client side\nwire, err := wirenet.Join(\":8989\", opts...)\n// OR make server side\nwire, err := wirenet.Mount(\":8989\", opts...)\n\n...\n\n// find an open session in some repository\nsess := hub.findSession(\"sessionID\")\nstream, err := sess.OpenStream(\"backup\")\nif err != nil {\n   handleError(err)\n}\ndefer stream.Close()\n \nbackup, err := os.Open(\"/backup.log\")\nif err != nil {\n   handleError(err)\n}\ndefer backup.Close()\n\n// write to stream\nn, err := stream.ReadFrom(backup)\n...\n```\n\n#### Writing to stream \n```go\nwire.Stream(\"account.set\", func(ctx context.Context, stream wirenet.Stream) {\n   // write to stream using writer \n   writer := stream.Writer()\n   for {\n      n, err := fileOne.Read(buf)\n      if err != nil {\n          handleError(err)\n          break\n      }\n   \t  n, err := writer.Write(buf[:n])\n      ...\n   }\n   // EOF frame\n   writer.Close()\n   \n   for {\n         n, err := fileTwo.Read(buf)\n         if err != nil {\n             handleError(err)\n             break\n         }\n      \t  n, err := writer.Write(buf[:n])\n         ...\n      }\n      // EOF frame\n      writer.Close() \n   ...\n\n   // or write to stream (recommended) \n   n, err := stream.ReadFrom(fileOne)\n   ...\n   n, err := stream.ReadFrom(fileTwo)\n})\n```\n\n#### Reading from stream \n```go\nwire.Stream(\"account.set\", func(ctx context.Context, stream wirenet.Stream) {\n   // reading from stream using reader \n   reader := stream.Reader()\n   buf := make([]byte, wirenet.BufSize)\n   n, err := reader.Read(buf)\n   // EOF frame\n   reader.Close()\n   ...\n\n   // or reader from stream (recommended)  \n   n, err := stream.WriteTo(file)\n   ...\n})\n```\n\n#### Using authentication\nserver\n```go\ntokenValidator := func(streamName string, id wirenet.Identification, token wirenet.Token) error {\n   if streamName == \"public\" {\n      return nil \n   }\n   return validate(token)\n}\n\nwire, err := wirenet.Mount(\":8989\", wirenet.WithTokenValidator(tokenValidator))\ngo func() {\n\tif err := wire.Connect(); err != nil {\n\t   handleError(err)\n    }\n}()\n\n\u003c-terminate()\nwire.Close()\n```\n\nclient\n```go\n token := wirenet.Token(\"token\")\n identification := wirenet.Identification(\"uuid\")\n wire, err := wirenet.Join(\":8989\",\n \t\twirenet.WithIdentification(identification, token),\n )\n if err := wire.Connect(); err != nil {\n    handleError(err)\n }\n```\n\n#### Using SSL/TLS certs\nserver\n```go\n// make keys \n// ./certs/server.key\n// ./certs/server.pem\ntlsConf, err := wirenet.LoadCertificates(\"server\", \"./certs\")\nif err != nil {\n\thandleError(err)\n}\nwire, err := wirenet.Mount(\":8989\", wirenet.WithTLS(tlsConf))\ngo func() {\n\tif err := wire.Connect(); err != nil {\n\t   handleError(err)\n    }\n}()\n\n\u003c-terminate()\nwire.Close()\n```\nclient\n```go\n// make keys \n// ./certs/client.key\n// ./certs/client.pem\ntlsConf, err := wirenet.LoadCertificates(\"client\", \"./certs\")\nif err != nil {\n\thandleError(err)\n}\nwire, err := wirenet.Mount(\":8989\", wirenet.WithTLS(tlsConf))\nif err := wire.Connect(); err != nil {\n    handleError(err)\n}\n```\n\n#### Shutdown \n```go\ntimeout := 120*time.Second \nwire, err := wirenet.Mount(\":8989\",\n    // Waiting time for completion of all streams\n    wirenet.WithSessionCloseTimeout(timeout),\n)\ngo func() {\n\tif err := wire.Connect(); err != nil {\n\t   handleError(err)\n    }\n}()\n\n\u003c-terminate()\n\nwire.Close()\n```\n\n#### KeepAlive\n```go\n// server side\nwire, err := wirenet.Mount(\":8989\",\n     WithKeepAlive(true),\n     WithKeepAliveInterval(30 * time.Second), \n)\n\n// OR client side\nwire, err := wirenet.Join(\":8989\",\n     WithKeepAlive(true),\n     WithKeepAliveInterval(30 * time.Second), \n)\n\ngo func() {\n\tif err := wire.Connect(); err != nil {\n\t   handleError(err)\n    }\n}()\n\n\u003c-terminate()\n\nwire.Close()\n```\n\n#### Hub mode\n##### Attention! The name of the stream for each client must be unique!\nhub\n```go\n hub, err := wirenet.Hub(\":8989\")\n hub.Connect()\n```\n\nclient1\n```go\nclient1, err := wirenet.Join(\":8989\")\nclient1.Stream(\"client1:readBalance\", func(ctx context.Context, s Stream) {})\ngo func() {\n   client1.Connect()\n}()\n...\nsess, err := client2.Session(\"uuid\")\nstream, err := sess.OpenStream(\"client2:readBalance\")\n\u003c-termiate()\nclient1.Close()\n```\n\nclient2\n```go\nclient2, err := wirenet.Join(\":8989\")\nclient2.Stream(\"client2:readBalance\", func(ctx context.Context, s Stream) {})\ngo func() {\n   client2.Connect()\n}()\n...\nsess, err := client2.Session(\"uuid\")\nstream, err := sess.OpenStream(\"client1:readBalance\")\n\u003c-termiate()\nclient2.Close()\n```\n\n#### Options\n```go\nwirenet.WithConnectHook(hook func(io.Closer)) Option\nwirenet.WithSessionOpenHook(hook wirenet.SessionHook) Option\nwirenet.WithSessionCloseHook(hook wirenet.SessionHook) Option\nwirenet.WithIdentification(id wirenet.Identification, token wirenet.Token) Option\nwirenet.WithTokenValidator(v wirenet.TokenValidator) Option                   // server side\nwirenet.WithTLS(conf *tls.Config) Option\nwirenet.WithRetryWait(min, max time.Duration) Option\nwirenet.WithRetryMax(n int) Option\nwirenet.WithReadWriteTimeouts(read, write time.Duration) Option\nwirenet.WithSessionCloseTimeout(dur time.Duration) Option\n```\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmmadfox%2Fgo-wirenet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmmadfox%2Fgo-wirenet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmmadfox%2Fgo-wirenet/lists"}