Configuration reference
Download and install the gateway from the browser deployment guide. Its release archive includes gateway.example.toml and the full deployment README.
The configuration is a TOML file: --config FILE, else the
WND_GATEWAY_CONFIG environment variable, else /etc/wnd-gateway/gateway.toml
(Windows: C:\ProgramData\WND Software\WND Gateway\gateway.toml). A complete,
commented example is gateway.example.toml. A setting
the gateway doesn't know is a mistake (so a typing error isn't silently
ignored); wnd-gateway check-config says which.
[server]
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
listen |
"127.0.0.1:8080" |
The address and port to listen on, such as "0.0.0.0:443". Any address other than this computer's own needs tls_certificate and tls_key: plain HTTP is only for testing on the server itself. |
public_url |
(required with HTTPS) | The address people open, such as "https://terminal.bank.example". Sign-in redirects use it, and the gateway only accepts browser connections that come from this page. |
tls_certificate |
none | The certificate for that name (PEM, with its chain). |
tls_key |
none | Its private key (PEM). Readable by the gateway's account only. |
data_dir |
/var/lib/wnd-gateway (Windows C:\ProgramData\WND Software\WND Gateway) |
Where the audit log, usage logs, guides and recordings are kept, and files on their way to browsers. |
log_format |
"json" |
The gateway's own log on standard output: "json" (one object per line) or "text". |
log_file |
none (Windows service: gateway.log in data_dir) |
Also write the log to this file. |
client_address_header |
none | The header a reverse proxy puts the browser's address in, such as "X-Forwarded-For". Only set it with such a proxy: anyone could send the header otherwise. |
reconnect_grace_seconds |
60 |
How long a browser window that lost its connection has to come back to its sessions. |
computer_name |
WEB- + this server's name |
The computer name of the gateway's sessions in the audit and usage logs. |
[sign_in]
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
idle_timeout_minutes |
30 |
Signed out after this many minutes without using WND Terminal. |
max_session_hours |
12 |
Signed in again after this many hours. |
[sign_in.oidc]
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
issuer |
(required) | The provider's issuer URL (https). |
client_id |
(required) | The app registration's client ID. |
client_secret / client_secret_file |
(one is required) | The client secret, or a file holding it (better: the configuration then holds no secret). |
token_auth |
"client_secret_basic" |
How the secret is sent to the token endpoint: "client_secret_basic" or "client_secret_post". |
scopes |
["openid", "profile", "email"] |
Must include openid. |
username_claim |
"preferred_username" |
The ID token claim with the person's login. When it isn't there, email, then sub. |
display_name_claim |
"name" |
The claim with the name shown in the title bar. |
groups_claim |
"groups" |
The claim listing the person's groups. |
allowed_domains |
none (anyone the provider signs in) | Only verified e-mail addresses (or Google hd) in these domains. |
required_groups |
none | Only people in at least one of these groups (compared without regard to case). |
button_label |
"Sign in with your organization's account" |
The sign-in button's words. |
[sign_in.local]
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
users_file |
(required) | The users file (TOML, [[user]] entries with name, password (an Argon2id hash), display_name, groups, disabled, helpdesk). |
[policy]
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
file |
none (the defaults) | The organization's policy.json, the same format as the desktop app's (policy settings). The gateway doesn't start with a policy file it can't read. |
[license]
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
file |
see below | The license file from WND Software's customer portal. |
key |
none | Or the license key itself (wnd1....). |
Without either, the gateway looks where the desktop app does: the policy's
License (key) and LicenseFile settings, WND_TERMINAL_LICENSE, next to the
system policy file, then wnd-terminal.license in data_dir.
[hosts]
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
allowed_networks |
none | Address ranges sessions may connect to, such as ["10.20.0.0/16", "192.0.2.10", "fd00:1::/64"]. See Which hosts people can reach. |
allowed_ports |
[23, 992] |
The ports for hosts people type themselves. Add 22 to let them type UNIX and Linux hosts for SSH. |
proxy |
none | The proxy every session goes through, when the gateway's network only lets connections out through one: "socks5://proxy.bank.example:1080", "socks4a://...", "http://proxy.bank.example:8080", "system" (the gateway computer's own settings: on Linux ALL_PROXY, HTTPS_PROXY, HTTP_PROXY and NO_PROXY) or "none". It takes the place of the policy's Proxy. A connection in the policy with its own Proxy keeps it. People in the browser can't choose a proxy. |
proxy_user, proxy_password / proxy_password_file |
none | The proxy's user name and password, if it asks for one (SOCKS5 user name and password, or HTTP Basic). The user name can also be in proxy (socks5://[email protected]:1080). The password is never logged. |
[limits]
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
max_sessions |
500 |
Sessions (terminals and printers) open on the gateway at once, for everyone. |
max_sessions_per_user |
10 |
Sessions one person may have open at once. |
max_browsers_per_user |
5 |
Browser windows one person may have connected at once. |
sign_in_attempts_per_minute |
10 |
Sign-in attempts from one address per minute (more are refused for a minute). |
failed_passwords_before_lockout |
5 |
Wrong passwords (local users) before that login waits 15 minutes. |
People who reach a limit are told in plain words ("You have 10 sessions open, the most your organization allows for one person. Close one to open another.").
[roaming]
Sessions that belong to the person, not the browser window. See Roaming sessions and docs/roaming.md.
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
enabled |
true |
Sessions belong to the person, and continue on any of their devices. false: each browser window has its own sessions, which close with it. |
keep_minutes |
480 |
How long a session nobody has open keeps running (1 to 10080). |
max_sessions_per_person |
8 |
The most sessions one person can leave running with no window open. The one left longest closes first. |
[helpdesk]
The help desk joining a person's session from a ticket, with their consent. Off without this section. See The help desk joining a session and docs/help-desk.md.
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
group |
none | The help desk's group, from the identity provider's groups claim or a local user's groups. Local users can also have helpdesk = true. |
join_minutes |
15 |
How long a ticket's Join live link works (1 to 1440). |
max_minutes |
60 |
The longest the help desk can stay with a session (1 to 480). |
[ai]
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
api_key / api_key_file |
none | The key for the AI model server in the policy's AIEndpoint, if it needs one. In a browser, people can't set their own AI server or key. |
[import]
Switch day: each person's set-up from the switch pack, added the first time they open WND Terminal in the browser. See docs/switch-day.md.
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
first_run_url |
none | A folder on the gateway, or an https:// address, with the pack's users files. The gateway reads <sign-in name>.wndconfig for the person who signed in (the name lower case, without DOMAIN\ or @domain). The policy's FirstRunImport is for desktops and isn't used here. |
first_run_once |
true |
false imports a person's file again whenever it changes. |
The browser remembers what it imported, so a person who uses two browsers gets
their set-up in each. The audit log has import.firstrun for each import.
[embed]
Terminals inside your own web applications. See Terminals in your own web applications and docs/embed-sdk.md.
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
allowed_origins |
(required) | The web applications that may show embedded terminals, as origins, such as ["https://crm.bank.example"]. |
secret / secret_file |
HS256: the secret your application's server signs embed tokens with (at least 32 characters), or a file holding it. | |
public_key_file |
RS256: the public key (PEM) of the key pair your application's server signs embed tokens with. Give this or a secret. | |
issuer |
none | If set, every embed token's iss must be this. |
allow |
["keyboard", "read", "write"] |
What embedded terminals may do: the person types in them (keyboard), the page reads the screen (read), the page types and sends keys (write). |
connections |
none (any) | The policy's connections an embedded terminal may open, by name. |
max_token_seconds |
300 |
The longest an embed token may be good for (1 to 3600). |
[sign_on]
Signing people on to z/OS and IBM i with RACF PassTickets. See Signing on to hosts with PassTickets.
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
user_id_claim |
none | The ID token claim that holds each person's host user ID, such as "mainframe_user_id". |
user_id_file |
none | A file of sign-in names and host user IDs, for people without the claim and for local users. |
Each connection that signs on with a PassTicket is a [[sign_on.passticket]]:
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
connection |
none | The connection's name in the policy, such as "Production (TSO)". |
host / port |
none / any port | Or the host name (and port) the session connects to. |
application |
(required) | The PassTicket application name: the RACF PTKTDATA profile, such as "TSOPROD" or "CICSPROD", or the one IT set up on IBM i. |
algorithm |
"legacy" |
"legacy" (DES) or "enhanced" (HMAC-SHA-512), as the profile is set up. |
characters |
"upper" |
Enhanced PassTickets only: "upper" or "mixed", as the profile's TYPE. |
key_file / key_env |
(one is required) | The secured sign-on key in hexadecimal: a file only the gateway's account can read, or an environment variable. |
steps |
(required) | What to do on the logon screens, in WND Terminal's macro language (wait, type, press, move), typing {user} (the host user ID) and {passticket}. |
[mcp]
AI agents working sessions through the gateway's MCP endpoint, /mcp. Off
unless enabled. See AI agents and
docs/mcp.md.
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
enabled |
false |
Agents may connect at /mcp. |
mode |
"readonly" |
What agents may do: "readonly", "ask" (each change waits at /approvals) or "allow". With the policy's AgentActionsMode set too, the stricter one wins. |
approvers_group |
none | The group (from the identity provider, or a local user's groups) whose members approve agents' changes at /approvals. None: each person approves their own agent's changes. |
approval_timeout_seconds |
300 |
How long a change waits for an answer (1 to 3600). No answer is no. |
sandbox_only |
false |
Every session agents open plays a recording from sandbox_folder on a replay host, never a real host. |
sandbox_folder |
none | The recordings (.wndrec) the sandbox plays, and flows (.wndflow) to go with them. Required when anything is in the sandbox. |
audiences |
none | Audiences accepted in people's tokens besides sign_in.oidc.client_id, such as "api://wnd-gateway" for an access token. |
Each program that works on its own, not as a person, is a
[mcp.service_accounts.NAME] (the name is letters, digits, -, _ and .):
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
token_sha256 |
(required) | The SHA-256 of its token, in hexadecimal. wnd-gateway new-agent-token NAME makes a token and prints this. |
connections |
none | The policy's connections it may open, by name (* and ? allowed). None: those AgentConnections allows. With both, both must allow it. |
flows_folder |
none | A folder of flows that become its tools, besides the policy's AgentFlowFolders. |
mode |
mcp.mode |
Stricter than mcp.mode for this account. It can't be less strict. |
sandbox_only |
false |
Only the sandbox for this account. |
[privileged]
Privileged access: keys that wait for a second person's approval, windows of
elevated access, and the review queue. The rules are the policy's
PrivilegedRules, then the ones in rules_file. See
docs/privileged-access.md.
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
rules_file |
none | More rules (a JSON list), after the policy's. wnd-gateway check-privileged FILE checks it. |
approvers_group |
mcp.approvers_group |
The group whose members approve held keys and elevated access at /approvals, when a rule names no group of its own. Nobody ever approves their own request. |
reviewers_group |
none | The group whose members review privileged recordings at /review. |
approval_timeout_seconds |
the policy's PrivilegedApprovalTimeout, else 300 |
How long a held key waits for an approver (1 to 86400). No answer is no. |
max_elevation_minutes |
120 |
The longest window of elevated access a person may ask for (1 to 1440). |
recordings_folder |
the gateway's recordings | Where /review reads recordings. |
webhook_url |
none | Each new approval request is posted here as JSON (its number, kind, rule, who and a link to /approvals, but never the screen), for a chat channel or a ticket flow. |
[api]
Gateway configuration at /api/v1, with an OpenAPI description. Off
unless enabled. See Screen flows as APIs and
docs/flow-apis.md. Programs use the tokens of
[mcp]: a service account's ([mcp.service_accounts]) or a person's.
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
enabled |
false |
Programs may call flows at /api/v1. |
mode |
"readonly" |
What flows that change data (writes: yes) may do: "readonly" (refused, 409), "ask" (an approver decides at /approvals first, and the call answers 202) or "allow". The policy's AgentActionsMode and a service account's mode hold too, and the stricter one wins. |
flows_folder |
none | The folder of flows served, besides the policy's AgentFlowFolders. With mcp.sandbox_only, the flows in mcp.sandbox_folder too. |
connection |
none | The policy's connection a flow runs in when its header has no #! connection: line. |
public_url |
the gateway's address with /api/v1 |
The API's address in the OpenAPI document (servers). |
title |
"company on the license mainframe API" | The OpenAPI document's title. |
max_sessions_per_connection |
4 |
Sessions open at once for a caller (a person or a service account) on a connection, for flows that start on the same screen (the same reset macro). From 1 to 100. |
pool_idle_minutes |
15 |
A pooled session nobody used for this long closes (1 to 1440). |
queue_timeout_seconds |
30 |
How long a call waits for a session when every one is busy (0 to 600). Then it's answered 503. |
max_queue |
50 |
The most calls waiting for a session, for one caller and one connection. More are answered 503 at once. |
mcp.sandbox_only (or a service account's sandbox_only) applies to the API
too: its sessions play recordings from mcp.sandbox_folder on replay hosts.
[tables]
Screen tables that Excel, Power BI, Tableau and scripts read as an OData feed
at /odata. Off unless folder is set. See Screen tables
and docs/screen-tables.md.
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
folder |
none | The folder of screen tables: flows (.wndflow) with a table: line. The feed lists every table in it, and it's read again within 10 seconds of a change. |
cache_seconds |
60 |
How long an answer is kept for the same caller, table and parameters, so a Power BI refresh that asks twice reaches the host once. 0 keeps nothing, and 86400 is the most. |
Callers sign in as for /api/v1: a service account's token or a person's
token as a bearer token, or, at /odata only, a service account's name and
token with Basic authentication, which is what Excel and Power BI ask for.
The tables run in the same sessions as screen flows served as APIs, so the
[api] session settings and the sandbox hold for them too.
[insight]
Usage intelligence: desktops send their response time records to /collect,
and people in viewers_group see the usage report at /insights. Off without
this section. Your license must include Usage intelligence. See
Usage intelligence and
docs/usage-intelligence.md.
On a small screen, swipe the table to read all columns.
| Setting | Default | What it does |
|---|---|---|
collector_token_sha256 |
none | The SHA-256 of the token desktops send (the policy's ResponseTimeCollectorToken). wnd-gateway new-collector-token makes a token and prints this. None: /collect takes nothing, and the report has the gateway's own sessions only. |
keep_days |
400 |
Days of records kept (1 to 3660). Older day files are removed once a day. |
viewers_group |
none | The group (from the identity provider, or a local user's groups) whose members see the report at /insights. None: nobody does. |
service_accounts |
none | Service accounts from [mcp.service_accounts], by name, that may read /insights/data.json with their token (Power BI, Excel). |
folders |
none | More folders of day files to include, such as the policy's ResponseTimeFolder. |
max_mb_per_day |
1024 |
The most /collect takes in a day, in megabytes, from every desktop together. Past it, desktops are asked to wait and keep their records to send later. |
Signing on to hosts with PassTickets
People who signed in to the gateway can be signed on to z/OS (TSO, CICS, IMS and anything else RACF, ACF2 or Top Secret protects) and to IBM i without typing a host password. When someone opens a connection set up here, the gateway:
- finds their host user ID: the
user_id_claimfrom their ID token, else the line for their sign-in name inuser_id_file; - waits for the logon screen and plays the connection's
steps; - right before
{passticket}is typed, makes a PassTicket for that user ID and the connection'sapplicationfrom the secured sign-on key, and types it into the password field.
A PassTicket works once, for a short time (10 minutes for the legacy kind, as
the profile's TIMEOUT says for the enhanced kind), so a ticket that's seen
is of no use later. The person sees the session log on by itself. If they
have no host user ID, or something on the logon screen isn't what the steps
expect, the session stays on the logon screen and the window says why.
[sign_on]
user_id_claim = "mainframe_user_id"
user_id_file = "/etc/wnd-gateway/user-ids.toml"
[[sign_on.passticket]]
connection = "Production (TSO)"
application = "TSOPROD"
key_file = "/etc/wnd-gateway/tsoprod.key"
steps = """
wait "Logon ===>"
type {user}
press Enter
wait "ENTER PASSWORD"
type {passticket}
press Enter
"""
[[sign_on.passticket]]
connection = "Payroll (IBM i)"
application = "PAYROLL"
algorithm = "enhanced"
key_env = "WND_PAYROLL_PASSTICKET_KEY"
steps = """
wait "Sign On"
type {user}
press FieldExit
type {passticket}
press Enter
"""
The user ID file is TOML, one line per person, the sign-in name in quotes:
"[email protected]" = "ALICE01"
"bob" = "BOB02"
Setting it up on the host. On z/OS, define the application's PassTicket
profile with the key, as for any PassTicket program (RDEFINE PTKTDATA TSOPROD
SSIGNON(KEYMASKED(...)), or KEYENCRYPTED / EPTKEYLABEL for an ICSF key
and enhanced PassTickets, then SETROPTS RACLIST(PTKTDATA) REFRESH), and allow
the gateway's sign-ons as your security team requires. The key in
key_file or key_env is the same key, in hexadecimal: 8 bytes (16 digits)
for legacy PassTickets, 32 to 256 bytes for enhanced ones. On IBM i, set up
the application's PassTicket key as IBM documents for your release (the same
legacy algorithm as QSYGENPT). User IDs and application names are 1 to 8
characters (A-Z, 0-9, #, $, @); IBM i user profiles longer than 8 characters
can't be signed on this way.
The key stays on the gateway. It is read when the gateway starts and kept
in memory only (wiped when the gateway stops). It's never written to the
gateway's log or the audit log, never part of a message, and never sent to a
browser. The PassTicket is made on the gateway, typed into the host's hidden
password field, and wiped. The browser gets the screen, which hides that
field. Keep key_file readable by the gateway's account only (chmod 600),
or use key_env with your service manager's secret store. wnd-gateway
check-config reads the keys and the user ID file and says if something is
wrong, without showing the key.
The audit log records every automatic sign-on as session.auto_sign_on:
who (the person signed in), the host user ID (host_user), the
application, the method (PassTicket) and the algorithm, and whether it
worked. Never the key or the ticket.
Checked against IBM's documentation, once more against a real host. Both algorithms are written from IBM's description (z/OS Security Server RACF Macros and Interfaces, and APAR OA59196 for the enhanced one). IBM publishes no complete worked example, so WND Terminal's tests check its code against a second, separately written implementation and against IBM's one published value (the legacy translation step). Before relying on it, sign on once to a test RACF (or IBM i) with a key you define there, for each algorithm you use.