Feedback wanted: TCP pre-application authentication SDK for Go

Hi everyone,

I’m working on a Go library called libknock, and I’d like to get feedback on the API design and overall abstraction.

The project is an embeddable TCP pre-application authentication SDK for Go applications.

The basic idea is:

client
  -> TCP connect
  -> send a binary authentication frame
  -> application protocol starts

server
  -> accept TCP
  -> verify the authentication frame
  -> return a clean net.Conn
  -> application protocol starts

The server side exposes authenticated net.Listener wrappers. The client side exposes an authenticated net.Dialer-style API.

A minimal server looks like this:

ln, err := net.Listen("tcp", ":9000")
if err != nil {
    return err
}

ln = libknock.WrapListener(ln, knockCfg)

for {
    conn, err := ln.Accept()
    if err != nil {
        return err
    }

    go handleConn(conn)
}

A minimal client looks like this:

d := libknock.Dialer{
    Base: &net.Dialer{},
    Config: knockCfg,
}

conn, err := d.DialContext(ctx, "tcp", "example.com:9000")
if err != nil {
    return err
}

The library verifies a binary auth frame before the application protocol starts. After successful authentication, the caller receives a normal net.Conn.

The upper layer can be plain TCP, TLS, HTTP, gRPC, a custom binary protocol, or some long-lived agent connection. libknock does not parse or modify the application protocol payload.

The current scope is:

  • authenticated net.Listener

  • authenticated net.Dialer

  • binary auth frame

  • timestamp window checks

  • nonce replay protection

  • client secret resolution

  • clean net.Conn handoff

  • optional port knocking / firewall gate modes

  • optional relay mode for unmodified upstream binaries

It is not intended to replace TLS, mTLS, application authentication, authorization, or a VPN. The goal is only to add a small TCP-level admission step before the application protocol parser sees any input.

The main design questions I’d like feedback on are:

  1. Does the net.Listener / net.Dialer abstraction feel idiomatic for this kind of library?

  2. Should low-level functions like ServerAuth(ctx, conn, cfg) and ClientAuth(ctx, conn, cfg) be part of the stable public API, or should users be encouraged to use only WrapListener and Dialer?

  3. What is the best way to expose peer metadata after authentication without making the API awkward, especially when the connection is later wrapped by tls.Conn?

  4. Should optional port knocking / firewall gate support live in the same repository, or should it be separated from the core TCP pre-auth package?

  5. Are there any obvious pitfalls in wrapping net.Conn / net.Listener this way that I should account for before stabilizing the API?

Repository:

https://github.com/libknock/libknock

This is currently an early RC, so I’m more interested in API and design feedback than in promoting it as a stable package.