Docs

Embed kit: a terminal inside your own web apps

Show a live mainframe or IBM i session inside a portal, a CRM or a help desk tool, and let the page read the screen and type into it, as far as IT allows.

In short

Your page loads the embed kit from your own WND Terminal gateway and calls WndTerminal.mount(). Your server signs a short-lived token for the person signed in. The gateway shows the terminal only inside pages IT lists, and your page can read the screen, type and react when it changes.

The embed kit puts a live WND Terminal session inside a web application of your own, such as a customer portal, a CRM, a help desk tool or an intranet page. The person uses the mainframe or IBM i screen right there. Your page can read the screen, type into it and react when it changes, so a CRM can open the customer’s record on the mainframe when an agent picks a customer.

It runs on WND Terminal in a browser. Everything the gateway does for its own page holds for an embedded terminal too, including your policy, masking, which hosts can be reached, limits, seats and the audit log.

How it works

  1. Your page loads the kit from your gateway and calls WndTerminal.mount(). The kit adds a frame whose page comes from the gateway.
  2. Your server signs an embed token for the person signed in to your application. It says who they are, which page may show the terminal, and for how long.
  3. The gateway checks the token and opens the session. The terminal works in the frame as it does in WND Terminal itself.
  4. Your page asks the frame for things, such as getScreenText() or sendKeys(), and the gateway decides what your page may do.

Your page never holds the session or talks to the host. It talks only to the frame, and only in the ways the gateway allows.

1. Turn it on in the gateway

Add an [embed] section to gateway.toml, with the address of each web application that may show terminals and a secret your server and the gateway share.

[embed]
allowed_origins = ["https://crm.bank.example"]
secret_file = "/etc/wnd-gateway/embed-secret"     # openssl rand -base64 48
allow = ["keyboard", "read", "write"]
connections = ["Production (TSO)"]

Then check it with wnd-gateway check-config and restart the gateway.

On a small screen, swipe the table to read all columns.

SettingDefaultWhat it does
allowed_originsRequiredThe web applications that may show embedded terminals, such as "https://crm.bank.example"
secret or secret_fileThe shared secret for HS256 tokens, at least 32 characters
public_key_fileFor RS256 tokens, the public key of your server’s key pair, so the gateway holds nothing that can make tokens
issuerNoneIf set, every token’s iss must match it
allow["keyboard", "read", "write"]What embedded terminals may do
connectionsAnyThe policy’s connections an embedded terminal may open
max_token_seconds300The longest a token may be good for

2. Make embed tokens on your server

Add an endpoint that answers only people signed in to your application and returns a token for that person. An embed token is a JSON Web Token signed with HS256 or RS256, so any language with HMAC-SHA-256 or a JWT library can make one. In Node.js:

import { createHmac, randomUUID } from "node:crypto";

export function embedToken({ gateway, origin, user, secret, connection, seconds = 60 }) {
  const now = Math.floor(Date.now() / 1000);
  const claims = { sub: user, aud: gateway, origin, connection, iat: now, exp: now + seconds, jti: randomUUID() };
  const b64 = (o) => Buffer.from(JSON.stringify(o)).toString("base64url");
  const signed = `${b64({ alg: "HS256", typ: "JWT" })}.${b64(claims)}`;
  return `${signed}.${createHmac("sha256", secret).update(signed).digest("base64url")}`;
}

On a small screen, swipe the table to read all columns.

ClaimNeededWhat it is
subYesThe person’s sign-in name. Their seat, usage and audit log are under it.
audYesThe gateway’s address. A token for another gateway is refused.
originYesThe page that may show the terminal. The token is refused anywhere else.
iat, expYesWhen it was made and when it expires. A minute is plenty, since the token is used only to start.
jtiYesA unique ID. Each token works once.
connectionNoThe only connection this terminal may open
allowNoNarrows what this terminal may do. It can’t allow more than the gateway does.
name, groupsNoThe person’s name for the logs, and their groups for session recording

wnd-gateway check-embed-token says whether the gateway would take a token, and why not, without using it up.

3. Show the terminal in your page

Every gateway serves the kit at /sdk/wnd-terminal-embed.iife.js.

<div id="terminal"></div>
<script src="https://terminal.bank.example/sdk/wnd-terminal-embed.iife.js"></script>
<script>
  const term = WndTerminal.mount(document.getElementById("terminal"), {
    gateway: "https://terminal.bank.example",
    session: "Production (TSO)",
    token: () => fetch("/api/wnd-embed-token").then((r) => r.text()),
  });
  term.on("screenChanged", async () => {
    if ((await term.getScreenText()).includes("READY")) console.log("At the TSO READY prompt");
  });
</script>

Give token as a function, so each connect gets a fresh token. The same terminal is also an HTML element, <wnd-terminal gateway="…" session="…">, with the same methods and events.

4. Let your page frame the gateway

If your application sends a Content Security Policy, allow frames from the gateway, and its script.

Content-Security-Policy: frame-src https://terminal.bank.example; script-src 'self' https://terminal.bank.example

What an embedded terminal may do

On a small screen, swipe the table to read all columns.

PermissionWhat it allows
keyboardThe person types in the terminal and clicks on its screen.
readYour page reads the screen with getScreenText(), getFields() and waitForText().
writeYour page types and sends keys with typeText(), sendKeys() and setCursor().

With only keyboard, people use the terminal in your page and your page can’t read or type in it. With only read, it’s a view your page reads and nobody types in. Macros, file transfer, Fill from Excel, Ask WND, printers and connecting to other hosts aren’t part of an embedded terminal. A call that isn’t allowed is refused with the code not_allowed and recorded in the audit log.

The JavaScript API

On a small screen, swipe the table to read all columns.

MethodWhat it does
connect(), disconnect()Signs in with a token and opens the session, or closes it
sendKeys(keys)Text and keys written as SendKey codes, such as "LOGON TSO@E"
typeText(text)Types text exactly as it is
getScreenText()The screen’s text. Password fields are blank and card numbers are masked.
getFields()The screen’s fields with row, column, length, protected, numeric and text
setCursor(row, col)Moves the cursor
waitForText(text, options)Waits until text appears, or is gone
getStatus()Connected, rows, columns, cursor and whether the keyboard is locked
on(event, handler)Events ready, connected, screenChanged, disconnected and error
setTheme(), fit(), focus(), destroy()Light or dark, resize, take the keyboard, remove the terminal

Security

  • Only the pages IT allows can show it. The gateway serves the embed page only to an origin in allowed_origins, with a Content Security Policy whose frame-ancestors is exactly that origin.
  • Messages are checked both ways. The kit takes messages only from its own frame at the gateway’s origin, and the frame only from the page it’s in.
  • Tokens are short-lived and work once. A token goes to the frame in a message, never in an address, so it isn’t in any server’s log. It’s refused when its signature, audience, origin, expiry or ID isn’t right.
  • No cookies. Browsers that block third-party cookies make no difference, and another site can’t use a person’s gateway sign-in.
  • Your page never sees passwords. Password fields are never given out, card numbers are masked, and with AllowCopy off your page can’t read the screen’s text.

The audit log

The gateway records each embedded terminal under the token’s person, with the page it was shown in. The events are embed.start, embed.refused with the reason, embed.blocked for calls that weren’t allowed and embed.end, next to the usual session events. Tokens and secrets are never logged.

Troubleshooting

  • Run wnd-gateway check-config first, then wnd-gateway check-embed-token with a token from your server.
  • frame_refused means the page’s origin isn’t in allowed_origins exactly, or the browser can’t reach the gateway.
  • token_signature means the secret or key pair differs between your server and the gateway. token_expired usually means the two clocks differ by more than 30 seconds. token_used means a token was used twice.
  • not_allowed means the gateway’s or the token’s allow, or its connections, don’t include what was asked.

Questions

Can our page read passwords from the screen?

No. Password fields are never given out, card numbers are masked as your policy says, and with AllowCopy off the page can’t read the screen’s text at all.

Which pages can show the terminal?

Only the origins IT lists in the gateway’s allowed_origins. Browsers refuse to show the terminal inside any other page.

How long does an embed token last?

As long as your server says, up to max_token_seconds (300 by default). Each token works once, only to start the terminal, so a minute is plenty.

Where does the kit’s script come from?

From your own gateway, at /sdk/wnd-terminal-embed.iife.js. Nothing is loaded from WND Software.

See it on your own mainframe.

Install WND Terminal on a handful of PCs or Macs, import your sessions and keymaps, and let a real team use it next to what you run today. Free, two to four weeks.