NetStacksNetStacks

Session Sharing

Enterprise

Share a live terminal session with teammates for read-only viewing or read-write collaboration, with expiry, viewer limits, and instant revocation.

Overview

Enterprise (Controller) feature

Session sharing is available when NetStacks is connected to a Controller (enterprise mode). The share controls live in the Share Session dialog; a generated share link opens a lightweight, in-app viewer served from the Controller origin.

Session Sharing lets you hand a teammate a live view of one of your terminal sessions. You create a short-lived share link from the session’s tab, send it to a colleague, and they join a minimal viewer that mirrors your terminal in real time. Depending on the permission you choose, the joiner either watches read-only or can type into the same session alongside you.

Key Capabilities

  • Read-only or read-write — choose read-only (viewer watches output only) or read-write (joiner can type commands into the shared session) when you create the link.
  • Configurable expiry — each link has a time-to-live in minutes (5 to 1440, default 60). When it elapses, the link stops working.
  • Viewer cap — set a maximum number of concurrent viewers (1 to 50, default 5) per link.
  • Live viewer count — the session tab shows a share indicator and a badge with the number of connected viewers, refreshed while the dialog is open.
  • Instant revoke — revoke a single link or all links for the session at once; connected viewers are dropped.

Use Cases

  • Pair troubleshooting — a senior engineer joins read-only to observe, or read-write to take the keyboard.
  • Training and demos — share a read-only link so trainees follow a procedure live.
  • Change review — let an approver watch each step of a maintenance window as it happens.
  • NOC handoffs — the outgoing engineer shares an active session with the incoming engineer for a clean handoff.
Sensitive data is visible to viewers

Anyone holding a valid link sees everything rendered in the terminal, including passwords, keys, or secrets that appear on screen. With a read-write link the joiner can also run commands. Prefer read-only unless collaboration is required, keep TTLs short, and revoke the moment you are done.

How It Works

A share link is bound to a single live session and carries an opaque token. The joiner’s viewer opens a WebSocket back to the Controller, which fans the session’s PTY output out to every connected viewer and (for read-write links) feeds their keystrokes back into the same session.

The Share Token and Link

Creating a share returns a token, an expires_at timestamp, and a share_url. The link is the current app URL with the token in the fragment, for example https://controller.example.com/#share=<token> (or /terminal/#share=<token> when the app is served under a path). Because the token lives in the URL fragment, it is not sent to the server as part of the page request — it is read by the app and used to open the viewer WebSocket.

The Viewer

Opening a share link boots a minimal, single-purpose view (no full app chrome). It connects to the Controller’s shared-session WebSocket endpoint and renders the live terminal with xterm.js. A small toolbar shows a connection-status dot and a permission label: Viewing for read-only or Collaborating for read-write. There is no scrollback of pre-join history — the viewer shows output from the moment it connects forward.

WebSocket Protocol

The viewer speaks a compact binary protocol on the shared-session socket. Every frame is one type byte followed by the payload:

TypeByteDirectionMeaning
DATA0x01bothTerminal output; or keystrokes from a read-write joiner
RESIZE0x02viewer → serverNew cols/rows (two uint16 values)
PING / PONG0x03 / 0x04bothKeepalive; viewer replies PONG to a PING
CLOSE0x05server → viewerSession has ended
ERROR0x06server → viewerUTF-8 error text (invalid/expired link, etc.)
SESSION_INFO0x08server → viewerJSON with the granted permission and a viewer id

The viewer only forwards keystrokes after it receives a SESSION_INFO frame whose permission is read-write; otherwise input is dropped client-side, keeping read-only links strictly observational.

Output mirroring, not a recording

Sharing streams the live session only. To capture a session for later playback, use Session Recording, which is independent of sharing.

Step-by-Step Guide

Create and Send a Share Link

  1. Open a terminal session to a device.
  2. Right-click the session tab and choose Share Session.
  3. In the dialog, click New Share Link, then set the Permission (Read Only or Read Write), Expiry (minutes), and Max Viewers.
  4. Click Create Share Link. The generated URL appears with a Copy button.
  5. Copy the link and send it over your usual channel. The session tab now shows a share indicator.
Defaults

New links default to Read Only, a 60-minute expiry, and a cap of 5 viewers. Expiry accepts 5–1440 minutes and the viewer cap 1–50.

Join a Shared Session

  1. Open the share link the owner sent you.
  2. The minimal viewer connects and shows the live terminal. The toolbar shows a status dot and either Viewing (read-only) or Collaborating (read-write).
  3. If the link granted read-write, you can type into the same session; with read-only, your keystrokes are ignored.
Note

The viewer streams from the moment you connect — there is no pre-join scrollback. If the link is invalid or expired, the viewer shows an “Unable to Join Session” message.

Manage and Revoke Shares

  1. Reopen the dialog from the tab’s share indicator (or right-click → Share Session).
  2. The Active Shares list shows each link with its permission badge, expiry time, and a truncated token. The list refreshes every few seconds while the dialog is open.
  3. Use Copy to re-copy a link, Revoke to drop a single link, or Revoke All to remove every link for the session.
Revocation is immediate

Revoking invalidates the token and disconnects anyone using it, with no grace period. Revoke before displaying anything sensitive you do not want viewers to see.

API Reference

The dialog drives a small REST surface on the Controller. Paths are shown relative to the API client’s base URL; substitute a real session id for {sessionId} and a real token for {token}. These examples mirror the request and response shapes the app actually uses.

Create a Share Link

create-share.shbash
# POST /sessions/{sessionId}/share
curl -X POST "$API/sessions/$SESSION_ID/share" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "permission": "read-only",
    "ttl_minutes": 60,
    "max_viewers": 5
  }'

# Response
{
  "share_url": "https://controller.example.com/#share=8f3a2c1b9d...",
  "token": "8f3a2c1b9d...",
  "expires_at": "2026-06-16T15:30:00Z"
}

permission is read-only or read-write. ttl_minutes (5–1440) and max_viewers (1–50) are optional and default to 60 and 5 respectively.

List Active Shares

list-shares.shbash
# GET /sessions/{sessionId}/share
curl -s "$API/sessions/$SESSION_ID/share" \
  -H "Authorization: Bearer $TOKEN" | jq '.'

# Response: an array of active shares
[
  {
    "token": "8f3a2c1b9d...",
    "permission": "read-only",
    "max_viewers": 5,
    "created_at": "2026-06-16T14:30:00Z",
    "expires_at": "2026-06-16T15:30:00Z"
  }
]

Get One Share

get-share.shbash
# GET /sessions/{sessionId}/share/{token}
curl -s "$API/sessions/$SESSION_ID/share/$SHARE_TOKEN" \
  -H "Authorization: Bearer $TOKEN" | jq '.'

# Response
{
  "session_id": "sess_abc123",
  "token": "8f3a2c1b9d...",
  "permission": "read-only",
  "max_viewers": 5,
  "viewer_count": 3,
  "expires_at": "2026-06-16T15:30:00Z",
  "created_by": "[email protected]"
}

Revoke a Share

revoke-share.shbash
# DELETE /sessions/{sessionId}/share/{token}
# Disconnects anyone currently using the link. Returns 2xx with no body.
curl -X DELETE "$API/sessions/$SESSION_ID/share/$SHARE_TOKEN" \
  -H "Authorization: Bearer $TOKEN"

Connect as a Viewer (WebSocket)

The viewer opens the shared-session socket with the token as a query parameter and exchanges the binary frames described in How It Works:

viewer-ws-url.txttext
wss://controller.example.com/ws/shared?share=<token>

# Frame layout: [1 type byte][payload]
#   0x01 DATA          terminal output (server -> viewer)
#                      keystrokes      (viewer -> server, read-write only)
#   0x02 RESIZE        uint16 cols, uint16 rows (viewer -> server)
#   0x03 PING / 0x04 PONG   keepalive
#   0x05 CLOSE         session ended
#   0x06 ERROR         UTF-8 error text
#   0x08 SESSION_INFO  JSON: {"type","session_id","permission","viewer_id"}
Endpoints may vary by version

Exact base paths can differ between Controller releases. If a call does not resolve, capture the request the app makes from your browser’s network panel and match it.

Q&A

Q: Can a joiner type into my session?
A: Only if you create the link with the read-write permission. With read-only (the default) the joiner can watch output but their keystrokes are dropped client-side. The viewer toolbar shows Collaborating for read-write and Viewing for read-only.
Q: How long does a share link last?
A: For the time-to-live you set when creating it — between 5 and 1440 minutes, defaulting to 60. After it expires the token stops working and the viewer shows an “Unable to Join” error. You can also revoke at any time.
Q: How many people can view at once?
A: Up to the max_viewers cap on the link (1–50, default 5). You can create multiple links for the same session, each with its own permission, expiry, and cap.
Q: Do viewers see what happened before they joined?
A: No. The viewer streams live output from the moment it connects. For earlier content, use Session Recording.
Q: How do I know someone is watching?
A: The session tab shows a share indicator while any link is active, plus a viewer-count badge when one or more viewers are connected. Click the indicator to reopen the dialog and manage or revoke links.
Q: Where does the share link open?
A: In the same NetStacks web app served by the Controller. The token rides in the URL fragment (/#share=<token>), which boots a minimal viewer view rather than the full app.
Q: What happens when I revoke?
A: The token is invalidated immediately and anyone using it is disconnected. Use Revoke All to clear every link for the session in one action.

Troubleshooting

“Unable to Join Session” in the Viewer

  • The link has expired (past its ttl_minutes) or was revoked. Ask the owner to create a fresh link.
  • The viewer cap is reached — later joiners are turned away until a slot frees up or the owner raises max_viewers on a new link.
  • The token in the URL is incomplete (truncated on copy/paste). Copy it again from the dialog.

Viewer Cannot Connect

  • WebSocket upgrades are blocked. Confirm the network path allows WSS to the Controller; some proxies and firewalls drop Upgrade requests.
  • A reverse proxy (nginx, HAProxy) in front of the Controller must be configured to proxy WebSockets with generous idle timeouts.
  • The Controller hosting the session must be reachable from the viewer’s network for the share URL to resolve.

No Live Output / Frozen Viewer

  • If the owner’s session ended, the viewer receives a CLOSE frame and shows “Session has ended.” Reconnecting will not help until a new session and link exist.
  • A dropped keepalive can stall the socket. Reload the share link to re-establish the connection.

Read-Write Joiner Cannot Type

  • The link was created as read-only. Permission is fixed at creation — issue a new read-write link.
  • Input is only forwarded after the SESSION_INFO frame arrives; if it never does, the socket likely failed to authorize the token.
If a link leaks

If a share link reaches the wrong hands, revoke it immediately from the dialog (or via the DELETE endpoint). Revocation disconnects every viewer at once. Keep TTLs short and prefer read-only to limit exposure.

  • Terminal Overview — the terminal interface where the Share Session dialog lives.
  • Session Recording — capture a session for playback, complementing live sharing.
  • Multi-Tab — manage multiple sessions; share any one from its tab.
  • Session Context — metadata attached to sessions you may be sharing.
  • Audit Logs — review administrative and access events in the Controller.