Skip to content

From zero to your module in your sandbox ​

From an empty folder to your module rendering inside Building OS, bound to a sandbox that is yours alone. About 30 minutes the first time. The steps are in order because each one needs the one before it, and each links to the page that covers it in depth.

Before you start ​

You need four things. If any is missing, write to developers@tango.vision before going further.

  • A developer account in the developers realm. Registration is invite-only; we create the account and you set your own password. You use it to mint API keys in the portal. See Developer account.
  • A registry token for npm.k8s.tangovision.dev. The SDK lives on a private registry, and the token is personal to you. Never commit it.
  • A sandbox sign-in, issued after your sandbox exists (step 3). Invited developers get a username and a temporary password, separate from the developer account. Tango Vision employees sign in with their company account instead, once they are in the /sandbox-developers group and an operator has linked their sandbox login; they get no password. See Sign in.
  • Node 24 and pnpm 10.18.2, exactly. Chrome or Firefox for the last step: Safari cannot load a module from your machine.

One-time setup ​

bash
# ~/.npmrc — the token comes from your registration email
@tv:registry=https://npm.k8s.tangovision.dev/
//npm.k8s.tangovision.dev/:_authToken=${TV_NPM_TOKEN}
bash
export TV_NPM_TOKEN=...              # keep it in your shell profile or a secret manager
npm view @tv/extension-sdk version   # expect 1.15.0 or later

Use @tv/extension-sdk 1.15.0. 1.14.1 is the oldest version whose scaffold builds: 1.14.0 scaffolds a project that does not build. 1.14.1 and earlier also name the federation container tv-module-<slug> instead of the camelCase slug the platform expects; if you scaffolded with one of them, set name in vite.config.ts to the camelCase slug (hello for hello, workOrders for work-orders). Details, including the Docker build-secret pattern: Getting started.

1. Scaffold the module ​

bash
npx @tv/extension-sdk init-module hello --category=operations --external
cd tv-module-hello
pnpm install

--external generates CI that runs on GitHub-hosted runners with your own TV_NPM_TOKEN secret. Without it, the generated workflow calls actions in a private Tango Vision repository and fails outside our organisation.

You get module-manifest.json (the contract: id, permissions, events, where the UI mounts), src/Shell.tsx (the component the shell renders), vite.config.ts (the federation remote exposing ./Shell) and a README naming the ports and URLs below.

bash
pnpm dev          # standalone preview with a mock platform context, http://localhost:5001
pnpm test
npx @tv/extension-sdk validate module-manifest.json
npx @tv/extension-sdk check-exposes module-manifest.json --config=./vite.config.ts

Edit Shell.tsx and the locales/ until the standalone preview shows what you want. Inside a real shell only certain SDK calls work: useSelectedBuildingId() from @tv/extension-sdk/react for the selected building, and getAccessToken() or authorizationHeader() from @tv/extension-sdk/context to authenticate your API calls, once per request. Never read localStorage['access_token']. React context does not cross the federation boundary, so usePlatformContext() throws inside the shell.

In depth: Your first module and Calling the API from a module.

2. Get a sandbox API key ​

Open the developer portal, sign in with your developer account and mint a key. It is shown once. Keys carry the sandbox scope by default, expire after 90 days and can be revoked in the same place.

bash
export TV_API_TOKEN=tvk_...      # the portal key; or: npx @tv/extension-sdk login

On Windows, use the key. login in SDK 1.15.0 and earlier cannot save the session there: it is larger than Windows Credential Manager holds. If you need login, set TV_SDK_TOKEN_STORE=file first, and keep it set for later commands.

In depth: Get a sandbox API key, API keys and CLI sign-in.

3. Create your sandbox ​

bash
npx @tv/extension-sdk sandbox create \
  --name=hello-dev --type=office --storeys=2 --area-sqm=5000

Provisioning takes one to a few minutes; the command polls until the sandbox is ready. You get:

FieldValue
idsbx_… — your sandbox id, which step 5 takes
apiUrlhttps://<slug>.sandbox.k8s.tangovision.dev
consoleUrlThe same host: the sandbox shell
organizationIdYour sandbox's tenant id, unique to this sandbox
apiTokenAdministrator of this sandbox only

Inside: a seeded office building with storeys, spaces and equipment, its own PostgreSQL database and its own tv-api. It expires after 14 days by default (extend up to twice, 7 days each). The data is synthetic, and nothing you store survives expiry. Types: office, mall, university. Up to 3 sandboxes at a time.

Now send us the slug and organizationId

We register the sandbox host for sign-in and create (or, for employees, link) your sandbox shell login with tenant_id set to your organizationId. Until we do, the shell cannot sign you in. Every time you recreate a sandbox, this step repeats.

In depth: On-demand sandboxes, including what is isolated and what is shared, the quotas, and the API behind this command.

4. Serve the remote from your machine ​

A federation remote exists only in a build; pnpm dev emits no remoteEntry.js. Two terminals:

bash
pnpm dev:remote     # vite build --watch
pnpm serve:remote   # vite preview --port 6001 --strictPort, CORS on

Your remote is at http://localhost:6001/assets/remoteEntry.js. The port is fixed on purpose: if 6001 is busy the command fails instead of silently moving, which would leave the registry pointing at nothing.

In depth: Serve your remote from your machine.

5. Install the module into your sandbox ​

bash
npx @tv/extension-sdk sandbox connect sbx_...    # prints export TV_API_URL=... TV_API_TOKEN=...
npx @tv/extension-sdk sandbox install module-manifest.json \
  --sandbox=sbx_... \
  --remote-entry=http://localhost:6001/assets/remoteEntry.js

sbx_… is your sandbox id from step 3, not the slug in its URL. Lost it? npx @tv/extension-sdk sandbox list shows the id of every sandbox you own; the portal lists sandboxes by name only.

This registers the manifest in your sandbox's registry with the localhost remote entry, and licenses the module on the seeded building. It prints the URL to open. Re-run it whenever you change the manifest; code changes need no re-install, because the shell loads your remote fresh.

In depth: Install the module into your sandbox.

6. Open it in the sandbox shell ​

https://<slug>.sandbox.k8s.tangovision.dev/sandbox/hello
  1. Use Chrome or Firefox. Invited developers sign in with the sandbox login we sent; the first sign-in asks you to set a new password. Employees click Tango Vision staff and use their company account.
  2. You land in Building OS as Administrator with your seeded building selected.
  3. Open /sandbox/hello. Chrome may ask once for permission to reach your local network; allow it. Your Shell.tsx renders inside the shell.

Edit, save, reload. dev:remote rebuilds, and the shell picks up the new bundle on the next load.

In depth: Open it and Sign in.

Rules that explain the blank pages ​

  1. Only a sandbox shell loads a module from localhost. The production shell never will; your module reaches customers only after we review the source and build it in our CI.
  2. Loopback only: localhost or 127.0.0.1. A LAN address or a tunnel is refused.
  3. The module renders in your browser only. A colleague opening the URL gets a load error, because localhost is their machine.
  4. Your backend runs on your machine and the browser can call it, but the platform cannot: the sandbox has no outbound internet, so Copilot cannot reach an MCP endpoint on localhost, and the sandbox Copilot cannot call an external LLM.
  5. Share i18next and react-i18next in vite.config.ts both or neither, and keep the use-sync-external-store/shim alias the scaffold wrote. Breaking either fails only inside a shell, where your CI cannot see it.

The full checklist is The rules for loading a module from localhost.

When something is wrong ​

You seeCauseFix
"useQuery" is not exported on buildSDK 1.14.0 scaffoldUse 1.15.0, or pnpm add -w @tanstack/react-query@^5
Missing required argument: <id>, or 404 from sandbox connectNo sandbox id, or the wrong onenpx @tv/extension-sdk sandbox list shows your ids (sbx_…)
sandbox list or create fails after sandbox connectThe TV_API_URL / TV_API_TOKEN it printed now point the CLI at your sandbox, not at the service that manages sandboxesUse a new terminal, or add --api=https://sandbox-api.k8s.tangovision.dev
login fails: longer than the platform limit of 2560 charsWindows Credential Manager cannot hold the session (SDK 1.15.0 and earlier)Use the portal key in TV_API_TOKEN, or set TV_SDK_TOKEN_STORE=file and log in again
401 from sandbox installWrong tokenUse the sandbox's apiToken (from sandbox connect), not the portal key
Sign-in says invalid redirectHost not registered yetSend us the slug (step 3) and wait for confirmation
Employee sign-in refused right after the staff buttonNot in /sandbox-developers, or the login is not linked yetAsk to be added to the group, and ask us to link your sandbox login
Signed in, shell says no tenanttenant_id not set on your loginTell us; we set it to your organizationId
Module not found, or 404Not installed, or slug mismatchRe-run sandbox install; the URL slug is the part of the manifest id after module-
Blank module, console shows CORS or 404Remote not servingBoth terminals running? curl http://localhost:6001/assets/remoteEntry.js
Blocked script in SafariHTTPS page loading HTTP loopbackUse Chrome or Firefox
Every API call 401 inside the moduleReading localStorage['access_token']Call getAccessToken() from @tv/extension-sdk/context per request; the sandbox shell does not write that key
Sandbox goneExpired (14 days by default)Create again, and send us the new slug

More: When the page is blank.

What you can and cannot do ​

  • You can create, reset, extend and delete your own sandboxes and install modules into them. Your sandbox token administers that sandbox only.
  • You cannot reach any other sandbox or any production tenant. NetworkPolicies stand between the sandbox namespace and production, and your sandbox's database role cannot open another sandbox's database or the server's postgres database. See What is isolated, and what is shared.
  • You cannot publish to the platform registry or to customers. That path does not exist yet; promotion is a manual review by Tango Vision.
  • Sandboxes are shared infrastructure with a fixed quota. Treat them as disposable, and use them for development only.

The developer platform is in preview, and sandboxes come without uptime guarantees. Questions: developers@tango.vision.

Built on the Tango Vision platform. Questions? developers@tango.vision