
Company News
Socket Joins New OpenJS Program to Fund Node.js Security Work
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.
Embeddable WebSocket bridge for running local Pi coding-agent RPC sessions from Node.js apps.
Embeddable Node.js WebSocket bridge for running local
pi coding
agent RPC sessions from browsers, internal tools, dashboards, and custom
automation.
pi-ws is library-first: you embed PiWs in your own Node.js process, keep
control over auth and routes, and get a built-in /ws/pi bridge that starts one
local Pi subprocess per WebSocket connection.
browser/client ── WebSocket JSON ──▶ pi-ws ── JSONL stdin/stdout ──▶ pi --mode rpc
/ws/pi.uWebSockets.js installers.bwrap or firejail.c12-based config loading for binaries and deployments.pino logs and example launchers with pino-pretty.pnpm add pi-ws
npm install pi-ws
Node.js >=22.19.0 is required.
import { PiWs } from 'pi-ws';
const pipe = new PiWs({
host: '127.0.0.1',
port: 8787,
chatExample: false,
});
pipe.handle({
method: 'get',
path: '/health/application',
handler: (res) => {
res
.writeHeader('content-type', 'application/json')
.end(JSON.stringify({ ok: true }));
},
});
await pipe.listen();
The built-in Pi bridge is available at:
ws://127.0.0.1:8787/ws/pi
Clients send Pi RPC JSON objects as WebSocket text frames. pi-ws validates
each frame as a JSON object, forwards it to Pi as JSONL, parses Pi stdout back
to JSON objects, and sends responses back as WebSocket text frames.
pi-ws does not implement mandatory server auth and does not enable auth by
default. Applications decide how to protect the route using hooks, reverse
proxies, network policy, or their existing auth stack.
The package includes a small token-based reference implementation for projects that want a simple built-in option:
import { createStaticTokenAuthHook, PiWs } from 'pi-ws';
const pipe = new PiWs();
pipe.addHook(
'onAuth',
createStaticTokenAuthHook({
token: process.env.PI_WS_AUTH_TOKEN ?? 'change-me',
queryParam: 'token',
createSession: async (request) => ({
clientAddress: request.remoteAddress ?? 'unknown',
}),
}),
);
await pipe.listen();
Browser clients that cannot send custom WebSocket upgrade headers can authenticate with the reserved first message:
{ "token": "change-me", "type": "pi_ws_auth" }
For HTTP routes, use the same token policy with protectHttpHandler():
import { protectHttpHandler, StaticTokenAuthorizer } from 'pi-ws';
const authorize = new StaticTokenAuthorizer({
token: 'change-me',
queryParam: 'token',
}).authorize;
pipe.handle({
method: 'get',
path: '/api/private',
handler: protectHttpHandler({
authorize,
handler: (res) => {
res.end('ok');
},
}),
});
Artifacts are enabled by default. Each WebSocket session gets a private
artifact directory under the configured artifact root, and the Pi subprocess
receives the full path through PI_WS_ARTIFACT_DIR.
The browser does not need the absolute server path. The ready event exposes safe status only:
{
"artifactDirName": "s-8588573f292b",
"artifactsEnabled": true,
"sandboxMode": "process",
"type": "pi_ws_ready"
}
artifactDirName is only the last directory name, not the full path.
Small files are sent as one metadata frame plus one binary frame:
pi_ws_artifactLarge files are sent as chunk metadata plus binary frames:
pi_ws_artifact_startpi_ws_artifact_chunkpi_ws_artifact_endExample config:
const pipe = new PiWs({
artifacts: {
enabled: true,
dir: './.pi-ws/artifacts',
maxFileBytes: 25 * 1024 * 1024,
chunkSizeBytes: 256 * 1024,
logLevel: 'info',
logFile: './.pi-ws/pi-ws.log',
},
});
Generated files are discovered after they are stable on disk. Symlinks and paths outside the artifact root are ignored.
The default sandbox mode is process. It creates one session root under
sandbox.cwd and places cwd, HOME, and TMPDIR inside that root. This is
process-level isolation and prompt guidance, not an OS security boundary.
const pipe = new PiWs({
sandbox: {
mode: 'process',
cwd: './.pi-ws/sandbox',
envPolicy: 'minimal',
allowReadDirs: ['./inputs'],
allowWriteDirs: ['./scratch'],
denyServerDirectory: true,
},
});
Use system mode when you need OS-enforced isolation:
const pipe = new PiWs({
sandbox: {
mode: 'system',
command: 'bwrap',
args: [
'--ro-bind',
'{allowReadDirs}',
'--bind',
'{allowWriteDirs}',
'--chdir',
'{sandboxCwd}',
],
},
});
envPolicy: "minimal" forwards only a small provider-focused allowlist plus
sandbox.envAllowlist. Explicit sandbox.env values remain the supported way
to pass application-specific tool configuration, but protected sandbox
variables such as HOME, TMPDIR, and PI_WS_SANDBOX_CWD stay controlled by
the bridge.
PiWs keeps the extension surface intentionally small:
handle() adds HTTP routes.route() adds WebSocket routes.use() installs low-level uWebSockets.js handlers.addHook('onRequest', hook) runs pre-upgrade checks for /ws/pi.addHook('onAuth', hook) authenticates /ws/pi from upgrade metadata or the
reserved first WebSocket message.configurePi(), configureArtifacts(), configureSandbox(), and
configureTls() merge focused runtime config.Generated API docs:
The pi-ws binary is a thin wrapper around the library:
import { loadConfig, PiWs } from 'pi-ws';
const config = await loadConfig();
const pipe = new PiWs(config);
await pipe.listen();
Run after installation:
pi-ws
Configuration is resolved in this order:
loadConfig({ overrides }) valuesPI_WS_* environment variablespi-ws.config.* files in the current working directorypi-ws field in package.jsonExample pi-ws.config.ts:
import { definePiWsConfig } from 'pi-ws';
export default definePiWsConfig({
host: '127.0.0.1',
port: 8787,
pi: {
provider: 'openai',
model: 'gpt-4.1',
},
});
Core server:
PI_WS_HOST - bind host, default 127.0.0.1PI_WS_PORT - bind port, default 8787PI_WS_PREFIX - WebSocket prefix, default /wsPI_WS_MAX_PAYLOAD_BYTES - max inbound frame sizeReference token auth:
PI_WS_AUTH_TOKEN - enables the reference token auth hook for /ws/piPI_WS_AUTH_HEADER - token header, default authorizationPI_WS_AUTH_SCHEME - token scheme, default BearerPI_WS_AUTH_QUERY_PARAM - optional query-string token parameterPI_WS_AUTH_REALM - optional WWW-Authenticate realmPi subprocess:
PI_WS_PI_COMMAND - optional Pi command overridePI_WS_PI_ARGS - whitespace args or JSON string arrayPI_WS_PI_CWD - optional Pi subprocess cwdPI_WS_PI_AGENT_DIR - optional PI_CODING_AGENT_DIRPI_WS_PI_PROVIDER - Pi providerPI_WS_PI_MODEL - model id or patternPI_WS_PI_THINKING - thinking levelPI_WS_PI_NAME - session display namePI_WS_PI_SYSTEM_PROMPT - replace Pi system promptPI_WS_PI_APPEND_SYSTEM_PROMPT - string or JSON string arrayPI_WS_PI_EXTENSIONS - string or JSON string arrayPI_WS_PI_PROMPT_TEMPLATES - string or JSON string arrayArtifacts:
PI_WS_ARTIFACTS_ENABLED - enable/disable artifact transferPI_WS_ARTIFACTS_DIR - artifact rootPI_WS_ARTIFACTS_MAX_FILE_BYTES - max transfer sizePI_WS_ARTIFACTS_CHUNK_SIZE_BYTES - binary chunk sizePI_WS_ARTIFACTS_SCAN_INTERVAL_MS - discovery poll intervalPI_WS_ARTIFACTS_STABILITY_WINDOW_MS - file stability windowPI_WS_ARTIFACTS_LOG_LEVEL - pino log levelPI_WS_ARTIFACTS_LOG_FILE - pino log destinationSandbox:
PI_WS_SANDBOX_MODE - off, process, or systemPI_WS_SANDBOX_CWD - sandbox rootPI_WS_SANDBOX_ALLOW_READ_DIRS - JSON string arrayPI_WS_SANDBOX_ALLOW_WRITE_DIRS - JSON string arrayPI_WS_SANDBOX_ENV_POLICY - inherit, minimal, or allowlistPI_WS_SANDBOX_ENV_ALLOWLIST - JSON string arrayPI_WS_SANDBOX_ENV - JSON object of explicit env valuesPI_WS_SANDBOX_COMMAND - external wrapper command for systemPI_WS_SANDBOX_ARGS - wrapper argsTLS:
PI_WS_TLS_KEY_FILE / PI_WS_TLS_CERT_FILE - enable HTTPS/WSSPI_WS_TLS_CA_FILE - optional CA bundlePI_WS_TLS_PASSPHRASE - optional private-key passphrasePI_WS_TLS_DH_PARAMS_FILE - optional DH params filePI_WS_TLS_CIPHERS - optional OpenSSL cipher suite overridePI_WS_TLS_PREFER_LOW_MEMORY_USAGE - optional TLS memory tuning flagFrom this repository:
mise install
pnpm install
pnpm build
node examples/embedded-server.mjs
Open:
http://127.0.0.1:8787/examples/chat/
The guided chat launcher:
pnpm example:chat
See examples/README.md for provider keys, base URL configuration, auth, artifact previews, and sandbox directories.
mise install
pnpm install
pnpm test
Useful scripts:
pnpm dev - watch-mode binary entrypointpnpm demo - run the local demo serverpnpm build - build Node output and generated API docspnpm build:docs - regenerate API docspnpm lint - type, style, and dependency audit checkspnpm test - lint and unit testsRoute registration order:
/healthz/examples/chat/${wsPrefix}/pihandle()route()use()flowchart TD
Client[Browser or WS client]
UWS[uWebSockets.js]
Bridge[pi-ws bridge]
Pi[Pi CLI RPC subprocess]
Client -->|WS text JSON| UWS
UWS -->|message event| Bridge
Bridge -->|stdin JSONL| Pi
Pi -->|stdout JSONL| Bridge
Pi -->|stderr and lifecycle| Bridge
Bridge -->|WS text and binary frames| UWS
UWS -->|frames| Client
FAQs
Embeddable WebSocket bridge for running local Pi coding-agent RPC sessions from Node.js apps.
We found that pi-ws demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Company News
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.

Research
/Security News
A malicious Firefox extension fetches its payload after installation to evade detection, steal Google session cookies, and automate account takeover.