glm-5.1 9e807883de feat(core): rename Interface to StreamInterface, add MessageInterface, restructure ListenerConfig
Per ADR-035: split Interface trait into StreamInterface (stream-based, SSH/RawFraming)
and MessageInterface (request/response, HTTP/DNS). Remove TransportKind::Dns (DNS is
a MessageInterface). Change WebTransport { host } to { server_name: Option<String> }.
Restructure ListenerConfig from flat struct to enum with Stream/Http/Dns variants.
2026-06-09 10:26:04 +00:00
2026-06-05 10:04:32 +00:00
2026-06-05 10:04:32 +00:00

Alknet

Status: Alpha — This project is in early development. It depends on solid libraries (russh, tokio, iroh) for core functionality, but the glue code and integration between them has not been fully vetted for production use. Because alknet operates low in the network stack, bugs can cause serious problems downstream (leaked connections, broken tunnels, auth failures). Use with caution and report issues.

A self-hostable SSH-based tunnel tool that provides VPN-like functionality without being a VPN protocol.

What it does

  • Private tunneling — Route traffic to internal services (Postgres, Redis, APIs) over SSH
  • Censorship circumvention — SSH over TLS on port 443 is indistinguishable from HTTPS to DPI
  • NAT traversal — The iroh transport enables peer-to-peer connections without public IPs or port forwarding
  • Service mesh connectivity — Lightweight transport layer for event systems via reserved alknet-* destinations

The core insight: SSH tunnels work because SSH is fundamental infrastructure. Blocking it breaks the internet. Alknet makes SSH tunneling accessible through a simple CLI with pluggable transports.

Quick start

Build

cargo build --release

The default build includes TLS and iroh transports. To build a minimal binary with just TCP:

cargo build --release --no-default-features -p alknet

Server

# Generate a host key
ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N ""

# Start the server on port 22 (TCP)
alknet serve --key ssh_host_ed25519_key \
    --authorized-keys ~/.ssh/authorized_keys

# TLS with stealth mode (looks like nginx 404 to scanners)
alknet serve --key ssh_host_ed25519_key \
    --transport tls \
    --acme-domain example.com \
    --stealth

# iroh (no public IP needed)
alknet serve --key ssh_host_ed25519_key \
    --transport iroh

Client

# Connect via TCP and start a SOCKS5 proxy on 127.0.0.1:1080
alknet connect --server example.com:22 \
    --identity ~/.ssh/id_ed25519

# Connect via TLS
alknet connect --server example.com:443 \
    --transport tls \
    --identity ~/.ssh/id_ed25519

# Connect via iroh (peer-to-peer, no public IP)
alknet connect --peer <endpoint-id> \
    --transport iroh \
    --identity ~/.ssh/id_ed25519

# With port forwarding
alknet connect --server example.com:22 \
    --identity ~/.ssh/id_ed25519 \
    --forward 5432:db.internal:5432 \
    --forward 6379:redis.internal:6379

Use the SOCKS5 proxy

Once connected, point any SOCKS5-aware application at 127.0.0.1:1080:

curl --socks5 127.0.0.1:1080 http://internal-api:8080/health

For VPN-like "route all traffic" behavior, use tun2proxy alongside alknet's SOCKS5 proxy (see ADR-014).

Crates

Crate Description
alknet-core Core library: transport trait, SOCKS5 server, port forwarding, auth, server handler
alknet CLI binary (alknet connect / alknet serve)
alknet-napi Node.js native addon via napi-rs (connect() / serve())

Feature flags

Feature Crate Default Description
tls alknet-core, alknet yes TLS transport (tokio-rustls)
iroh alknet-core, alknet yes iroh QUIC P2P transport
acme alknet-core no ACME/Let's Encrypt auto-cert provisioning
testutil alknet-core no Test utilities (for internal use)

Transport modes

Transport Client Server Notes
TCP --transport tcp --server addr:port --transport tcp --listen addr:port Direct SSH over TCP. Default.
TLS --transport tls --server addr:port --transport tls --tls-cert/--tls-key or --acme-domain SSH wrapped in TLS. Looks like HTTPS.
iroh --transport iroh --peer <id> --transport iroh QUIC P2P via iroh. No public IP needed.

Authentication

  • Ed25519 public keys — Default. Load authorized keys from a file via --authorized-keys.
  • OpenSSH certificate authority — Optional. Use --cert-authority for multi-user deployments.
  • No password authentication — Key-based auth only (see ADR-012).

Key formats are OpenSSH throughout (private keys: -----BEGIN OPENSSH PRIVATE KEY-----, public keys: ssh-ed25519 AAAA...). PEM-encoded keys (PKCS#1, PKCS#8) are not supported.

Architecture

Alknet's core architectural decision is that SSH never touches the network directly. The transport layer produces a duplex byte stream, and SSH runs over it via russh::client::connect_stream() / russh::server::run_stream(). This makes transports fully pluggable.

Client                                          Server
  │  transport.connect()                          │  transport_acceptor.accept()
  │  ─────────────────────────────────────────────▶│
  │        (duplex byte stream established)        │
  │  russh::client::connect_stream(stream)        │  russh::server::run_stream(stream, handler)
  │  ═══════ SSH session over stream ═════════════ │
  │  channel_open_direct_tcpip(host, port)        │
  │  ─────────────────────────────────────────────▶│
  │  ┌─────── TCP proxy ──────────────────┐       │
  │  │  SSH channel ←→ TcpStream::connect │       │
  │  └────────────────────────────────────┘       │

See docs/architecture/ for full specifications and ADR index.

Node.js API

The alknet-napi crate provides a Node.js native addon via napi-rs:

const { connect, serve } = require('alknet-napi');

// Client: open a duplex stream through SSH
const stream = await connect({
  server: "example.com:22",
  transport: "tcp",
  identity: "/path/to/key",
});
const data = await stream.read(1024);
await stream.write(Buffer.from("hello"));
await stream.close();

// Server: accept connections and receive streams
const server = await serve({
  transport: "tcp",
  hostKey: "/path/to/host_key",
  authorizedKeys: "/path/to/authorized_keys",
  listen: "0.0.0.0:22",
});
server.onConnection((event) => {
  const { stream, info } = event;
  // handle stream
});

iroh (peer-to-peer)

iroh transport eliminates the need for public IPs or port forwarding. Both sides discover each other through a relay, then establish a direct QUIC connection. This is ideal for services behind NAT, distributed systems, or any scenario where opening ports is impractical.

// Server: starts an iroh endpoint and prints its peer ID
const server = await serve({
  transport: "iroh",
  hostKey: "/path/to/host_key",
  authorizedKeys: "/path/to/authorized_keys",
  irohRelay: "https://relay.iroh.network/",  // optional, defaults to iroh's relay
  proxy: "socks5://proxy.example.com:1080", // optional, for restrictive networks
});
console.log("iroh endpoint ID:", server.endpointId);
// e.g. iroh endpoint ID: abc23xyz...

// Clients connect using that peer ID
const stream = await connect({
  peer: server.endpointId,
  transport: "iroh",
  identity: "/path/to/key",
  irohRelay: "https://relay.iroh.network/",  // must match the server's relay
  proxy: "socks5://proxy.example.com:1080",   // optional
});

The endpointId property returns the server's z-base-32 encoded iroh node ID. Share this ID with clients so they can connect — no DNS, no public IP, no port forwarding required.

TLS

TLS transport wraps SSH in TLS, making the connection indistinguishable from HTTPS traffic to deep packet inspection:

// Server
const server = await serve({
  transport: "tls",
  hostKey: "/path/to/host_key",
  authorizedKeys: "/path/to/authorized_keys",
  listen: "0.0.0.0:443",
  tlsCert: "/path/to/cert.pem",
  tlsKey: "/path/to/key.pem",
});

// Client
const stream = await connect({
  server: "example.com:443",
  transport: "tls",
  identity: "/path/to/key",
  tlsServerName: "example.com",  // optional, SNI hostname
  insecure: true,                 // accept self-signed certs (dev only)
});

Status and stability

This is alpha software. While it depends on well-established libraries (russh, tokio, rustls, iroh) for SSH, async I/O, TLS, and QUIC respectively, the integration layer that ties them together has not been battle-tested. Potential concerns include:

  • Connection handling edge cases — reconnection logic, graceful shutdown, resource cleanup
  • Security review — the auth layer, rate limiting, and stealth mode should be audited before production use
  • API stability — the library API (alknet-core) and NAPI interface may change between versions
  • Performance — no load testing or benchmarking has been done yet

Please test thoroughly and file issues for any problems you encounter.

License

Licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Description
No description provided
Readme 3 MiB
Languages
Rust 100%