Traffic policies
Attach a policy file to any share and the relay enforces it on every public request — before traffic reaches your machine. Works for tunnels, file shares and upload pages alike, anonymously and self-hosted.
Quick start
Section titled “Quick start”xport 3000 --policy-file xport.ymlon_http_request: - name: admin-only expressions: - req.URL.Path.startsWith('/admin') actions: - type: ip_restrictions config: allow: ["203.0.113.0/24", "10.8.0.7"] - type: basic_auth config: username: root password: s3cret
- name: brake-the-scrapers expressions: - req.Method == 'GET' && req.URL.Path == '/api/search' actions: - type: rate_limit config: capacity: 30 rate: 1m - type: deny config: status_code: 429 body: slow down
on_http_response: - expressions: - res.StatusCode >= 200 && res.StatusCode < 300 actions: - type: add_headers config: headers: X-Content-Type-Options: nosniff X-Frame-Options: DENY - type: remove_headers config: headers: [Server]The CLI validates the file locally (every CEL expression compiles before the share starts); the relay re-validates and compiles it at register. Invalid policies fail the share with a clear error — never silently.
Expressions
Section titled “Expressions”Rules carry CEL expressions, ANDed together; all must hold for the actions to run. Variables:
| Variable | Fields |
|---|---|
req |
Method, URL.Path, URL.RawPath, URL.RawQuery, Host, Proto, Headers (canonical map of string → list) |
conn |
ClientIP (resolved through the trusted edge) |
res (response phase) |
StatusCode, Headers |
Examples:
req.Method == 'GET' && req.URL.Path.startsWith('/admin')'x-webhook-token' in req.Headers && req.Headers['X-Webhook-Token'][0] == 'abc'req.Headers['User-Agent'][0].matches('(?i)curl.*')res.StatusCode >= 500Request bodies are never exposed to expressions.
Actions
Section titled “Actions”Request phase: deny (status_code, body), ip_restrictions
(allow/deny CIDR lists — deny wins), rate_limit (capacity,
rate like 30s/1m, optional bucket_key CEL list; excess → 429),
basic_auth (username, password, realm), redirect (url,
status_code 301/302/303/307/308), custom_response (status_code
100–599, content_type, body, optional headers — the policy answers
the request itself: mock responses, maintenance pages), url_rewrite
(from/to — replace a path prefix before tunneling, e.g.
from: /api, to: "" strips it), add_headers, remove_headers,
log (message — logs the match, never the content).
Response phase: deny (replaces status/body), add_headers,
remove_headers, log.
Actions run in order; the first one that produces a final response (deny, redirect, rate limit, auth challenge) ends the request — it never reaches your service.
Limits
Section titled “Limits”24 KiB on the wire, 16 rules per phase, 8 expressions and 8 actions per rule, CEL evaluation cost-capped. Policies beyond that are rejected at register.
Configuration
Section titled “Configuration”Set tunnel.policy_file in config.yaml to
attach the same policy to every share on that machine; --policy-file
wins per invocation.

