FlowDriver is a research transport that carries SOCKS5 traffic through Google Drive files. The Go lessons are virtual connections, binary envelopes, polling/flush loops, custom HTTP transport, and quota-aware design.

FlowDriver: a Go transport that treats Google Drive as a queue


FlowDriver is a research project that tunnels SOCKS5 traffic through Google Drive files. It is dual-use code. The repository itself warns against illegal or production use, and that warning belongs in any discussion of it.

For Go readers, the useful part is the transport design. FlowDriver turns a cloud storage folder into a message queue, wraps streams in binary envelopes, and exposes those streams as net.Conn-like virtual connections.

Client side: SOCKS5 into a virtual connection

The client uses a SOCKS5 server and supplies a custom dial function:

socks5.WithDial(func(ctx context.Context, network, addr string) (net.Conn, error) {
	session := transport.NewSession(sessionID)
	session.TargetAddr = addr
	engine.AddSession(session)
	return transport.NewVirtualConn(session, engine), nil
})

That is the key abstraction. The SOCKS5 library thinks it received a normal net.Conn. FlowDriver’s virtual connection turns reads and writes into session buffers that the transport engine can flush to Google Drive.

This is a useful Go pattern. If you can satisfy net.Conn, a lot of networking code becomes reusable even when the underlying transport is unusual.

Sessions hold transmit and receive queues

The session type stores per-connection state:

type Session struct {
	ID         string
	TargetAddr string
	Tx         [][]byte
	Rx         [][]byte
	// locks and lifecycle fields
}

Writes enqueue outbound chunks. Polling downloads inbound envelopes and appends them to the receive side. The virtual connection bridges that queue-based world back into the blocking Read/Write interface callers expect.

That boundary is the whole trick: streams at the API edge, batches in the storage layer.

Envelopes are binary, not JSON

The transport envelope includes session ID, sequence number, target address, and payload. It implements binary marshal/unmarshal methods:

type Envelope struct {
	SessionID  string
	Seq        uint64
	TargetAddr string
	Payload    []byte
}

func (e *Envelope) MarshalBinary() ([]byte, error)
func (e *Envelope) UnmarshalBinary(data []byte) (int, error)

Binary encoding is a reasonable choice here. These files are internal transport messages, not human-edited config. Smaller payloads mean fewer bytes through a quota-limited API.

The unmarshaler returns io.ErrUnexpectedEOF for truncated data. That matters because cloud-storage polling can encounter partially written or partially downloaded objects if the protocol is sloppy.

The engine is two loops: flush and poll

The transport engine owns active sessions and starts background loops:

func (e *Engine) Start(ctx context.Context) {
	go e.flushLoop(ctx)
	go e.pollLoop(ctx)
	go e.cleanupLoop(ctx)
}

The flush loop collects outbound session data and uploads message files. The poll loop lists/downloads files, decodes envelopes, and routes them back to sessions. A cleanup loop removes old files.

This is queue-worker Go: a mutex-protected session map, tickers controlled by context cancellation, and careful cleanup so the storage folder does not fill forever.

Server side opens the real TCP connection

The server creates the same engine in server mode and registers a callback for new sessions:

engine.OnNewSession = func(sessionID, targetAddr string, session *transport.Session) {
	go handleServerConn(sessionID, targetAddr, session, engine)
}

The handler dials the requested target:

conn, err := net.Dial("tcp", targetAddr)

Then it copies between the real TCP connection and the virtual session. The server is the bridge from storage-backed transport back to normal TCP.

The HTTP client can pin transport details

FlowDriver has an internal HTTP client package with a custom RoundTripper for host header rewriting and a custom DialContext path:

type hostRewriteTransport struct {
	Transport  http.RoundTripper
	HostHeader string
}

func (t *hostRewriteTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	req.Host = t.HostHeader
	return t.Transport.RoundTrip(req)
}

The config exposes fields such as target IP, SNI, and Host header. That is another sensitive part of the project, but technically it shows how Go lets you separate TLS server name, TCP destination, and HTTP Host behaviour when building custom transports.

Quotas shape the design

The README calls out Google Drive API quota pressure and warns against very low refresh rates. The config has refresh_rate_ms and flush_rate_ms because every poll and upload can become an API call.

That is an important lesson for any Go system built on third-party APIs: your concurrency and ticker settings are part of your correctness story. A transport that works in a local test can fail in practice by exhausting quota.

What to take from FlowDriver

The useful Go ideas are the virtual net.Conn, binary envelopes, session map, flush/poll loops, context-controlled goroutines, cleanup, and custom HTTP transport.

The security lesson is restraint. This is a research transport for constrained environments, not a production networking layer. If you study it, study the Go boundaries: stream interface at the edge, queue files underneath, and explicit rates around every external API call.