Endpoints & Tunnels
HTTP / HTTPS
Forward public HTTPS to a local HTTP or HTTPS service. The public edge terminates TLS and serves your traffic over HTTP/1.1, HTTP/2, and HTTP/3 (QUIC) — clients pick whichever they support.
--url <hostname> to attach a reserved subdomain or custom domain. Omit --url and the server provisions an ephemeral endpoint — a randomly generated hostname that lives for the duration of the agent process.
tunnel.local_http_version to http2 (h2c) or http3 (QUIC, experimental — local target must serve HTTPS) for backends that require it.
# Reserved hostname
ngris http 8080 --url blog.ngris.com
# No --url → ephemeral endpoint
# (random hostname, valid for this run)
ngris http 8080
# Local HTTPS (self-signed OK — verified by default,
# set local_service_insecure_tls: true to skip)
ngris http https://localhost:8443 --url secure-app.ngris.com
TCP
Expose raw TCP services like databases or SSH (use with care). The server automatically assigns a free TCP port on the regional ingress (e.g. tcp://eu-fin.ngris.com:17032) and forwards bytes verbatim to your local listener. You don't pick the public port — only the hostname.
--url <hostname> to bind to a reserved hostname (the server still picks the port). Omit --url and the server provisions an ephemeral endpoint — a randomly generated tcp://<random>.ngris.com:<assigned-port> that lives for the duration of the run.
dedicated_ports entitlement and reserve one from the dashboard.
--proxy-protocol v2 (or v1) and the agent prepends a PROXY protocol header to the backend connection, carrying the original client's IP and port — so your database, SSH, or TLS-passthrough backend logs the real client instead of the agent. Your backend must be configured to expect it (e.g. Postgres behind pgbouncer, or proxy_protocol on in nginx/HAProxy); otherwise it reads the header as junk and drops the connection. HTTP tunnels don't need this — the edge already forwards the client IP as X-Forwarded-For.
# Reserved hostname, server-assigned port
ngris tcp 5432 --url mydb.ngris.com
# SSH on a reserved hostname
ngris tcp 22 --url ssh.ngris.com
# No --url → ephemeral
# (random hostname + server-assigned port, valid for this run)
ngris tcp 5432
# Preserve the real client IP to the backend
# (backend must be configured to expect PROXY protocol)
ngris tcp 5432 --url mydb.ngris.com --proxy-protocol v2
UDP
Tunnel datagram services like DNS, WireGuard, or QUIC-based protocols. Like TCP, the server automatically assigns a free UDP port on the regional ingress (udp://<hostname>:<assigned-port>) — you supply only the hostname. Packets are forwarded to your local listener and the response is shipped back to the originating client.
--url <hostname> to bind to a reserved hostname (the server still picks the port). Omit --url and the server provisions an ephemeral endpoint — a randomly generated udp://<random>.ngris.com:<assigned-port> that lives for the duration of the run.
# Reserved hostname, server-assigned port
ngris udp 53 --url dns.ngris.com
# WireGuard server
ngris udp 51820 --url wg.ngris.com
# No --url → ephemeral
# (random hostname + server-assigned port, valid for this run)
ngris udp 5353
# Test it — replace host:port with the assigned public URL
dig @dns.ngris.com -p 17032 example.com
HTTP/3 & QUIC at the edge
HTTP/3 is enabled automatically for every HTTPS endpoint — no flags, no separate command. The public edge advertises HTTP/3 via the alt-svc response header on the first HTTP/1.1 or HTTP/2 request; modern browsers and clients then upgrade to QUIC on subsequent connections.
Real client IPs survive the HTTP/3 hop via PROXY protocol v2 over UDP, so log lines and the traffic inspector see the original client address regardless of the negotiated HTTP version.
curl -sI --http3 https://your-app.ngris.com (curl built with HTTP/3 support) or inspect alt-svc in DevTools.
curl -sI --http3 https://your-app.ngris.com
# Without --http3 you can still see the advertisement:
curl -sI https://your-app.ngris.com | grep -i alt-svc
# alt-svc: h3=":443"; ma=86400
Custom Domains
Map your domain to an endpoint with automated certificates.
- Add your domain in the dashboard (e.g., dev.example.com).
- Create a DNS CNAME from dev.example.com → your-org.cname.ngris.com.
- Start the endpoint and attach the domain.
*.staging.example.com). Contact sales for setup.
ngris http 3000 --url dev.example.com
Config File
Use config.yaml in your working directory for repeatable setups across environments. Every key shown below is optional — defaults are baked in; CLI flags and environment variables override the file.
observability.region to express a region preference (e.g. us-east-va, eu-fin).
NGRIS_AGENT_ prefix and underscores for nesting (e.g. NGRIS_AGENT_AUTHENTICATION_TOKEN, NGRIS_AGENT_OBSERVABILITY_REGION).
tunnels: is non-empty it replaces the single tunnel: block above. The TUI is disabled in this mode.
# config.yaml — place in working directory
authentication:
token: "your-api-token"
observability:
region: "us-east-va" # region preference for discovery
tunnel:
type: "http" # http | tcp | udp
local_address: "localhost:3000"
url: "myapp.ngris.com" # empty → ephemeral endpoint
name: "My Service Tunnel"
authentication:
token: "your-api-token"
logging:
level: "info" # debug | info | warn | error | fatal
file: "./logs/agent.log"
format: "text" # text | json
control_plane_verbose: false
# Agent → tunnel-server TLS
tls:
ca_cert_file: "" # custom CA (PEM) added to the trust store
ca_cert_only: false # pin: trust ONLY ca_cert_file
insecure_skip_verify: false # UNSAFE — debugging only
# Allow self-signed certs on a local HTTPS backend
local_service_insecure_tls: false
observability:
region: "us-east-va"
tunnel:
type: "http" # http | tcp | udp
local_address: "localhost:80" # port, host:port, or file://<dir>
url: "" # empty → ephemeral endpoint
name: "My Service Tunnel"
http_port: 9090 # local stats server port
verbose: false
max_requests_log: 1000
# Timeouts
tcp_dial_timeout: 10s
tcp_idle_timeout: 5m # 0 disables
tcp_keepalive: 30s
control_read_timeout: 60s
local_service_timeout: 60s
# Local protocol + transfer tuning
local_http_version: "http1.1" # http1.1 | http2 (h2c) | http3 (QUIC)
chunk_size: 65536 # 64 KiB
max_concurrent_requests: 100
# Static file serving (when local_address is file://<dir>)
disable_directory_listing: false
index_filename: "index.html"
file_server_write: false # WebFM read-write — no built-in auth
use_webfm_ui: false
# Load balancing across multiple agents on the same tunnel
load_balancing:
weight: 1
max_fails: 0 # 0 disables
fail_timeout: 30s
# Routing identity (server-side routing rules)
agent_id: ""
agent_name: ""
agent_tags: "" # e.g. "api,v2,production"
# Multi-tunnel: when non-empty REPLACES the single `tunnel:` above
tunnels:
- type: "http"
local_address: "localhost:3000"
url: "app.ngris.com"
name: "web"
- type: "tcp"
local_address: "localhost:22"
url: "ssh.ngris.com"
name: "ssh"
- type: "udp"
local_address: "localhost:53"
name: "dns"
# Launch all tunnels defined in config.yaml
# The agent auto-loads config.yaml from the working directory
ngris
# Or run a single tunnel with CLI flags
ngris http 3000 --url myapp