main
md 364 lines 18.8 KB
Rendered Raw
1 ---
2 title: Configuration Reference
3 description: Complete reference for all Portal environment variables, CLI flags, and configuration files.
4 ---
5
6 # Configuration Reference
7
8 Complete reference for all Portal environment variables, CLI flags, and configuration files.
9
10 ## Relay Server Environment Variables
11
12 The relay server (`relay-server`) reads configuration from environment variables. Each variable corresponds to a CLI flag of the same shape (e.g. `PORTAL_URL``--portal-url`). CLI flags take precedence over environment variables when both are set.
13
14 ### Core
15
16 | Variable | Default | Type | Description |
17 |----------|---------|------|-------------|
18 | `PORTAL_URL` | `https://localhost:4017` | string | Public base URL of this relay server |
19 | `IDENTITY_PATH` | `./.portal-certs` | string | Directory path for relay identity, policy state, and TLS materials |
20 | `API_PORT` | `4017` | int | Admin/API server listen port |
21 | `SNI_PORT` | `443` | int | TCP SNI router listen port |
22 | `WIREGUARD_PORT` | `51820` | int | Public and listen UDP port for relay discovery overlay |
23
24 ### Transport
25
26 | Variable | Default | Type | Description |
27 |----------|---------|------|-------------|
28 | `MIN_PORT` | `0` | int | Inclusive minimum port for UDP and raw TCP transports (`0` = disabled) |
29 | `MAX_PORT` | `0` | int | Inclusive maximum port for UDP and raw TCP transports (`0` = disabled) |
30 | `UDP_ENABLED` | `false` | bool | Enable UDP relay transport; requires a valid `MIN_PORT`/`MAX_PORT` range |
31 | `TCP_ENABLED` | `false` | bool | Enable raw TCP port transport; requires a valid `MIN_PORT`/`MAX_PORT` range |
32
33 ### Features
34
35 | Variable | Default | Type | Description |
36 |----------|---------|------|-------------|
37 | `DISCOVERY` | `false` | bool | Serve relay discovery endpoints and poll discovery peers |
38 | `BOOTSTRAPS` | `""` | string | Additional bootstrap relay API URLs used for discovery expansion (comma-separated) |
39
40 ### Payments
41
42 | Variable | Default | Type | Description |
43 |----------|---------|------|-------------|
44 | `X402_ENABLED` | `false` | bool | Enable relay-owned Sui x402 facilitator endpoints under `/api/x402` for future control-plane payments |
45 | `X402_TESTNET` | `false` | bool | Use Sui testnet for relay-owned x402 facilitator payments; `false` uses Sui mainnet |
46 | `X402_PAY_TO` | `""` | string | Sui payment recipient address for relay-owned control-plane x402 resources |
47
48 ### Proxy
49
50 | Variable | Default | Type | Description |
51 |----------|---------|------|-------------|
52 | `TRUST_PROXY_HEADERS` | `false` | bool | Trust `X-Forwarded-*` and `X-Real-IP` headers from trusted proxies |
53 | `TRUSTED_PROXY_CIDRS` | `""` | string | Trusted proxy CIDR allowlist for forwarded headers (comma-separated); defaults to private/loopback ranges when `TRUST_PROXY_HEADERS` is enabled |
54
55 ### TLS
56
57 | Variable | Default | Type | Description |
58 |----------|---------|------|-------------|
59 | `ACME_DNS_PROVIDER` | `""` | string | DNS provider for managed DNS-01/A-record sync, ECH HTTPS records, and ENS gasless DNSSEC/TXT automation (`cloudflare` \| `gcloud` \| `hetzner` \| `njalla` \| `route53` \| `vultr`); leave empty to use manual `fullchain.pem`/`privatekey.pem` from `IDENTITY_PATH` |
60 | `ENS_GASLESS_ENABLED` | `false` | bool | Enable ENS gasless DNS import automation for the managed DNS zone and lease hostnames |
61
62 ### Diagnostics
63
64 | Variable | Default | Type | Description |
65 |----------|---------|------|-------------|
66 | `PPROF_ENABLED` | `false` | bool | Enable the relay pprof diagnostics HTTP server |
67 | `PPROF_ADDR` | `127.0.0.1:6060` | string | pprof listen address when enabled; keep it on loopback unless the port is protected |
68
69 ### Admin
70
71 | Variable | Default | Type | Description |
72 |----------|---------|------|-------------|
73 | `ADMIN_TOKEN` | | string | Bearer token source for relay admin and policy APIs; set a long random value for production relays |
74
75 ### Frontend API Service
76
77 The TypeScript API service reads these environment
78 variables:
79
80 | Variable | Default | Type | Description |
81 |----------|---------|------|-------------|
82 | `PORT` | `8081` | int | Frontend API HTTP listen port |
83 | `PORTAL_API_BASE_URL` | `https://portal:4017` | string | Relay API base URL used to compose frontend-owned state |
84 | `LANDING_PAGE_ENABLED` | `false` | bool | Default landing page flag when no frontend state has been saved yet |
85 | `PORTAL_FRONTEND_STATE_PATH` | `""` | string | Optional JSON file path for persisted frontend-owned state; a Compose deployment can store this under `./.portal-certs/frontend-state/state.json` |
86 | `HEADLESS_SHELL_URL` | `""` | string | Headless Chrome CDP WebSocket URL; leave empty to disable generated thumbnails |
87
88 ### Cloudflare
89
90 | Variable | Default | Type | Description |
91 |----------|---------|------|-------------|
92 | `CLOUDFLARE_TOKEN` | | string | Cloudflare DNS API token; required when `ACME_DNS_PROVIDER=cloudflare` |
93
94 ### Google Cloud
95
96 | Variable | Aliases | Default | Type | Description |
97 |----------|---------|---------|------|-------------|
98 | `GCP_PROJECT_ID` | `GOOGLE_CLOUD_PROJECT`, `GCLOUD_PROJECT`, `GCE_PROJECT` | | string | Google Cloud project ID for Cloud DNS automation; auto-detected from ADC or GCE metadata when omitted |
99 | `GCP_MANAGED_ZONE` | `GCP_ZONE`, `GCE_ZONE_ID` | | string | Explicit Google Cloud DNS managed zone name or numeric ID override |
100 | `GOOGLE_APPLICATION_CREDENTIALS` | | | string | Path to GCP service account key file (standard ADC; used by the GCP client library) |
101
102 ### Hetzner
103
104 | Variable | Aliases | Default | Type | Description |
105 |----------|---------|---------|------|-------------|
106 | `HETZNER_API_TOKEN` | `HCLOUD_TOKEN` | | string | Hetzner Cloud API token for DNS automation; required when `ACME_DNS_PROVIDER=hetzner` |
107
108 ### AWS
109
110 | Variable | Aliases | Default | Type | Description |
111 |----------|---------|---------|------|-------------|
112 | `AWS_ACCESS_KEY_ID` | | | string | AWS access key ID for Route53 static credentials; uses the default AWS credential chain when omitted |
113 | `AWS_SECRET_ACCESS_KEY` | | | string | AWS secret access key for Route53 static credentials |
114 | `AWS_SESSION_TOKEN` | | | string | AWS session token for Route53 temporary credentials |
115 | `AWS_REGION` | `AWS_DEFAULT_REGION` | `us-east-1` | string | AWS region for Route53 and Route53-backed DNS-01 |
116 | `AWS_HOSTED_ZONE_ID` | | | string | Explicit Route53 hosted zone ID override |
117 | `AWS_DNSSEC_KMS_KEY_ARN` | | | string | AWS KMS key ARN used to create a Route53 DNSSEC key-signing key when needed |
118
119 ### Vultr
120
121 | Variable | Default | Type | Description |
122 |----------|---------|------|-------------|
123 | `VULTR_API_KEY` | | string | Vultr API key for DNS automation; required when `ACME_DNS_PROVIDER=vultr` |
124
125 ### Njalla
126
127 | Variable | Default | Type | Description |
128 |----------|---------|------|-------------|
129 | `NJALLA_TOKEN` | | string | Njalla API token for DNS automation; required when `ACME_DNS_PROVIDER=njalla` |
130
131 ---
132
133 ## Portal Tunnel CLI Flags
134
135 The `portal expose` subcommand accepts the following flags. Flags that read from environment variables are noted in the **Env Var** column.
136
137 ### Connection
138
139 | Flag | Env Var | Type | Default | Description |
140 |------|---------|------|---------|-------------|
141 | `--relays` | | string | _(registry)_ | Additional Portal relay server API URLs (comma-separated; scheme omitted defaults to https) |
142 | `--discovery` | | bool | `true` | Include public registry relays and discover additional relay bootstraps |
143 | `--multi-hop` | `MULTI_HOP` | string | | Ordered multi-hop relay API URLs, comma-separated |
144 | `--multi-hop-depth` | `MULTI_HOP_DEPTH` | int | `0` | Automatically select one multi-hop route with this hop count; 0 or 1 disables multi-hop |
145 | `--max-active-relays` | `MAX_ACTIVE_RELAYS` | int | `3` | Maximum auto-selected relays to keep connected; explicit relays are always included |
146 | `--ban-mitm` | `BAN_MITM` | bool | `false` | Ban relay when the MITM self-probe detects TLS termination |
147
148 ### Identity
149
150 | Flag | Env Var | Type | Default | Description |
151 |------|---------|------|---------|-------------|
152 | `--identity-path` | `IDENTITY_PATH` | string | `identity.json` | Identity JSON file path |
153 | `--identity-json` | `IDENTITY_JSON` | string | | Identity JSON payload; overrides `--identity-path` contents and is persisted there when both are set |
154
155 ### Lease
156
157 | Flag | Env Var | Type | Default | Description |
158 |------|---------|------|---------|-------------|
159 | `--name` | | string | _(auto)_ | Public hostname prefix (single DNS label); auto-generated when omitted |
160 | `--description` | | string | | Service description metadata |
161 | `--tags` | | string | | Service tags metadata (comma-separated) |
162 | `--owner` | | string | | Service owner metadata |
163 | `--thumbnail` | | string | | Service thumbnail URL metadata |
164 | `--hide` | | bool | `false` | Hide service from relay listing screens |
165 | `--x402-pay-to` | | string | | Sui USDC payment recipient address for this tunnel |
166 | `--x402-testnet` | | bool | `false` | Use Sui testnet for tunnel x402 payments; default is Sui mainnet |
167
168 ### Routing
169
170 | Flag | Env Var | Type | Default | Description |
171 |------|---------|------|---------|-------------|
172 | `--http-route` | | string | | HTTP route mapping in `PATH=UPSTREAM [METHOD[,METHOD...]:USDC_AMOUNT]` form; repeat to aggregate multiple local HTTP services behind one public URL; route amounts require `--x402-pay-to` |
173
174 ### Transport
175
176 | Flag | Env Var | Type | Default | Description |
177 |------|---------|------|---------|-------------|
178 | `--udp` | `UDP_ENABLED` | bool | `false` | Enable public UDP relay in addition to the default stream path |
179 | `--udp-addr` | `UDP_ADDR` | string | | Local UDP target address for relayed datagrams (`host:port` or port only); defaults to the target when `--udp` is enabled |
180 | `--tcp` | `TCP_ENABLED` | bool | `false` | Request a dedicated TCP port on the relay for raw TCP services (no TLS; e.g., Minecraft, game servers) |
181
182 The `portal list` subcommand accepts the following flags:
183
184 | Flag | Type | Default | Description |
185 |------|------|---------|-------------|
186 | `--relays` | string | _(registry)_ | Additional Portal relay server API URLs (comma-separated) |
187 | `--default-relays` | bool | `true` | Include public registry relays |
188
189 ---
190
191 ## Configuration Files
192
193 ### `config.toml`
194
195 `portal agent run` reads the platform default `config.toml` and starts one managed process for all declared tunnels. Relative paths are resolved from the config file directory. The config file must exist before the agent is started.
196
197 Default paths:
198
199 | OS | Config | Default identity |
200 |----|--------|------------------|
201 | Linux user | `$XDG_CONFIG_HOME/portal-tunnel/agent/config.toml` or `~/.config/portal-tunnel/agent/config.toml` | `$XDG_DATA_HOME/portal-tunnel/agent/identity.json` or `~/.local/share/portal-tunnel/agent/identity.json` |
202 | Linux root | `/etc/portal-tunnel/agent/config.toml` | `/var/lib/portal-tunnel/agent/identity.json` |
203 | macOS user | `~/Library/Application Support/Portal Tunnel/Agent/config.toml` | `~/Library/Application Support/Portal Tunnel/Agent/identity.json` |
204 | macOS root | `/Library/Application Support/Portal Tunnel/Agent/config.toml` | `/Library/Application Support/Portal Tunnel/Agent/identity.json` |
205 | Windows | `%ProgramData%\Portal Tunnel\Agent\config.toml` | `%ProgramData%\Portal Tunnel\Agent\identity.json` |
206
207 ```toml
208 [agent]
209 control_addr = "127.0.0.1:4018"
210 service_name = "portal-agent"
211
212 [[tunnels]]
213 id = "web"
214 name = "myapp"
215 target = "127.0.0.1:3000"
216 relays = ["https://portal.example.com"]
217 discovery = false
218 description = "Managed web tunnel"
219 tags = ["web"]
220
221 [[tunnels]]
222 id = "api"
223 name = "myapp"
224 x402_pay_to = "0x..."
225 x402_testnet = true
226
227 [[tunnels.http_routes]]
228 prefix = "/api"
229 upstream = "http://127.0.0.1:3001"
230 methods = ["GET"]
231 amount = "0.01"
232
233 [[tunnels.http_routes]]
234 prefix = "/"
235 upstream = "http://127.0.0.1:5173"
236 ```
237
238 Agent fields:
239
240 | Field | Default | Description |
241 |-------|---------|-------------|
242 | `state_dir` | Platform default state directory | Stores the local control endpoint token and runtime state |
243 | `control_addr` | `127.0.0.1:4018` | Loopback-only local control API address |
244 | `service_name` | `portal-agent` | OS service name |
245 | `allowed_wallets` | empty | Wallet addresses allowed to read local agent status through wallet auth; empty allows any wallet on the loopback auth endpoint |
246
247 The local agent dashboard and mutating control API calls use the bearer token in
248 the agent state directory. Wallet-authenticated agent requests are read-only and
249 can only read `/agent/status`.
250
251 Tunnel fields mirror `portal expose` flags:
252
253 | Field | Type | Description |
254 |-------|------|-------------|
255 | `id` | string | Stable tunnel ID used by the agent dashboard |
256 | `target` | string | Local TCP target, equivalent to the `portal expose <target>` argument |
257 | `http_routes` | table array | HTTP route mappings; cannot be combined with `target` or `udp` |
258 | `relays` | string array | Explicit relay API URLs |
259 | `discovery` | bool | Include registry and relay discovery expansion |
260 | `multi_hop` | string array | Ordered multi-hop relay path |
261 | `multi_hop_depth` | int | Automatically select one multi-hop route with this depth |
262 | `identity_path` | string | Tunnel identity JSON file path. When omitted, one tunnel uses the platform default `identity.json`; multiple tunnels use `<state-dir>/<tunnel-id>/identity.json` |
263 | `identity_json` | string | Identity JSON payload; overrides `identity_path` contents and is persisted there when both are set |
264 | `udp`, `udp_addr`, `tcp` | bool/string | UDP and raw TCP relay options |
265 | `description`, `tags`, `owner`, `thumbnail`, `hide` | mixed | Lease metadata shown by relays |
266 | `x402_pay_to` | string | Tunnel-owned Sui USDC x402 payment recipient for paid HTTP routes |
267 | `x402_testnet` | bool | Use Sui testnet for tunnel-owned x402 paid routes; omitted or `false` uses Sui mainnet |
268 | `http_routes[].amount` | string | Optional Sui USDC x402 amount, such as `0.01`, for one HTTP route prefix; requires `x402_pay_to` |
269 | `http_routes[].methods` | string array | Optional HTTP methods that require payment on that route; empty means every method |
270
271 When any routed HTTP entry has `amount`, the tunnel also serves
272 `/x402/client.js` and `/x402/prepare` on the public tunnel origin. Browser
273 frontends served by another route in the same tunnel can import
274 `/x402/client.js` and use `x402Fetch()` to run the same Sui wallet payment flow
275 as the standalone payment app. Native clients use `/x402/prepare` directly and
276 send the signed payload as `X-PAYMENT`. Payment is still enforced by the tunnel
277 on the paid route prefix. Tunnel paid routes default to Sui mainnet and use Sui
278 testnet when `x402_testnet = true`.
279
280 For a task-oriented walkthrough, see [Portal Agent](/portal-agent).
281
282 ### `identity.json`
283
284 Stores the secp256k1 identity used to sign tunnel sessions and relay descriptors. `portal expose` treats `--identity-path` as a direct JSON file path. `relay-server` treats `IDENTITY_PATH` as a state directory and stores this file at `IDENTITY_PATH/identity.json`.
285
286 | Field | Type | Description |
287 |-------|------|-------------|
288 | `name` | string | Human-readable label for this identity |
289 | `address` | string | Derived EVM address used for SIWE and identity ownership |
290 | `public_key` | string | Compressed secp256k1 public key hex |
291 | `private_key` | string | secp256k1 private key hex; keep secret |
292 | `mnemonic` | string | BIP-39 mnemonic used to derive the secp256k1 identity key; keep secret |
293 | `derivation_path` | string | EVM derivation path for `mnemonic`; defaults to `m/44'/60'/0'/0/0` |
294 | `wireguard_public_key` | string | Relay-only WireGuard overlay public key when discovery is enabled |
295 | `wireguard_private_key` | string | Relay-only WireGuard overlay private key when discovery is enabled |
296 | `encrypted_client_hello_seed` | string | Relay-only HKDF salt for deriving the ECH HPKE private key; generated automatically when missing; keep secret |
297
298 When `mnemonic` is present, Portal derives the private key at `derivation_path`
299 and preserves the mnemonic form when rewriting `identity.json`. The same
300 identity file or state directory can be reused across restarts to keep a stable
301 address.
302
303 ### `policy.json`
304
305 Persists relay policy state. Managed automatically by the relay on write; do not edit manually while the server is running.
306
307 Relay policy settings are stored at `IDENTITY_PATH/policy.json`.
308
309 ---
310
311 ## ACME DNS Provider Configuration
312
313 Set `ACME_DNS_PROVIDER` (or `--acme-dns-provider`) to one of the values below to enable DNS-backed automation. Portal uses the same provider for DNS-01 challenges, managed A records, ECH HTTPS records, and optional ENS gasless DNS records.
314
315 When this variable is empty the relay server falls back to manually supplied `fullchain.pem` and `privatekey.pem` files in `IDENTITY_PATH`.
316
317 For ENS gasless behavior and wallet authentication details, see [Wallet and ENS](/wallet-and-ens).
318
319 ### Cloudflare (`cloudflare`)
320
321 | Variable | Required | Description |
322 |----------|----------|-------------|
323 | `CLOUDFLARE_TOKEN` | Yes | Cloudflare DNS API token with `Zone:DNS:Edit` permission |
324
325 ### Google Cloud DNS (`gcloud`)
326
327 | Variable | Required | Description |
328 |----------|----------|-------------|
329 | `GCP_PROJECT_ID` | No | Google Cloud project ID; auto-detected from ADC or GCE metadata when omitted |
330 | `GCP_MANAGED_ZONE` | No | Cloud DNS managed zone name or numeric ID; inferred from the portal domain when omitted |
331 | `GOOGLE_APPLICATION_CREDENTIALS` | No | Path to a service account key JSON file; uses Application Default Credentials when omitted |
332
333 ### AWS Route53 (`route53`)
334
335 | Variable | Required | Description |
336 |----------|----------|-------------|
337 | `AWS_ACCESS_KEY_ID` | No | Access key ID; uses the default AWS credential chain (instance profile, env, `~/.aws/credentials`) when omitted |
338 | `AWS_SECRET_ACCESS_KEY` | No | Secret access key; required when `AWS_ACCESS_KEY_ID` is set |
339 | `AWS_SESSION_TOKEN` | No | Session token for temporary credentials |
340 | `AWS_REGION` | No | AWS region; defaults to `us-east-1` |
341 | `AWS_HOSTED_ZONE_ID` | No | Route53 hosted zone ID; inferred from the portal domain when omitted |
342 | `AWS_DNSSEC_KMS_KEY_ARN` | No | KMS key ARN for DNSSEC key-signing key creation |
343
344 ### Hetzner DNS (`hetzner`)
345
346 | Variable | Required | Description |
347 |----------|----------|-------------|
348 | `HETZNER_API_TOKEN` | Yes | Hetzner Cloud API token with DNS zone and RRSet write access |
349
350 Note: Hetzner DNS does not support provider-side DNSSEC signing, so `ACME_DNS_PROVIDER=hetzner` supports ACME, A records, and HTTPS/ECH records, but not ENS gasless DNSSEC automation.
351
352 ### Njalla DNS (`njalla`)
353
354 | Variable | Required | Description |
355 |----------|----------|-------------|
356 | `NJALLA_TOKEN` | Yes | Njalla API token with DNS record write access |
357
358 Note: Njalla supports managed ACME, A records, TXT records, and HTTPS/ECH records. Portal does not automate Njalla DNSSEC signing, so `ACME_DNS_PROVIDER=njalla` does not support ENS gasless DNSSEC automation.
359
360 ### Vultr DNS (`vultr`)
361
362 | Variable | Required | Description |
363 |----------|----------|-------------|
364 | `VULTR_API_KEY` | Yes | Vultr API key with DNS domain, record, and DNSSEC write access |