Qubittron Bastion
TypeScript SDKGuides

Environments

Where the Bastion SDK runs — Node, Bun, Deno, edge runtimes, and browsers — with the caveats for each.

The SDK is built against the Fetch standard. Anywhere fetch, Response, Request, and ReadableStream are globals, the SDK should work.

RuntimeStatusNotes
Node.js 20+✅ First-classfetch is global since Node 18, stable in 20.
Node.js 18⚠ WorksSame global fetch, but technically experimental. Upgrade to 20 if you can.
Bun✅ First-classDrop-in replacement for Node for SDK purposes.
Deno✅ WorksUse npm: specifier. Pass apiKey explicitly via Deno.env.get; the SDK's process.env.BASTION_API_KEY fallback only fires under Deno's Node-compat mode.
Cloudflare Workers✅ WorksNo process — pass apiKey explicitly via the binding.
Vercel Edge✅ WorksSame as Workers — pass apiKey explicitly.
Browsers (modern)⚠ Works but don'tYour API key would leak. Proxy through your server.
React Native⚠ Use with careRequires a fetch polyfill that supports streaming bodies if you stream.

Node.js

Nothing to configure:

import { Bastion } from "@qubittron/bastion-sdk";

const client = new Bastion(); // reads BASTION_API_KEY

Module formats: both ESM (.mjs) and CJS (.cjs) are shipped — TS types included for both.

Bun

Identical to Node:

const client = new Bastion({ apiKey: Bun.env.BASTION_API_KEY });

Cloudflare Workers / Vercel Edge

process.env does not exist on these runtimes. Pass the key explicitly from the binding/env:

export default {
  async fetch(req: Request, env: { BASTION_API_KEY: string }) {
    const client = new Bastion({ apiKey: env.BASTION_API_KEY });
    // ...
  },
};

The SDK uses no Node-only APIs (fs, crypto, buffer) on the hot path. Streaming via ReadableStream works.

Deno

import { Bastion } from "npm:@qubittron/bastion-sdk";

const client = new Bastion({ apiKey: Deno.env.get("BASTION_API_KEY") });

Always pass apiKey explicitly under Deno. The SDK's automatic process.env.BASTION_API_KEY fallback assumes a Node process object exists, which only happens under Deno's Node-compatibility mode — relying on it is fragile across Deno versions.

Browsers — don't ship your key

The SDK runs in browsers, but never put a real API key in browser-bundled code:

  • Static-site bundlers (Vite, Next.js client components, Astro) will embed any value you import from process.env.SOMETHING into the JS sent to the user.
  • Even a "short-lived" key visible in DevTools is a key in the wild.

The right pattern: proxy through your server.

// server (Hono, Express, Next.js route handler, etc.)
const client = new Bastion();
app.post("/api/chat", async (c) => {
  const body = await c.req.json();
  const res = await client.chat.completions.create(body);
  return c.json(res);
});

// browser
const res = await fetch("/api/chat", {
  method: "POST",
  body: JSON.stringify({ model: "gpt-oss-120b", messages }),
});

If you must call directly from the browser (e.g. an internal admin tool, an authenticated app where the key is per-user and short-lived), set up a key-per-user system on your backend — never share one global key across users.

React Native

The SDK uses streaming bodies for SSE. Older RN versions ship a fetch polyfill that does not stream — response.body may be null. Verify on your target before relying on stream: true. Non-streaming calls work everywhere.

Polyfill notes

If your runtime is missing fetch, you can inject one:

import { Bastion } from "@qubittron/bastion-sdk";
import { fetch } from "undici";

const client = new Bastion({
  apiKey: process.env.BASTION_API_KEY,
  fetch: fetch as typeof globalThis.fetch,
});

See Custom fetch for more uses of this hook.

On this page