Listeners

A listener pairs a network server (the transport layer) with a listener (the fox3 protocol handler). Listeners are created at runtime via the WebSocket API (listener.create) or the browser UI. They are in-memory only — they do not persist across server restarts.

Status

Protocol Status Transport
http working HTTP/1.1 plain
https working HTTP/1.1 TLS
h2c working HTTP/2 cleartext
http2 working HTTP/2 over TLS
http3 working HTTP/3 (QUIC)
tcp coming soon raw TCP
udp coming soon raw UDP
smb coming soon Windows named pipe
dns coming soon DNS
doh coming soon DNS-over-HTTPS
wss coming soon WebSocket Secure

HTTP family (http, https, h2c, http2, http3)

All five HTTP variants share the same listener and handler code. The Protocol option selects which transport the underlying server starts.

Options

Option Default Required Description
Protocol yes One of http, https, h2c, http2, http3
Name My HTTP Listener yes Human-readable identifier
Interface 127.0.0.1 yes Bind address
Port 80 (http) / 443 (https/h2c/http2/http3) yes Bind port
PSK fox3 yes Pre-shared key — SHA-256 hashed for agent JWT signing
JWTKey (random 32-byte, base64) no Explicit JWT signing key; auto-generated if absent
JWTLeeway 1m no JWT expiry tolerance (Go duration string, e.g. 30s)
URLS / no Comma-separated URL paths the agent handler mounts on
X509Cert data/x509/server.crt for TLS PEM certificate path
X509Key data/x509/server.key for TLS PEM private key path
Transforms jwe,json no Comma-separated transform pipeline (see below)
Authenticator NONE no none or opaque
Description Default HTTP Listener no Free text
{
  "action": "listener.create",
  "id": "req-1",
  "payload": {
    "Protocol":    "https",
    "Name":        "https-443",
    "Interface":   "0.0.0.0",
    "Port":        "443",
    "PSK":         "change-me",
    "X509Cert":    "/etc/ssl/certs/server.crt",
    "X509Key":     "/etc/ssl/private/server.key",
    "Transforms":  "jwe,json",
    "Authenticator": "none"
  }
}

Then start it:

{ "action": "listener.start", "id": "req-2", "payload": { "id": "<listener-uuid>" } }

Example — plain HTTP for local testing

{
  "action": "listener.create",
  "id": "req-3",
  "payload": {
    "Protocol":   "http",
    "Name":       "http-8443",
    "Interface":  "0.0.0.0",
    "Port":       "8443",
    "PSK":        "test-psk",
    "Transforms": "jwe,json"
  }
}

Transform pipeline

The Transforms option is a comma-separated ordered list of encoder/encrypter names applied to agent messages.

Outbound (server → agent): transformers applied left-to-right. Inbound (agent → server): transformers applied right-to-left.

Name Type Notes
jwe encrypter JWE compact serialization, AES-256-GCM. Default for HTTP.
aes encrypter Raw AES-256-GCM
rc4 encrypter RC4 stream cipher
xor encrypter XOR with key
json encoder JSON marshal/unmarshal of messages.Base
gob-base encoder Go gob encoding of messages.Base
gob-string encoder Go gob encoding of string type
base64-byte encoder Base64 encode → []byte
base64-string encoder Base64 encode → string
hex-byte encoder Hex encode → []byte
hex-string encoder Hex encode → string

Default: jwe,json

  • Outbound: JSON.Construct(Base)JWE.Construct(json_bytes) → wire bytes
  • Inbound: JWE.Deconstruct(bytes)JSON.Deconstruct(plaintext)Base

The agent must use the same pipeline in the same order.


Listener lifecycle

listener.create  →  listener stored in memory (not yet accepting connections)
listener.start   →  server begins Listen() + Start()
listener.stop    →  server stops accepting new connections
listener.delete  →  server stopped (if running), listener removed from memory

Listeners do not survive a server restart. Recreate them after restart or automate with a startup script that calls listener.create + listener.start via the WebSocket API.


TLS certificates

For HTTPS/HTTP2/HTTP3 listeners you need a certificate and key. Options:

Self-signed (testing)

mkdir -p data/x509
openssl req -x509 -newkey rsa:4096 -keyout data/x509/server.key \
  -out data/x509/server.crt -days 365 -nodes -subj "/CN=fox3"

The server looks for these paths by default if X509Cert/X509Key are not specified.

Let’s Encrypt

Use Certbot or acme.sh to obtain a cert for your domain, then pass the paths in the listener options.