{"service":"fullstacked-cloud-docs","version":"1.22.3","environment":"live","generatedAt":"2026-10-24T09:38:32.560Z","formats":{"html":"https://docs.fullstacked.cloud/","markdown":"https://docs.fullstacked.cloud/?format=md","json":"https://docs.fullstacked.cloud/?format=json"},"services":{"console":"https://console.fullstacked.cloud","auth":"https://auth.fullstacked.cloud","git":"https://git.fullstacked.cloud","tunnels":"https://tunnels.fullstacked.cloud","tunnelsHost":"tunnels.fullstacked.cloud","mcp":"https://mcp.fullstacked.cloud","install":"https://install.fullstacked.cloud","docs":"https://docs.fullstacked.cloud"},"runtime":{"version":"1.0.0-alpha.1844","guide":"https://docs.fullstacked.cloud/runtime?format=md"},"dataEngineLimits":{"postgres":{"image":"postgres:18.6-alpine","port":5432,"memoryMB":256,"cpus":2,"pids":256},"redis":{"image":"redis:8.10.2-alpine","port":6379,"memoryMB":256,"cpus":1,"pids":128},"mongodb":{"image":"mongo:8.3.11-noble","port":27017,"memoryMB":512,"cpus":2,"pids":512},"s3":{"image":"rustfs/rustfs:1.0.0","port":9000,"memoryMB":768,"cpus":2,"pids":1024}},"projectRuntimeEnv":[{"name":"TUNNELS_HOST","injectedBy":"GET /api/v1/projects/:id/env","description":"Tunnels hub host for tunnel.register from fullstacked/tunnel."},{"name":"AUTH_HOST","injectedBy":"GET /api/v1/projects/:id/env","description":"Bare host of the Authentication service."},{"name":"AUTH_URL","injectedBy":"GET /api/v1/projects/:id/env","description":"Base URL of the Authentication service (GET ${AUTH_URL}/api/v1/me)."},{"name":"DOCS_URL","injectedBy":"GET /api/v1/projects/:id/env","description":"Base URL of this documentation (?format=md | ?format=json)."},{"name":"AUTH_TOKEN","injectedBy":"Client GUI / action runner at launch (in memory, never stored)","description":"The signed-in user's fsc_* bearer token: Authorization: Bearer ${AUTH_TOKEN}."}],"topics":[{"slug":"runtime","title":"FullStacked Runtime","url":"https://docs.fullstacked.cloud/runtime","markdownUrl":"https://docs.fullstacked.cloud/runtime?format=md","jsonUrl":"https://docs.fullstacked.cloud/runtime?format=json","markdown":"# FullStacked Runtime\n\nFullStacked Cloud projects run on the FullStacked runtime (`fullstacked` npm package, version 1.0.0-alpha.1844 on this platform). A project is one bundled ES module: UI code (DOM, React) and \"backend\" code (Node built-ins, database drivers, `process.env`) run in the same JavaScript context, bridged to a native Go core. There is no separate server process: never start an HTTP server, call your own functions directly. This page lists what the runtime actually provides so code written for it works the first time.\n\n## Execution model\n\n| Fact | Consequence for your code |\n| :--- | :--- |\n| One entrypoint per project: the first of `index.tsx`, `index.ts`, `index.jsx`, `index.js` found at the project root | Import everything else from it. When both `index.tsx` and `index.ts` exist, `index.tsx` is the entrypoint. |\n| The entrypoint is always compiled with the classic JSX transform (`React.createElement`), whatever `tsconfig.json` says; other files follow `tsconfig.json` `jsx` (classic when unset) | Start every file that contains JSX with `import React from \"react\";`. Without it the app fails with `ReferenceError: React is not defined`. |\n| esbuild bundles the entrypoint (ES module, TypeScript and JSX built in) into `out/`, with a generated `out/index.html` that only loads the bundle's script and stylesheet: it has no root element | There is no HTML file to write. Create your root element in code (see below). CSS you import is bundled and linked. |\n| Only `.png`, `.jpg`, `.jpeg`, `.gif` and `.svg` imports have a loader (they resolve to file URLs) | Other asset types (`.webp`, fonts, media), including those referenced by CSS `url()`, fail the build with \"No loader is configured\". `out/` is deleted and rebuilt on every bundle: keep static files at the project root, where `fetch(\"/<file>\")` finds them. |\n| The bundle targets the browser platform | npm packages resolve to their `browser` builds (for example `ws` resolves to its browser stub, which throws: use the global `WebSocket`). |\n| UI and logic share one context | Put data access in plain `async` functions (for example `db.ts` or `actions/<name>.ts`) and call them from components. No REST layer, no `express`, no `http.createServer`. |\n| `process.env` comes from the runtime (Client GUI launch, `.env` + `-e` flags, or the action runner) | Read `process.env.*` anywhere. Never hardcode platform hosts. See [Injected process.env](https://docs.fullstacked.cloud/project-runtime). |\n| The filesystem is rooted at the project directory | `fs` paths starting with `/` resolve inside the project, relative paths against `process.cwd()` (which starts at `/`, the project root). A path that would leave the project resolves to the project root instead of failing: `fs.rm(\"..\")` deletes the whole project. |\n\n```tsx\n// index.tsx: the single entrypoint\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { App } from \"./app\";\n\nconst root = document.createElement(\"div\");\ndocument.body.append(root);\ncreateRoot(root).render(<App />);\n```\n\n### Running a project\n\n```bash\nnpx fullstacked              # bundle and run the project in the current directory (port 9000 by default)\nnpx fullstacked --port 3000  # another starting port (-p is --plugin, not the port)\nnpx fullstacked -b           # bundle only\nnpx fullstacked -f script.ts # bundle and run one script file, then exit\nnpx fullstacked -e AUTH_TOKEN=$AUTH_TOKEN -e KEY=value\n```\n\n`<project>/.env` is loaded first, then every `-e KEY=VALUE` (which wins); with `-f`, `.env` is read from the current directory. In the FullStacked Cloud Client GUI the environment is injected for you.\n\n## Module resolution\n\nThe bundler maps runtime modules before npm resolution: it strips a `node:` prefix and a `fullstacked/` prefix, then looks the name up in the runtime's module table. Consequences:\n\n- `node:fs`, `fs` and `fullstacked/fs` are the same module; `fullstacked/tunnel` and `tunnel` too.\n- npm packages whose name collides with a runtime module (`auth`, `bundle`, `config`, `fetch`, `git`, `packages`, `parentWindow`, `plugin`, `run`, `test`, `tunnel`, `websocket`, or a Node built-in listed on this page) cannot be imported: the runtime module is used instead.\n- A named import of something a runtime module does not export (`import { createServer } from \"http\"`, `import { exec } from \"child_process\"`, `import { copyFile } from \"fs\"`) fails the bundle with \"No matching export\".\n- `require()` of these modules works in CommonJS dependencies.\n\n## Global `fetch` and `WebSocket`\n\nAt startup the runtime replaces `globalThis.fetch` and `globalThis.WebSocket` with implementations executed by the Go core.\n\n| Call | Behavior |\n| :--- | :--- |\n| `fetch(\"https://...\")` / `fetch(\"http://...\")` | Sent by the Go core: no browser CORS, no preflight, no mixed-content blocking. Custom headers (including `Authorization`) and methods work. |\n| `fetch(\"http://<tunnel name>/...\")` | Routed through the registered tunnel (see [fullstacked/tunnel](#fullstackedtunnel)). |\n| `fetch(\"/path\")` or `fetch(\"fs://...\")` | In an app, served by the runtime's local file handler: the file at `out/<path>`, else `<project>/<path>`, else not found. It never reaches FullStacked Cloud (in `-f` scripts and actions it fails). |\n| `fetch(\"./path\")` | Throws (invalid URL). Use an absolute URL. |\n| `new WebSocket(\"wss://...\" \\| \"ws://...\", protocols?)` | Dialed by the Go core, tunnel names included. Standard browser signature only: no custom headers option. |\n\nPitfalls of the `fetch` implementation:\n\n- `Content-Type` is never inferred from the body: set it yourself (`\"Content-Type\": \"application/json\"` with `JSON.stringify(...)`). Avoid `FormData` bodies: no multipart boundary header is generated.\n- `response.clone()` and `response.formData()` return `null`. `json()`, `text()`, `arrayBuffer()`, `blob()`, `bytes()` and streaming `response.body` work.\n- A response header sent several times keeps only its last value (several `Set-Cookie` headers collapse to one).\n- `redirect: \"manual\"` or `\"error\"` disables following redirects; the default follows them. `signal` (AbortController) is honored.\n\n```ts\nconst res = await fetch(`${process.env.AUTH_URL}/api/v1/me`, {\n    headers: { Authorization: `Bearer ${process.env.AUTH_TOKEN}` },\n});\n```\n\n## Node built-in modules\n\n### Backed by the Go core\n\n| Module | What exists |\n| :--- | :--- |\n| `fs`, `fs/promises` | Sync, callback and promise forms of `readFile`, `writeFile`, `appendFile`, `readdir` (`withFileTypes`), `mkdir` (always recursive), `rm` / `unlink` (`rm` is recursive), `rename`, `stat`, `cp`; plus `existsSync`, `statSync`, `realpathSync`, `createReadStream`, `createWriteStream`, `constants`. Not available: `copyFile`, `access`, `lstat`, `chmod`, `chown`, `utimes`, `symlink`, `watch`. Copy a file or a directory with `cp` / `cpSync` (parent directories are created). |\n| `path` | `join`, `resolve`, `normalize`, `parse`, `extname`, `dirname`, `basename`, `isAbsolute`, `relative`, `sep`; `path.posix` and `path.win32` are the same object. No `format` or `delimiter`. |\n| `os` | `platform()`, `arch()`, `endianness()`, `release()`, `type()`, `hostname()`, `tmpdir()` (`/.tmp`, inside the project). Nothing else (no `cpus`, `homedir`, `networkInterfaces`, `EOL`). |\n| `net` | TCP client only: `Socket`, `connect`, `createConnection`, `isIP`, `isIPv4`, `isIPv6`. No `createServer`. Database drivers (`pg`, `ioredis`, `mongodb`) use it, including through tunnel names. |\n| `dns`, `dns/promises` | `lookup` (callback and promise); promises `resolve4`, `resolve6`, `resolveCname`, `resolveMx`, `resolveNs`, `resolveSrv`, `resolveTxt`. The generic `resolve()` is a stub: use the typed functions. |\n| `dgram` | `createSocket(\"udp4\" \\| \"udp6\")` and `Socket`. |\n| `crypto` | Node's API implemented by the Go core: `createHash`, `createHmac`, `hash`, `randomBytes`, `randomFill(Sync)`, `randomInt`, `randomUUID`, `getRandomValues`, `pbkdf2(Sync)`, `scrypt(Sync)`, `hkdf(Sync)`, `createCipheriv` / `createDecipheriv`, `createSign` / `createVerify`, `sign` / `verify`, `generateKeyPair(Sync)`, `generateKey(Sync)`, key objects, public/private encrypt and decrypt, Diffie-Hellman and ECDH, `timingSafeEqual`. `X509Certificate` and `Certificate` throw. Do not rely on `crypto.subtle` / `webcrypto`: the webview may not be a secure context. |\n| `zlib` | Deflate, inflate, gzip, gunzip, unzip, raw variants and Brotli: streams (`createGzip`, ...), callback and `*Sync` forms, `crc32`, `constants`. |\n| `worker_threads` | `new Worker(path)` (a module worker: point it at a separately bundled `.js` file, for example from `fullstacked/bundle` `bundleFile`) and `parentPort` inside the worker. `message` listeners receive a `MessageEvent`: read `event.data`. No `workerData`, `isMainThread` or `MessageChannel`. |\n| `process` (also the global), in apps | `env` (read from the runtime on each access: treat it as read-only, assignments are not kept), `argv`, `versions.fullstacked` (object with `major`, `minor`, `patch`, `build`, `branch`, `hash`), `cwd()`, `chdir()`, `nextTick`, `hrtime` (no `hrtime.bigint`), `exit()`, `stdout` / `stderr` (`write` goes to the console). `process.on(...)` and the other event methods are no-ops; `version` is `\"\"`; `browser` is `true`; `platform` / `arch` are not set (use `os`). In `-f` scripts and remote action runs the code runs under Node and `process` is Node's own. |\n\n`fs` details:\n\n- Errors carry no `code` (no `ENOENT`): the message reads like `error from bridge: [open /abs/path: no such file or directory]`. Test with `existsSync` instead of catching `ENOENT`.\n- `rm` and `unlink` of a missing path succeed silently.\n- The callback forms of `mkdir`, `rm`, `unlink`, `writeFile`, `appendFile`, `rename` and `cp` take no options argument: `fs.mkdir(p, { recursive: true }, cb)` never calls `cb`. Prefer `fs.promises` or the sync forms.\n\n### Pure JavaScript\n\n`buffer` (global `Buffer`), `events`, `stream` (`promises`, `web`, `consumers`), `util` (`util/types`), `url`, `querystring`, `string_decoder`, `timers` (`timers/promises`, global `setImmediate`), `assert`, `readline` (`readline/promises`), `perf_hooks`, `diagnostics_channel`, `constants`, `tty`, `console`.\n\n### Unavailable\n\n| Module | Behavior | Use instead |\n| :--- | :--- | :--- |\n| `http`, `https`, `http2` | Export only `Agent` (no `createServer`, `request`, `get`, `STATUS_CODES`): `http.createServer(...)` fails with `TypeError: ... is not a function`, and a named import of them fails the bundle | `fetch`; for UI to logic calls, import and call your functions. Libraries that need `http.request` or `http.Agent` do not work: use `fetch`-based clients. |\n| `tls` | `connect`, `createServer`, `createSecureContext` throw `Error(\"unavailable\")` | Keep `ssl: false` on drivers connected through a tunnel (the tunnel is already `wss://`). |\n| `child_process`, `async_hooks`, `module`, `vm`, `v8`, `sqlite` | Any property access on the default import throws the string `\"unavailable\"`; a named import fails the bundle | `fullstacked/git`, `fullstacked/packages`, `fullstacked/bundle`, `fullstacked/run`. |\n| Any other built-in (`cluster`, `inspector`, `repl`, ...) | Not provided | |\n\n## Runtime modules (`fullstacked/*`)\n\n### fullstacked/tunnel\n\n```ts\nimport tunnel from \"fullstacked/tunnel\";\n\ninterface Tunnel {\n    host: string;           // hub hostname only: no scheme, no port (use process.env.TUNNELS_HOST)\n    authorization?: string; // sent verbatim as the Authorization header of the WebSocket upgrade\n    port?: number;          // hub port, default 443 (80 when unsecure)\n    unsecure?: boolean;     // ws:// instead of wss://\n    path?: string;          // hub WebSocket path (FullStacked Cloud: leave unset)\n    name?: string;          // the name to register, default 8 random letters\n}\ntunnel.register(t: Tunnel): Promise<string>; // resolves with the tunnel name\n```\n\nThe returned name is a virtual host recognized by `net` (and every TCP driver), `fetch` and `WebSocket`; for `fullstacked/git`, pass it as the `tunnel` argument (`clone(url, dir, { tunnel })`, `pull(dir, tunnel)`, ...). The port you give a driver or put in a URL is ignored: the hub picks the target from the token.\n\nHow it works:\n\n- `register` stores the settings under the name and opens the tunnel's first WebSocket session to `wss://<host>:<port><path>` in the background. It never fails on bad credentials: authorization errors surface on the first connection.\n- Each registered tunnel keeps its own WebSocket sessions to the hub, authenticated once at their upgrade. Every connection to the name (socket, `fetch`, `WebSocket`) is a stream multiplexed on one of them: no handshake per connection. The runtime adds sessions under load (up to 8, 32 streams each) and closes idle ones.\n- Registering the same `name` again with identical settings reuses its sessions; different settings replace them (the old sessions close once their streams end). The registry is shared by every project context of the runtime. Register once and reuse the name.\n\nWith FullStacked Cloud, `authorization` is `` `${process.env.AUTH_TOKEN}:${process.env.<X>_TOKEN}` ``: see [Managed Data](https://docs.fullstacked.cloud/data) and [Edge & LAN Tunnels](https://docs.fullstacked.cloud/tunnels).\n\n### Other modules\n\n| Import | API |\n| :--- | :--- |\n| `fullstacked/auth` | `authenticate(url: string, opts?: { windowFeatures?: string }): Promise<any>`: opens `url` with `auth=1` added in the platform's auth surface and resolves with what the page posts back. Needs a window (apps only). See [Runtime Sign-In](https://docs.fullstacked.cloud/auth-byoa). |\n| `fullstacked/config` | `get(key)`, `set(key, value: string)`, `list()`, `delete(key)` (alias `del`): string key/value store of the runtime context, persisted in the project's `.git/config.json`. |\n| `fullstacked/git` | The directory comes first (`clone(url, dir, { tunnel?, proxy? })` excepted): `hasGit(dir)`, `init(dir, url)`, `head(dir)`, `status(dir)`, `add(dir, path)`, `log(dir)` (the last 10 commits), `commit(dir, message, { name, email })`, `pull(dir, tunnel?)`, `fetch(dir, tunnel?)`, `push(dir, tunnel?)`, `reset(dir, hard, ...files)`, `branch(dir)`, `tags(dir)`, `checkout(dir, ref, create?, tunnel?)`, `merge(dir, branch)`, `restore(dir, ...files)`, `setConfig(dir, key, value)`. Network calls return progress streams. Credentials come from a `git-auth` plugin. |\n| `fullstacked/packages` | `install(dir, saveDev: boolean, ...names)` (no names: the dependencies of `package.json`), `uninstall(dir, ...names)`, `audit(dir)`, `resolve(moduleName, startDir?)`, `addNodePath(path)`. Install and uninstall return a progress event emitter. |\n| `fullstacked/bundle` | `bundle(entryPoint?)` (a directory or file, default `.`), `bundleFile(entryPoint)`, `esbuildVersion()`. Resolve with `{ OutputFiles, Errors, Warnings }`. |\n| `fullstacked/run` | `run(directory \\| { directory?, env?, safe? }): Promise<number>`: creates a runtime context for the project in `directory` and resolves with its id. It does not open it: pass the id to `globalThis.fullstacked.open(id)` to display the project. |\n| `fullstacked/plugin` | `register(\"git-auth\", { callback: (host) => ({ username, password }) })`, `register(\"build\", { data: { filter, ext? }, callback })`, `register(\"deeplink\", { callback: (url) => void })`; resolves with `{ unregister() }`. `deeplink(url, ctx?)` triggers deeplink plugins. |\n| `fullstacked/parentWindow` | `disableAutoWindowSize()`: stop saving the window size when it is resized (the saved size is restored at startup, before your code runs). |\n| `fullstacked` | `execute(command, opts?)` runs a `fullstacked ...` CLI command line in-process; `validateCommand(command)` returns its tokens from `fullstacked` on, or `null`. |\n\n## Checklist for agents\n\n1. One entrypoint (`index.tsx` for apps with UI); create the root element in code.\n2. `import React from \"react\";` in every file that contains JSX, the entrypoint included.\n3. Call your own functions, never a local HTTP server; call FullStacked Cloud with absolute URLs built from `process.env`.\n4. Set `Content-Type` on every `fetch` with a body.\n5. Use `fs.promises` or the sync forms listed above; copy with `cp`, not `copyFile`; check paths with `existsSync`.\n6. Reach managed data and LAN services only through `fullstacked/tunnel` with `ssl: false` on the driver.\n7. No `child_process`, `http.request`, `tls`, or npm packages that depend on them.\n"}],"endpoints":[]}