gbash

Running gbash in the Browser

gbash compiles to WebAssembly, letting you run a full shell runtime in the browser with no server-side execution.

Build pipeline

The WASM artifact is built from the Go source using the js/wasm target. The build produces two files:

  • gbash.wasm -- the compiled runtime
  • wasm_exec.js -- the Go WASM support script (from the Go toolchain)

The @ewhauser/gbash-wasm package handles building and bundling these artifacts. In the website project, pnpm build compiles the WASM from source and copies both files into public/.

Loading in a web app

Import the Bash class from @ewhauser/gbash-wasm/browser:

import { Bash } from "@ewhauser/gbash-wasm/browser";
 
const bash = new Bash({
  cwd: "/home/agent",
  env: { TERM: "xterm-256color" },
  files: {
    "/home/agent/hello.txt": "Hello from gbash!\n",
  },
});
 
const result = await bash.exec("cat /home/agent/hello.txt");
console.log(result.stdout);   // "Hello from gbash!\n"
console.log(result.stderr);   // ""
console.log(result.exitCode); // 0

The runtime loads lazily on first use. It fetches wasm_exec.js and gbash.wasm, boots the Go runtime, and waits for window.GBashWasm to register. Subsequent Bash instances reuse the same loaded runtime.

You can override asset URLs if you host the files elsewhere:

const bash = new Bash({
  wasmUrl: "/assets/gbash.wasm",
  wasmExecUrl: "/assets/wasm_exec.js",
});

Pre-seeding files

Pass a files map in the constructor to populate the in-memory filesystem before execution:

const bash = new Bash({
  files: {
    "/data/config.json": '{"key": "value"}',
    "/scripts/setup.sh": "echo ready",
  },
});

You can also write files after initialization:

bash.writeFile("/data/input.csv", csvContent);

Custom commands in JavaScript

Register custom commands that execute as JavaScript functions:

import { Bash, defineCommand } from "@ewhauser/gbash-wasm/browser";
 
const bash = new Bash({
  customCommands: [
    defineCommand("now", async () => ({
      stdout: new Date().toISOString() + "\n",
      stderr: "",
      exitCode: 0,
    })),
  ],
});
 
const result = await bash.exec("now");

Custom commands are matched before the WASM runtime for simple command invocations, so they can override or extend built-in commands.

CSP headers

WebAssembly compilation requires the wasm-unsafe-eval CSP directive. Add it to your Content-Security-Policy header:

Content-Security-Policy: script-src 'self' 'wasm-unsafe-eval';

Without this directive, browsers will block WASM instantiation.

Cleanup

Dispose of the shell when you are done to release WASM resources:

await bash.dispose();

Reference

The website directory contains a full Next.js app running gbash in the browser via WASM, including terminal UI integration.