@helixdev/sdk
v0.1.0
Published
HELIX Instant SDK — identity, and later multiplayer/voice/wallet, for worlds running on HELIX Instant
Readme
@helixdev/sdk
The HELIX Instant SDK. Worlds running on HELIX Instant use it for player identity today, and multiplayer / voice / wallet / inventory in upcoming versions. It is the only way a world talks to the platform — worlds never call platform APIs or internal services directly.
This document is the SDK contract. It is written to be sufficient for an AI agent to integrate a world without reading the SDK source.
Install
npm install @helixdev/sdkCore concepts
- Your world runs in a sandboxed iframe inside the HELIX shell (the portal play page, or the
helix devshell during local development). The SDK talks to the shell viapostMessage; the shell talks to the platform. - Identity is granted, not taken. Your world receives a short-lived, world-scoped session that only unlocks the permissions declared in your
helix.jsonmanifest. You never see the player's platform credentials. - Login UI belongs to the shell. Your world cannot render a login form — it requests login, and the shell overlays its own UI. This is deliberate (anti-phishing) and means you never handle passwords.
- Standalone mode. When the world is opened directly (e.g.
vite devwithout a shell),init()resolves withembedded: falseand all identity APIs returnnull/false. Your world should still run — treat identity as an enhancement.
Quick start
import { Helix } from '@helixdev/sdk';
const { embedded, user, world } = await Helix.init(); // call once, before anything else
if (user) {
greet(user.displayName ?? user.username);
}
// React to login/logout at any time (e.g. update the HUD):
Helix.auth.onAuthChanged((user) => updateHud(user));
// Prompt login at a moment that makes sense in your world:
saveButton.onclick = async () => {
try {
const user = await Helix.auth.requestLogin(); // shell overlay; no page reload
await saveProgress(user.id);
} catch {
// player dismissed the login — keep playing, nothing is lost
}
};API
Helix.init(): Promise<HelixInitResult>
Performs the shell handshake. Must be called once before any other API. Safe to call again (returns the same state). Resolves within ~3s even with no shell present.
type HelixInitResult = {
embedded: boolean; // false when running standalone (local dev)
world: { id: string; slug: string; title: string } | null;
user: HelixUser | null; // null when not logged in
};Helix.auth.getUser(): Promise<HelixUser | null>
The current player, or null when unauthenticated. Requires the auth.profile permission in your manifest.
type HelixUser = {
id: string; // stable player id (UUID) — use this as your save key
username: string; // unique handle
displayName: string | null;
};Helix.auth.isAuthenticated(): boolean
Synchronous check.
Helix.auth.requestLogin(): Promise<HelixUser>
Asks the shell to show its login overlay. Resolves with the user on success. Rejects when: the player dismisses the overlay ('dismissed'), no shell is present (standalone), or the request times out. The world keeps running throughout — there is no reload, and your state is preserved. If already logged in, resolves immediately.
Helix.auth.onAuthChanged(cb: (user: HelixUser | null) => void): () => void
Subscribes to login/logout. Fires with the user on login and null on logout. Returns an unsubscribe function.
Helix.getSessionToken(): string | null
The raw world-scoped session token (JWT, aud: helixb-world). Most worlds never need this; later SDK modules use it internally.
Manifest requirements
Your bundle root must contain a helix.json manifest (see @helixdev/manifest). To use the identity APIs, declare the permission:
{
"helixVersion": "0.1",
"title": "My World",
"slug": "my-world",
"entry": "index.html",
"permissions": ["auth.profile"]
}Calling an API whose permission is not declared returns an error — permissions are enforced server-side on the session token, not just in the SDK.
Coming in later versions
Helix.multiplayer (instances), Helix.voice, Helix.wallet, Helix.inventory, Helix.cloudSave, Helix.leaderboards. The shapes follow the same pattern: capability declared in the manifest → granted on the session → exposed as a namespace.
