npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hypersoniclabs/helix-cli

v0.1.27-helix3.676

Published

helix — the HELIX creator CLI: scaffold, validate, and publish Instant Worlds

Readme

@hypersoniclabs/helix-cli

helix — the HELIX creator CLI: scaffold, validate, and publish Instant Worlds to the HELIX backend. It shares bundle validation with the server via @hypersoniclabs/helix-manifest, so "validate passed but publish failed" can't happen for contract reasons.

Naming

This is HELIX Creator Tools (the CLI plus the MCP), for use by your own agent (Claude Code, Codex, Cursor, ...). Dreamer is HELIX's built-in cloud agent on the website; it is a separate product. The generation commands (helix assets ...) call the Asset Generation API, whose route paths still start with /api/v1/dreamer/ for historical reasons. Dreamer is one client of Asset Generation and the publish APIs; Creator Tools are another. Glossary: helix-backend-api docs/architecture/glossary.md.

Architecture boundary

All creator feature and operational logic, validation, file mutation, network behavior, and tests live here. MCP packages may expose transport schemas, resources, instructions, and thin delegation to these commands, but must not reimplement this logic.

Install

npm install -g @hypersoniclabs/helix-cli

Authentication

helix login opens your browser to the website's /cli-login page; you sign in there and the CLI captures a client token via a one-shot loopback redirect. For CI/headless, pass a token directly: helix login --token <token> (mint one in the Creator Hub → Settings → Client Tokens).

The token is stored in ~/.helix/credentials.json (mode 600). It's sent as Authorization: Bearer <token> on publish calls. helix login validates it against GET /api/v1/users/me before saving and reads the token's exp so the CLI knows when it lapses.

The backend & website default to production: the published @hypersoniclabs/* CLI (built from helix3, see docs/npm-publishing.md) targets https://api.helixgame.com and https://helixgame.com. The former helix3 hosts (helix-backend-api-helix3.up.railway.app, new.helixgame.com) are the same environment, so a login saved under them keeps working without logging in again. The retired @helix3/* CLI resolves to production too; @helixdev/* targets staging. Override per run with --api-url/HELIX_API_URL and --web-url/HELIX_WEB_URL (e.g. a local/dev backend).

Commands

| Command | What it does | |---|---| | helix bridge detect\|inspect\|plan\|import\|validate\|doctor | Universal plugin-driven intake for assets, characters, animations, scenes, and engine projects. Produces deterministic plans, HELIX-native outputs, and checksummed provenance. See docs/bridge.md. | | helix bridge plugins create\|test\|list\|inspect\|install\|remove | Build and manage local/community Bridge Plugins using the versioned capability and permission contract. | | helix login [--api-url <url>] [--web-url <url>] [--token <t> [--refresh-token <r>]] [--no-browser] | Sign in via the browser (/cli-login), or --token for CI/headless. Stores the client token. --refresh-token (or HELIX_REFRESH_TOKEN) pairs a short-lived access token with its refresh token so a long command (assets resume) renews it when it expires. | | helix logout | Remove the stored token. | | helix whoami | Show current login status. | | helix init <dir> [--kind character\|multiplayer\|scene] | Scaffold a complete, build-ready Vite + TypeScript world. --kind scene emits a multiplayer world whose ENVIRONMENT is a sealed HELIX Scene v2 document — the starter document is compiled offline into public/scene/mall/, pinned in public/helix.json's scene field, kept out of the bundle by public/.helixignore, and lit by public/helix.visuals.json through the scene + visual system pins. | | helix install [--update] [--asset-pack <slug@range>] | Resolve a world's systems/abilities pins (a v0.2 or v0.3 manifest) → materialize the modules, the three import map, and helix.runtime.ts. Repeat --asset-pack to constrain specific system assets for this resolution. | | helix validate [dir] | Validate a bundle locally — the exact rules the server enforces. | | helix publish [dir] [--thumbnail <file>] [--upload-source] [--source-dir <path>] | Validate → resolve/create the world → upload files → finalize → set the cover image → print the play URL. --upload-source additionally sends the project's source through a separate private channel so the world can be edited on the website later — off by default. | | helix list | List your worlds. | | helix vehicle simulate <dir\|vehicle-package.json> [--item <id>] [--spec <file>] [--offline] [--json] | Drive the package offline with the platform's own engine instrument (the runtime ^0.3 serves today, cached by sha; offline = last cached, labelled) and judge 0-100 km/h, 0-60 mph, quarter mile, top speed, 100-0 km/h / 60-0 mph braking, skidpad g, turbo lag and the rest pose against the package targets and --spec. Exit 0 pass, 1 a figure out of tolerance, 2 could not simulate. See docs/vehicle-simulate.md. | | helix vehicle validate <vehicle-package.json> [--glb <model.glb>] [--no-simulate] [--json] | One-second local gate for a hand-authored Universal Vehicle, run before Package publication. Checks the package against the engine's own validator (vendored, pinned to an engine commit), the engine publish gate, the backend pin requirements, groundToBodyOrigin vs wheelRadius + staticRideHeight, and the componentBuild digest; with --glb also resolves the four wheel nodes, checks each wheel node's origin is at its hub (WHEEL_PIVOT_NOT_AT_HUB), the +X-left / +Z-forward frame, the vehicle publish budgets the articulation nodes and the add-on host manifest (asset.extras.HELIX_vehicle); drives packages that declare targets (helix vehicle simulate). Exit 0 no errors, 1 errors, 2 unreadable input. See Vehicle validate. | | helix vehicle publish <dir> --content-rating <r> [--item <id> --package-version <v> --reason <t>] [--json] | Package-first vehicle publication: validate, upload the model and samples, seal a public Vehicle Package (root pins the model; publish.json hostGrantExtensions sealed beside it), make the Item from that exact Version, read back pin, identity, audio and the host board (sockets, tunable paths). Resumable; --item stages a new Version and moves the car. Exit 0/1/2. See docs/vehicle-publish.md. | | helix vehicle pull <item-id> <dir> [--package-version <v>] [--content-rating <r>] [--json] | Rebuild a publishable COPY of a live car from its sealed Package Version (public Continuum routes): vehicle-package.json, every audio object, one model per served rendition, publish.json (legacy rating general -> everyone) and pull.json. No ledger, so vehicle publish <dir> mints a new Item. See docs/vehicle-publish.md. | | helix vehicle addon publish <dir> --content-rating <r> [--json] | Package-first vehicle add-on publication: the full vehicle addon check, seal a public addon Package (a tune-only part seals no model), make the Item, read back pin, backend fit and each host's add-on rail. Exit 0/1/2. | | helix vehicle preview [<dir-or-package.json>] [--glb <model.glb>] [--addon <dir>]… [--item <id>] [--tolerance-cm <cm>] [--out <dir>] [--proxy-item <id>] [--json] | Render a vehicle that is not published yet headless in the real runtime (hosted test grounds, Try Now through a published proxy vehicle whose car is swapped for your package + GLB, served from disk; nothing uploaded or recorded) and measure it: body-origin height against the declared groundToBodyOrigin (the number that locks at first mint), wheels on their hubs, arch clearance, rolling radius vs wheelRadius, steering, doors, seats. Prints a numeric summary with PASS/FAIL per check and writes frames, a contact sheet, bundle.json and preview.json. Boards with the candidate's own seat ids. With --addon <dir> (repeatable) it previews unpublished vehicle add-ons instead: fits each to the candidate car or a published car and measures the drawn geometry in world space against its anchor (wheels within 1 cm of each hub, socket/host-framed parts at their mount, not at the car's origin; tune-only ops read back from the live tuning). Exit 0 pass, 1 fail, 2 could not measure. See docs/vehicle-preview.md. | | helix continuum publish-glb <mesh.glb> --title <t> [--package-type asset\|character] [--offer marketplace\|world --price-lix <n> --content-rating <rating>] | Seal a Package first; optional offer makes it ownable. See Package-first GLB publication. | | helix continuum make-item --package-id <id> --package-version <v> --manifest-cid <cid> --content-rating <rating> [--offer marketplace\|world --price-lix <n>] | Project one exact sealed Package Version into an Item, with an optional first offer. | | helix continuum publish-owned-item <item-id> --package-id <id> --package-version <v> --manifest-cid <cid> --content-rating <rating> --accept-rating-attestation | Publish an existing personal Item from its unchanged public sealed Package pin. Offers are separate. | | helix vehicle audio-import <mod dir\|mod .zip\|AC car dir\|.bank> --out <dir> [--sim beamng\|ac\|auto] [--vehicle <id>] [--config <file.pc>] [--map <slot-map.json>] [--beamng-root <dir>] [--cylinders N] [--base-package <vehicle-package.json>] [--json] | Takes a car's sounds from the sim mod that carries them, never synthesised: a BeamNG mod's own ladders and every sound its parts name (stock FMOD events resolved in the install's banks with --beamng-root, which also supplies what the game itself plays for unnamed slots), or an Assetto Corsa car's FMOD bank. Writes audio/*.wav (16-bit PCM, malformed headers repaired) and audio.json (the package audio block, all 26 live slots (the retired tyre/brake squeal slots are never emitted), provenance on every slot, bank sample and blend). --map merges slots from several sources. The block always carries the runtime's mix fields (gains, load mix, muffling): from --base-package to keep a car's current mix, else the mod's soundConfig, else the engine's defaults. Exit 1 when a slot stays empty. See docs/vehicle-audio-import.md. | | helix vehicle carbon-weave <in.glb> <out.glb> [--tile-cm <cm>] [--roughness <r>] [--normal-scale <s>] [--carbon-materials <regex>\|none] [--ktx2] | Give a vehicle GLB's carbon-fibre a tiling 2x2 twill detail normal + roughness map at a real ~1.2 cm tile (UV scale per material from 3D/UV area), clearcoat roughness, non-metal. Tags source names that are exposed carbon first (default EXT_Carbon*, EXT_Plastic_shinny, EXT_painted_metal; an unmapped EXT_painted_metal rips as silver sides). Exit 1 when nothing was woven. See docs/vehicle-carbon-weave.md. | | helix vehicle audio-check <dir\|package.json\|audio.json\|item-id> [--reference <clip>]... [--json] | Proves every sound the runtime will select resolves, is PCM WAV and is audible (pre-publish on local files, or read-back on a published item), and that no two roles share one recording (shift up = shift down, an exhaust rung = an engine rung) and the rev limiter is a short crack the runtime can retrigger. Prints each slot's provenance and FAILS synthesised or generated audio (AUDIO_SYNTHESISED), a car without a recorded engine ladder (AUDIO_ENGINE_UNSOURCED), a missing or empty slot (AUDIO_SLOT_UNSOURCED, except absent: "not-fitted" / "combined-with-engine") among the 26 live slots (the retired tyre squeal, scrub, wheelspin, skid and brake squeal slots only warn: AUDIO_SLOT_RETIRED), recordings without provenance (AUDIO_PROVENANCE_MISSING) and a not-fitted claim the physics contradicts (AUDIO_SLOT_FITMENT_MISMATCH) and an audio block without finite mix fields (AUDIO_MIX_FIELDS_MISSING: a NaN gain freezes the car in Try Now); vehicle publish refuses the same. --reference <clip> (repeatable; slot=clip for a slot) compares the idle voice's centroid and firing roughness with a real-car recording. | | helix vehicle addon check <dir> [--json] | Add-on fit gate for local source and host compatibility, including the web customizer's meshless guard (ADDON_CUSTOMIZER_REFUSES: a part with no model.glb must be a verified tune-only/sound-only definition). | | helix vehicle addon check --item <addOnId>... / --host <vehicleId>... [--json] | Published mode (public reads, no sign-in): reads each add-on Item's exact Package pin and runs exactly what the web customizer runs; --host checks every add-on on the car's rail. Exit 1 = the customizer would refuse a part, with its message. | | helix item list-slots [--json] | Print the Character-Creator vocabulary a wearable declares: the cosmetic slot tags, and the genders. |

| helix item distribution create\|list\|update\|disable … | Manage Marketplace/World Claim or Buy routes without republishing the item or paying another publish fee. | | helix item visual-qa <item-id> [--submit] [--local-critic] [--storage-state <file>] [--host <id…>] | Render the item's current package version headless in the real runtime (test-grounds world for vehicles and whole avatars, the customiser for add-ons and wearables on every declared host/base, the item viewer for props), measure it, and with --submit record the backend's verdict (numeric asserts + vision critic) against that exact version. The visual check is advisory: it records notes with fix hints and never gates listing, distributions or version moves. Only a technical failure (an unusable file, with its plain reason) or content safety blocks. See docs/visual-qa.md. | | helix item visual-qa <item-id> --package-version <v> [--vehicle-package <file>] | Render a version the item is not pinned to yet and record advisory notes for it before (or after) the pointer moves; the move itself never waits on it. Vehicles and avatars go through Try Now (vehicles also need the move's vehicle package), add-ons and wearables through the customiser; the bundle names the candidate, and the vehicle checksum is the one the server minted for that trial. Never falls back to the current version. See docs/visual-qa-candidate-versions.md. With --submit, a PASS prints the next command (helix item set-version …) for a version not moved yet; a 403 for a non-creator says so on the first line; --status also lists the item's pending (sealed, not current) versions. | | helix item set-version <item-id> --package-version <v> --reason <text> [--expected-current-version-id <uuid>] [--as-admin] [--json] | Move an item to a sealed Package Version of its own package (pin resolved from the public Package API; POST /api/v1/universal-items/<id>/versions). The flow for a listed item: continuum publish-package seals vN → item set-version; the visual check is advisory and never gates the move. A refusal (a technical failure or content safety) prints the server's plain reason first, then the remedy. An unknown version lists the sealed ones. A vehicle moves its car with the pin: the version's sealed root (role vehicle-definition) is verified against the manifest and sent as vehiclePackage, the same rider vehicle publish --item sends; a version without one is refused with a pointer to that resume. A version the item already had is a rollback: PUT /api/v1/universal-items/<id>/versions/current { revision, reason } (the server restores that revision's retained vehiclePackage; a pin-only revision has none and is refused). --as-admin (platform admins, for an item another account created) posts the same body to POST /api/v1/admin/universal-items/<id>/versions: same gates (visual QA included, no override), --reason recorded with you as the actor. | | helix item thumbnail refresh <item-id> [--force] [--dry-run] [--json] | Re-render a published item's canonical thumbnail from its active Package Version with the standard server renderer (POST /api/v1/universal-items/<id>/thumbnail/regenerate, owner or admin). Prints the status (generated / current / dry-run), old -> new thumbnail URL, renderer version and the source lane + Package Version. --dry-run writes nothing; --force re-renders even when the stored thumbnail is current. Moving an item to a new Package Version now refreshes the thumbnail automatically unless you uploaded one. An older backend answers 404 and the CLI says it does not support thumbnail refresh yet. Not the world helix thumbnail set <slug> command. | | helix item thumbnail set <item-id> <image> [--json] | Upload your own thumbnail (png/jpg/webp, up to 10 MiB, checked locally) for a published item (PUT /api/v1/universal-items/<id>/thumbnail, moderated). A creator upload stays in place across version moves until you run thumbnail refresh --force. 403 = you are not the item's owner. | | helix item lods <packageId>[@version] \| --item <id> \| --glb <file> [--out <dir>] [--variant <id>] [--profile prop\|vehicle] [--api-url <url>] [--json] | Compile the helix.item-lod/1 Runtime Artifact for a published prop, Device or vehicle Package Version: up to two levels, LOD1 (props at most 25% of the triangles, vehicles max(5000, 10%), textures at most 512 px) and LOD2 (at most 300 triangles, textures at most 256 px), each its own GLB drawn in one primitive per render state (usually 1 or 2, via a baked material atlas) with its own textures (never larger than what they replace) and a measured maxErrorM, plus an 8-view 512 px octahedral impostor atlas. A level that does not cut half the triangles, or cannot be made strictly smaller in bytes (a vehicle LOD2 also: one over 25% of the extent from the source), is omitted. Content-addressed and deterministic; skinned input (avatars, add-ons) is refused. Local preview; the backend worker is the catalogue's source of truth. See docs/item-lod.md. | | helix device create <dir> --id <slug> --title <t> --model <mesh.glb> --template standing-desk\|clock\|television | Scaffold a GLB-backed helix.device/1 definition, including the interactive TV/media-player template. There is intentionally no website Device editor. | | helix device validate [dir] [--json] [--room] | Validate the definition, GLB bytes and named-node bindings, typed state/controls, capabilities, the runtime 512-step graph budget, deadline triggers, fill action args, and Home+World compatibility, including declarative character hand reach. --room also refuses state writes the room cannot run: any write reachable from an interact trigger through emitSignal handlers, delay or branch (the rule the backend applies when sealing a new publish). | | helix device test [dir] [--mode single\|multiplayer\|both] [--json] | Exercise every interaction, persistence remount, and deterministic one-/two-client authority convergence. | | helix world scene-elements compile <input> --output <scene> | Compile typed Scene v2 authoring, including exact Continuum Device pins, instances, and locally validated typed links. See examples/scene-elements-devices.json. | | helix preview-video <slug> <youtube-url> | Set or clear a published world's YouTube preview. | | helix thumbnail set <slug> <file> | Upload a thumbnail or preview image through the CLI-owned media path. | | helix assets search\|get\|versions\|track\|install\|update | Search the typed Vault with explainable ranking; inspect/manage annotations and immutable versions; install by durable ID with verified SHA-256 receipts. Material installs default to lean runtime KTX2 maps; use --material-renditions source for source PNGs or all for both families. | | helix assets start\|status\|generate\|resume | Start or poll generation without duplicates. Props/characters use the Asset Generation job lane; standalone assets use the shared broker and report the auto-published vaultAssetId. generate and resume block waiting for the job (see --timeout/--poll-interval below). | | helix assets voices [--search <text>] [--category premade\|professional] [--page-size <n>] [--page-token <token>] [--json] | Discover safe text-to-speech voices through the authenticated HELIX catalog, including language, labels, preview, default/recommended selection, and pagination. | | helix assets generate-image\|generate-material\|generate-audio\|generate-environment | Run the supported standalone generation adapters. Material is a validated PBR bundle (1K default, explicit 2K); splats declare object/environment scope. Animation remains an explicit unavailable boundary. | | helix assets start character "<prompt>" [--title t] [--visibility v] [--idempotency-key k] [--max-lix n] | Starts a character on the server-side Asset Generation job driver (POST /api/v1/asset-generation/jobs, mode: deterministic) and returns at once; stdout is the driver job JSON (id). --visibility is sent only when you pass it; the server owns the default and every other policy (reference review, redraws, retries, visual QA wait, WARN/BLOCK, refunds). The create is retry-safe: the CLI picks one idempotency key (a UUID, or your --idempotency-key, 8-96 chars), sends the same key on every try (3 tries on a timeout, dropped connection or 5xx), and the backend returns the existing job for the same owner and key, so a timeout after the server created the job never duplicates or double charges. The key is printed on stderr and in the stdout JSON as idempotencyKey; if a run's answer was lost, re-run the same command with --idempotency-key <key>. | | helix assets resume <id> [--timeout <ms>] [--poll-interval <ms>] (driver job) | Waits for succeeded, held, failed or handoff (default 80 min, poll 3 s), one progress line per change on stderr (stage, reference gate, scheduled retry, policy events, cost). Final stdout is the driver job JSON plus top-level publishedItemId (= result.itemId). Exit 0 for succeeded, held (the item exists but is not released; result.qa says why; helix assets refund <id> is offered) and handoff; exit 1 for failed (JSON on stdout, message with escalate/retriable on stderr) and for a client-side timeout (nothing on stdout). --visibility must match the job's (it is fixed at start) or the command exits 1. An access token that expires during the wait is renewed (see Login below: helix login --token <access> --refresh-token <refresh> or HELIX_REFRESH_TOKEN; otherwise another process's fresh helix login is picked up); a login that cannot be renewed ends the wait with helix login + helix assets resume <id>, and the job keeps running. An id the driver does not know (props, older characters) resumes through the previous loop below. helix assets generate character is start + resume. | | helix assets act <id> approve\|revise ["guidance"], helix assets refund <id>, helix assets status <id> --pipeline driver | Decide at the open reference gate, get the LIX back for a held avatar, or read the raw driver job. | | helix assets start-reference\|generate-reference | Start or fully drive a character job from a local four-view PNG/JPEG/WebP. The backend validates and reuses the exact sheet; it skips image generation while preserving mesh, texture, rig, LOD, thumbnail, billing, and Vault publication. generate-reference blocks waiting for the job (see --timeout/--poll-interval below). | | helix assets generate\|generate-reference\|resume [--timeout <ms>] [--poll-interval <ms>] | These three drive an Asset Generation prop/character job to completion (concept → mesh → texture → rig → publish) and block until it finishes. --timeout bounds how long the CLI process waits before giving up (default 1200000ms / 20min, unchanged if omitted); --poll-interval sets how often it checks job status while waiting (default 2000ms). A timeout is a CLIENT-SIDE giveup, not a job failure — the job keeps running server-side. The error prints the exact helix assets resume <job-id> command to check on or continue it; on a genuinely slow stage (e.g. mesh generation), rerun with a larger --timeout, or just poll separately with helix assets status <job-id> --pipeline dreamer. | | helix assets materials [--resolution <res>] [--no-resolve] [--human] | Discover the small built-in material palette. --resolution keeps only materials carrying that texture resolution. Each entry also carries the Scene v2 vault pin and the continuum package ref; --no-resolve skips those lookups. | | helix assets material <id> [--resolution <res>] [--no-resolve] [--human] | Resolve one material to its immutable map URLs at the resolution you pick (default: the pack's own default), plus its vault pin and continuum ref. | | helix assets credits\|check-loaders | Render asset credits from provenance and reject models requiring unsupported loaders. | | helix character import <body.glb> [--map <json>] [--fit source\|canonical] [--provenance <json>] | Convert one self-contained third-party humanoid through the public helix-humanoid@1 pipeline. The command rejects external/remote resources and resource bombs before parsing, generates bounded LODs, and emits a source/license/transformation/checksum receipt beside the output. --fit canonical aligns the rest skeleton/proportions; it does not prove that Base Male-targeted garments fit a different body surface. Hands fused to the thighs (an A-pose mesher merging touching surfaces) are cut free and re-weighted per side, gated on a walk-stride stretch measure; --limb-bridges report only logs what it would change, off skips it. | | helix character check <avatar.glb> [--out <dir>] [--json] [--no-inspection] | Judge an avatar GLB before it is published: the production Marketplace viewer plays the platform's walk, the engine's run and idle on it (nothing uploaded), the hosted gate's own avatar@3 math plus offline stage-1 and run/idle asserts judge it, and a fixed inspection set (walk at the back-swings from front, 3/4, true sides and back; run; idle turnaround; head/hands/feet) is written beside Base Female. Exit 0 pass (a floor, not a quality verdict), 1 a blocking assert failed (with measured value and next step), 2 could not measure. See docs/character-check.md. | | helix character fit <addon.glb> --body <base.glb> -o <out.glb> (--slot-type <character.x@1>\|--hidden-regions <globs\|none>) [--clearance-mm 3] | Pose-aware poke-through fit for a packaged character add-on (every LOD): across the canonical pose set, pushes garment geometry out along the body-facing normal to a minimum clearance, smoothed so hems and silhouettes keep their shape. Gated inside the stage by the poke-through measurement: the output is the fitted mesh only when it is strictly better (exit 0), otherwise a byte-identical copy of the input (exit 3). Bridge add-on imports run the same stage automatically; use the fitted output in a new sealed Package Version. | | helix character ear-clearance <addon.glb> --body <base.glb> (-o <out.glb>\|--check) [--target-mm 4] [--trigger-mm 1.5] [--max-push-mm 18] [--json] | Head-zone check and fix for headwear / mask / hair / eyewear: finds the base body's ears, measures the clearance to the OUTERMOST garment layer around each (horizontal ray from the head axis, per LOD; only samples the garment wraps on all four sides count, so a mask that stops in front of the ear or a cap above it is not a defect) and, when a wrapped ear has < 1.5 mm, pushes the garment smoothly outward (Gaussian field, sigma 22 mm, horizontal only, <= 18 mm total, every LOD). Kept only when no ear pokes afterwards. --check writes nothing and exits 3 when an ear pokes through. helix character fit runs it automatically for head-zone slot types (--no-ear-clearance skips it; --ear-clearance forces it); seal the corrected mesh in a new Package Version. The pose-aware fit cannot do this job on a thin head shell. | | helix character derive-hide <addon.glb> --body <base.glb> --slot-type <character.x@1> [--threshold 98] [--ops-out ops.json] [--json] | Measures which of the slot's granted body regions the garment fully occludes (bind pose; a body vertex counts when garment is in front of it along its outward normal within 60 mm; the least-covered LOD decides) and prints the hide ops that follow, with the measured share of every region left visible. A region is hidden only at >= 98 % AND when no triangle the hide would collapse (engine rule: >= 2 corners hidden) reaches a body vertex the garment does not cover (the sawtooth under a hem or collar); partly covered regions stay visible and the pose-aware fit keeps their skin from poking through. Bridge add-on conform does this automatically when the profile names no hideRegions; include the resulting operations in the new sealed Package Version before moving the Item. | | helix character retarget-animation <clip.fbx\|clip.glb> | Retarget a raw Mixamo/Meshy clip directly to helix-humanoid@1. FBX conversion is deterministic and runs inside the CLI; no hidden Blender/DCC pre-export is required. | | helix character export-vrm <avatar.glb> [-o out.vrm] [--lod n] [--rest-pose auto\|always\|off] [--name ..] [--author ..] [--json] | Export a published HELIX avatar as a VRM 1.0 file: humanoid map, expressions and visemes from helixFace, spring bones and colliders from helixDynamics, lookAt, firstPerson, meta and MToon materials. A-pose arms are re-posed to the T-pose VRM requires and the mesh re-skinned. See docs/vrm-export.md. | | helix character import-vrma <clip.vrma> [-o clip.glb] [--clip-name n] [--loop] | Import a VRM Animation (.vrma) as a helix-humanoid@1 clip GLB through the same retarget as Mixamo/Meshy clips; usable as an emote. See docs/vrm-export.md. | | helix pack scaffold\|validate <dir> | Scaffold and validate locomotion clip sources offline. Seal an add-on Package before any Item or offer is created. | | helix world audit\|source-audit\|perf-gate\|prove-live | Run the blocking world QA, performance, and deployed-build identity gates. | | helix world performance-report [bundle] [--scene <scene-v2.json>] [--package <package-version.json>] [--snapshot <inspect.json>] [--perf <receipt.json>] | Analyze exact declared scene resources and existing reference-profile budgets, while keeping authored geometry/material/delivery estimates separate from supplied runtime claims. Prints actionable instancing, Palette, lighting, transfer, and visual-QA guidance; it never predicts FPS from static counts. | | helix world scene-source inventory\|compile <helsinki\|bistro> <archive> | Verify and safely inventory a known CC BY source archive, then compile deterministic local proxy/detail GLBs and an existing-schema Scene v2 package. | | helix world scene-source publish <world-id> <package-dir> [--publish] | Validate the Scene v2 package, refuse it if it breaks the open-world performance rules (docs/scene-perf.md; no override), and print its multipart build receipt. Execution remains dry-run-only until backend private-pin entitlement lands; --publish currently fails closed, and access control never changes CC BY rights. | | helix scene validate <dir\|file> [--perf] [--tier desktop\|mobile\|desktop,mobile] [--out <report.json>] | Check a Scene v2 package against the shared validator and, with --perf, the 14 open-world performance rules (textures, geometry, cells, draws, budget, first-playable bytes). Exit 0 pass, 1 refused, 2 cannot be judged. See docs/scene-perf.md. | | helix scene split <input> -o <dir> [--cell-size m] [--core-radius m] [--spawn x,y,z] [--textures ktx2\|keep] | Split a monolithic environment (one big shell GLB, or a Scene v2 document with an eager shell) into a small eager spawn core plus distance-streamed cells on a fixed, content-addressed grid. Writes a validated Scene v2 package with per-cell bounds, priority and an honest memory budget, plus a report of eager bytes before and after. Deterministic and byte-identical on a rerun; never publishes. See docs/scene-split.md. | | helix scene retexture <split-dir> -o <dir> [--merge-psnr dB] [--uastc-rdo 2,1\|off] [--dry-run] | Shrink the textures of a split Scene bundle without moving a cell: merge textures that decode to the same picture, re-encode UASTC data and normal maps with RDO above a PSNR floor, rewrite the image uris in the GLBs that named them, reseal and validate. Nodes, stream groups, bounds, spawn and budgets are untouched. Never publishes. See docs/scene-split.md. | | helix continuum publish-source-map\|publish-scratch\|publish-package\|publish-glb\|make-item\|publish-owned-item | Resume exact-CID object adoption/ingestion, Package lease/seal, optional explicitly authorized Listing, and WorldRelease publication through one creator-owned pipeline. | | helix continuum receipt <job-id> | Read the sanitized durable receipt for a completed Continuum publication. | | helix unreal init\|validate\|doctor\|package [project] | Create and gate a shared-runtime or native-game publish manifest, check optional editor MCP, and assemble a local upload-shaped package. | | helix unreal publish <candidate> --world <slug> | Upload a cooked Unreal world to HELIX: verify the candidate, presign, upload every artifact, finalize, and promote it as the world's active build. | | helix doctor [--project <dir>] | Print the environment + whether the @helix toolchain (CLI/MCP/SDK/manifest) is current. |

Constraining system assets during installation

To update system code while deliberately selecting a compatible asset version:

# helix.json keeps the range, e.g. humanoid-character: "^0.3"; only the asset pack is constrained
helix install --update --asset-pack [email protected]

--asset-pack <slug@range> is repeatable for different asset slugs. Supported ranges are exact versions, ^/~ ranges (partial versions are padded as for system pins), latest, and *. Resolution selects the highest active version satisfying both the explicit constraint and the system envelope's asset requirement. Complete exact pins use the authoritative public version-detail endpoint, including active staged versions. Incorrect kinds/identities, unused slugs, duplicates, invalid ranges, missing versions, and incompatible constraints fail before installer writes.

These constraints apply to this invocation. An existing lock requires --update; a fresh install can use the option without a lock. The installer resolves from the catalog and writes the exact asset URL/version and unchanged envelope range into the lock, with invocation intent recorded in top-level assetPackConstraints. It does not reuse unverified asset metadata from an old lock. Normal helix install then replays that generated lock as usual.

A subsequent helix install --update without the option refreshes all dependencies from their ordinary requirements, including previously constrained assets. Repeat the option in your update command to retain the constraint. It is not a world manifest field or a permanent asset freeze, and cannot be combined with local first-party installation. Generated locks, import maps and runtime modules remain installer-owned.

Unreal runtime projects

helix unreal init <project> --mode shared-runtime|native-game creates helix.unreal.json. Shared-runtime projects ship cooked content/Blueprints against a pinned HELIX runtime; native-game projects ship separate signed client/server builds and may use custom C++ and plugins. Both require Unreal 5.8, the HelixSDK plugin, and HelixPlatform identity/session integration.

Networking is explicit: unreal-native, helixnet, custom, or none. Only helixnet requires contract v21; blank Unreal projects using arbitrary GameMode/Pawn classes and native networking do not need Lyra Experiences, Colyseus, or the HelixNet contract.

helix unreal validate is the publish gate and does not require MCP. helix unreal doctor adds the loopback/editor-only Unreal MCP authoring checks. helix unreal package copies validated cooked or native artifacts into a local checksummed staging directory — it is a local assembly step, not a publish.

Publishing a cooked world

helix unreal publish <candidate> --world <slug-or-id> takes the directory the Unreal HelixWorldPublish commandlet writes — world-build-manifest.json, its .sha256 receipt, cook-plan.json, and artifacts/<role>/… — and gets it live in one command:

helix unreal publish Saved/HelixPublish --world my-world --max-players 32 --visibility unlisted

It verifies the candidate against the bytes on disk (every digest recomputed — a manifest is a claim, not evidence), registers the Build and presigns each artifact, streams the uploads, finalizes so the server re-verifies every byte by size and SHA-256 and countersigns the manifest, then promotes the Build as the world's active release. --no-promote stops after verification; --verify-delivery additionally checks what a client would actually download.

The idempotency key defaults to the cooked manifest's own digest, so re-running after a dropped connection resumes the same Build rather than minting a second one.

Only shared-runtime worlds publish this way. A World Build may not contain creator executables, so native-game builds are a separate distribution path.

Working in a git worktree? Run this first

npm run check:dev-links

package.json links its sibling HELIX packages by relative path (file:../helix-manifest, file:../helix-web-sdk). That is right in the canonical layout, where the siblings sit beside this clone — but in a git worktree ../ is the worktree parent, so the links resolve to whatever happens to be there: a checkout parked on an older branch, or nothing at all.

The symptom is not "broken dev link". It is a branch that looks broken: tsc reporting has no exported member … in files your change never touched, and whole suites failing at import (could not find the dev shell in @hypersoniclabs/helix-sdk). People have concluded a PR was broken on the strength of exactly this.

check:dev-links names which link is wrong, which imported symbols the linked sibling does not export, and how to fix it without repointing a shared directory other worktrees resolve through.

Publish flow

Platform system updates

helix install records platform-owned systems in public/helix.runtime.json. Compatible ranges such as ^0.2 are externalized and resolved to an active, immutable catalog module when a world launches, so a compatible platform fix can reach an already-published world without creator edits or republishing. A new runtime version reaches no world until the platform deliberately publishes it to the catalog and it goes active there — building or merging one changes nothing on its own, and there is no rolling "latest".

Engine systems are ranges. Declare "systems": { "humanoid-character": "^0.3", "engine-core": "^0.1", "scene": "^0.3", "visual": "^0.1" } so a World loads the latest promoted engine at launch. An exact version such as 0.3.178 maps to the immutable catalog URL and freezes the World on that build forever, so helix validate and helix publish refuse it. If an engine fix is staged but not yet promoted, promote it (POST /api/v1/instant-packages/<slug>/versions/<version>/rollout, {"rolloutPercent":100,"reason":"…"}); do not pin Worlds to it. The one escape hatch is deliberate and audited:

helix publish dist --allow-exact-engine-pin "<why this World must stay frozen>"

The reason is required (an empty one is refused), a loud warning is printed, and the override (reason, slug, pinned versions) is appended to ~/.helix/engine-pin-overrides.jsonl (HELIX_ENGINE_PIN_OVERRIDE_LOG relocates it). helix validate takes the same flag. A custom Vite config is never rewritten: install reports legacy-bundled until the creator explicitly externalizes @helix/*. helix validate, helix publish, and the publish API refuse that legacy mode too. latest and * are not accepted publish ranges because they can cross a compatibility boundary.

The platform runtime is on three 0.185.1. The version is a property of the pinned system — humanoid-character@^0.3 resolves to an envelope declaring it — and helix install bakes it into the import map immutably, so an already-published world keeps running whatever it baked. A world behind that line still publishes: helix validate and helix publish only warn, naming the upgrade path (re-pin ^0.3, helix install --update, rebuild, republish), and helix doctor reports it (and a local devDep three that has drifted off the platform runtime) before you build.

That update contract is deliberately narrower than "the SDK updates itself":

| World dependency | What the built bytes contain | What can advance without republishing | | --- | --- | --- | | Platform systems (@helix/humanoid-character, @helix/engine-core and declared subpaths) with a compatible range | Bare imports plus a generated import-map entry | The compatible catalog target on the next fresh launch | | Platform systems with an exact version (refused by validate/publish without --allow-exact-engine-pin "<reason>") | Bare imports plus the exact immutable catalog URL | Nothing; the pin stays byte-identical | | Creator SDK (@hypersoniclabs/helix-sdk) | The SDK module graph bundled into the world's own JavaScript | Nothing; SDK changes require rebuilding and republishing the world | | Installed abilities (public/helix_modules/*) | Verified ability code/assets copied into the world build and indexed by installed.json | Nothing; update/install and republish are explicit creator actions | | Creator game code and assets | The world's own immutable build files | Nothing; the platform never rewrites a published creator bundle |

The generated import map, helix.runtime.json, and helix_modules/installed.json are the authoritative way to inspect which side of this boundary a particular build uses. Browser or proxy caching may serve a previous compatible resolver response during a transient outage; the descriptor's lastKnownGood field is provenance metadata, not an executable in-world fallback. Without a cached resolver response, a cold resolver failure fails closed.

Native features remain individually overridable without pinning the whole character runtime. The character system's native avatar features — carried emotes and the radial wheel (including its Customize door), x-ray, pointing, sitting, the avatar camera, the two camera legs, and rendering each player's universal avatar — are constructed by the character chassis itself, so every character World has them whether it was built on Character or on CharacterMultiplayer, and a World published today receives ones added later on its next fresh launch.

A World refuses what it does not want by NAME, in the character config it passes at create time — not in helix.json, and nothing here is a publish-time concern:

config: { character: { native: { disabled: ['xray', 'pointing'] } } }

Ids: emotes, emote-wheel, xray, pointing, sitting, avatar-camera, camera-first-person, camera-third-person, universal-avatar. It is a blocklist, never an allow-list, for the same reason this whole delivery contract exists: an allow-list would freeze every already-published World out of every feature added afterwards. A refusal also removes the feature's key, controller button, touch pad and prompt; universal-avatar is the top switch and takes emotes, the wheel, x-ray, pointing, sitting and the avatar camera with it (the camera legs survive — a World still needs a camera); refusing BOTH camera legs throws at create; and an unrecognised id is kept with a warning rather than rejected, because it may belong to a newer runtime. The older switches still work and route into the same resolver: CharacterMultiplayer.create's emotes: false, emoteWheel: false and universalAvatars: false, and the config keys character.emote.enabled, character.emote.wheel, character.xray.enabled, character.universalAvatar.enabled. (localControl: false is not one of these — it declares a spectator body the World drives its own camera and input for.) Every other compatible native fix continues to update normally.

This surface needs @helix/humanoid-character 0.2.54 or newer; an older runtime ignores character.native.disabled silently, so confirm against the resolved system's config schema rather than assuming. The full creator reference is the character world recipe in the HELIX MCP docs (read_doc({ name: "character-world" }), section 8g); the authoritative id list and defaults are that config schema, readable from the installed system manifest.

The runtime descriptor is mandatory for bundles that declare systems. It makes the delivery boundary inspectable by the CLI, MCP, backend, and fleet census; it does not execute code or silently rewrite an old minified bundle.

helix publish maps directly onto the backend's Instant World API:

  1. checkBundle validates helix.json + files via @hypersoniclabs/helix-manifest.
  2. GET /api/v1/instant-worlds/me → reuse the world with the manifest's slug, else POST /api/v1/instant-worlds.
  3. POST /api/v1/instant-worlds/:id/builds → presigned upload tickets.
  4. Files PUT directly to storage (6 concurrent), content-type/length bound to each ticket.
  5. POST /api/v1/instant-worlds/:id/builds/:buildId/finalize → byte-exact verify, activate, publish.
  6. GET /api/v1/instant-worlds/:slug → the play URL.

helix.json's fields are the shared manifest's, not the CLI's — including the optional v0.3 scene pin ({kind, id, revision, integrity:{sha256}}, or a continuum-scene package triple) naming the sealed Scene document a world streams as its environment; integrity.sha256 is the authority and id/revision are display text. Reference: docs/helix-scene-v2.md in @hypersoniclabs/helix-manifest.

.helixignore

An optional gitignore-style exclude list read at the bundle root. Matching paths are left out of the upload and out of the 50 MB / 200-file budgets, and the file itself is never uploaded. helix validate and helix publish print excluded N path(s) via .helixignore when it drops anything.

  • Blank lines and # comments are ignored.
  • scene/ — a trailing slash matches a directory and everything under it.
  • scene/pin.json — a plain path, relative to the bundle root, forward slashes.
  • *.map and **/notes.txt — * stays inside one path segment, **/ crosses segments.
  • !keep.json re-includes: rules apply in order and the last match wins. As in git, nothing under an excluded directory can be re-included.

It must land at the bundle root, so a Vite world keeps it at public/.helixignore and the build copies it into dist/. node_modules, .git, .vscode, .idea and screenshots are dropped regardless and need no rule.

The case it exists for: a @helix/scene world keeps its dev-only Scene document and source GLBs under public/scene/, which Vite copies verbatim. One scene/ line keeps megabytes of authoring bytes out of every player's download.

Opt-in source upload

A published world ships only its built bundle, so there is nothing on the server to edit later. --upload-source adds a second, separate upload that fixes that:

  1. POST /api/v1/instant-worlds/:id/source → a presigned PUT into a private bucket (this call also adopts the world into a project, which is the key its future workspace hangs from).
  2. helix-source.tar.gz PUT with cache-control: no-store.
  3. POST /api/v1/instant-worlds/:id/source/finalize → the server verifies the stored byte count equals the declared one, then records the version.
  • Off by default. Source is never sent unless you pass the flag. Not a prompt — an interactive confirm would break CI and agent-driven publishes.
  • What goes in: the project directory, honouring .gitignore, always excluding node_modules, .git, dist and .vite — the same exclude set the website's own workspace checkpoints use.
  • Which directory: [dir] is the BUILT bundle, so the source defaults to its parent. Override with --source-dir <path>.
  • Never part of the build. The bundle's content-type allowlist rejects archives, raw .ts is excluded from a bundle by design, the bundle size budget is the playable budget, and every build file lands on a public CDN path with a year-long immutable cache. Source uses its own private channel for exactly that reason.
  • Limit: 128 MiB compressed, checked locally before anything is uploaded.

Before upload, multiplayer worlds that install modules are checked for server realizability. A module with a bank reference must name a bank that is published with the declared checksum (bank modules belong to the default multiplayer block; the manifest validator refuses one inside a room type). A module without bank is inline: its rules are in the manifest, so there is nothing to resolve. The modules of every program (the default block and each multiplayer.roomTypes entry) must then fit the 100,000 tick-node ceiling at the number of players that program's room holds: 24 for a standard default block, a room type's own maxPlayers up to 24, and the declared maxPlayers up to 64 with capacityClass: "large", never fewer than 2. Fix the named module or bank when this gate refuses a publish. For exceptional recovery only, --skip-realizability-check bypasses this check; worlds without modules are unaffected.

The programmatic surface (publishWorld, checkBundle, whoAmI, …) is exported for other CLI modules. Agent integrations should instruct or delegate to the helix commands above rather than reimplementing operational behavior in a transport package.

Vault lifecycle

Use the durable Vault UUID as the handle. Search results include independent lexical, hard-filter, measured-performance, reuse, and availability explanations; they do not collapse those signals into a made-up score. helix assets install resolves an immutable version, follows the /download indirection, verifies its server checksum and byte count, installs related material maps, and writes public/helix.assets.json. Material installs keep the descriptor and default to the runtime KTX2 map family only; choose source PNGs with --material-renditions source, or both families with --material-renditions all. Reinstalling with a different selection removes the stale opposite family. Non-material assets keep their existing related-artifact behavior. For a reproducible agent build, pass --asset-version, --checksum-sha256, and --size-bytes together; a partial pin or any metadata/download mismatch fails before a provenance receipt is written.

Generation is source-agnostic at the backend:

  • prop and character start through the Asset Generation job route (/api/v1/dreamer/*), which seals a Package before any optional Item offer.
  • image, material, audio, and gaussian_splat start through the shared asset route. Gaussian splats must declare object or environment scope.
  • animation fails locally with CAPABILITY_UNAVAILABLE; it never starts a chargeable job.

Run helix assets status <job-id> --pipeline dreamer|asset to poll an existing job. A successful default-on generation reports vaultAssetId and vaultAutoPublish: "published"; an explicit creator opt-out reports "disabled".

For .helix-scene.json, the open Vault catalog is not the scene-local budget. Reuse a small material palette and instance repeated assets. The canonical ceilings are:

| Profile | Unique materials | Draw calls | Decoded texture memory | Triangles | Particles | | --- | ---: | ---: | ---: | ---: | ---: | | mobile | 48 | 300 | 256 MiB | 750,000 | 5,000 | | desktop | 128 | 1,200 | 1 GiB | 4,000,000 | 50,000 | | cinematic | 256 | 4,000 | 4 GiB | 15,000,000 | 250,000 |

These are publish-contract ceilings, not frame-rate promises; world runtime QA still applies.

The lifecycle acceptance harness intentionally has no CLI-local demo. Point it at a built world in helix-web-demo-worlds that consumes the published SDK and humanoid runtime:

npm run qa:vault-lifecycle -- \
  --world-template /path/to/helix-web-demo-worlds/worlds/sdk-showcase

Publishing is an explicit release-only action and additionally requires a new approved slug:

npm run qa:vault-lifecycle -- \
  --world-template /path/to/helix-web-demo-worlds/worlds/sdk-showcase \
  --publish --world-slug ch1146-vault-lifecycle-qa-<unique-suffix>

The publish receipt requires one usage increment, one world association, and an idempotent second publish with no additional increment.

Audio generation

All three audio modes use the same authenticated Asset Generation route and automatically publish the completed MP3 to Vault. The command waits up to 30 minutes so a ten-minute music request has enough time to render, ingest, and publish. It prints the durable vaultAssetId, measured Spark charge, provider cost, and server-stamped billingEvidence. It does not download another local copy unless --output is supplied.

# Sound effect
helix assets generate-audio "heavy metal door slamming shut" \
  --mode sound_effect --duration-seconds 3 --prompt-influence 0.4 --output door.mp3

# Music
helix assets generate-audio "hopeful orchestral exploration theme" \
  --mode music --duration-seconds 180 --force-instrumental

# Text to speech
helix assets voices --search narrator --page-size 10
helix assets generate-audio --mode text_to_speech \
  --text "Welcome to HELIX." --voice-id <id-from-catalog> \
  --language-code en --stability 0.5 --speaker-boost

Use the opaque --page-token printed by a page to continue. Voice cloning and voice administration are deliberately not exposed.

Use a deterministic four-view sheet when you already have approved FRONT, BACK, LEFT, and RIGHT character art and do not want Asset Generation to generate another image:

helix assets generate-reference ./character-four-views.png \
  "compact friendly service robot, game-ready humanoid proportions" \
  --title "Service Robot" \
  --target-polycount 12000

The reference endpoint is character-only. The file is image-guarded, normalized, checksum-bound to the job, semantically validated, and mirrored before approval. Retries reuse those same bytes and never invoke or charge the image provider.

Material texture resolution

A platform material can ship more than one texture resolution. --resolution picks which one you get; without it you get the pack's declared default, byte-for-byte what every earlier CLI returned.

helix assets material brick-block                      # the pack's default resolution
helix assets material brick-block --resolution 2k      # the 2K variant's map URLs
helix assets material brick-block --resolution 2048    # same thing — numeric spelling
helix assets materials --resolution 2k --human         # only materials that carry 2K

Accepted values are the keys the catalog itself declares (1k, 2k, …), case-insensitively, plus the numeric aliases 1024/2048, which map onto the variant authored at that pixel edge.

Both commands print JSON by default (agents and the MCP delegation parse it); --human prints a readable summary instead. Either way the output states which resolution was resolved and which ones the material offers, under resolution:

"resolution": {
  "requested": "2k", "resolved": "2k", "pixels": 2048, "default": "1k",
  "available": [{ "key": "1k", "pixels": 1024 }, { "key": "2k", "pixels": 2048 }],
  "applicable": true
}

Three rules make this safe to rely on:

  • No silent fallback, ever. Asking for a resolution the material does not carry is an error that names the ones it does. You never receive a different resolution than the one you asked for.
  • Old packs keep working. A catalog with no resolutions block (schemaVersion: 1) is treated as having exactly one resolution, keyed default, derived from its existing maps. Such a pack never claims to be "1k" — it does not say how big its textures are, so neither do we.
  • Procedural materials are not an error. glass and procedural_water carry no texture maps; a resolution request against them is ignored and the output says so in resolution.note.

helix assets install has no resolution flag: Vault related-artifact roles are semantic (source.albedo, runtime.ktx2.normal, sky.backdrop), never resolution-tagged, so a filter there would filter nothing.

Pinning a platform material — the Vault pin and the Continuum ref

A catalog id such as white-marble is a pack-local name. No scene format accepts it, so both material commands also return the two identities an author actually writes down:

"vault": { "assetId": "6d7ac215-…", "revision": 2 },
"continuum": {
  "packageId": "3970d36f-9374-56f9-9be8-576bfa70fa8d",
  "listingId": "33a5a6bc-…",
  "version": "1.0.1",
  "manifestCid": "cid:sha256:b7a3206f…"
}

vault is the Scene v2 identity. A materials[] entry must resolve to a material resource whose source is an immutable Vault pin (helix-manifest/src/scene-v2.ts:1627-1628), so the pin is written on the resource and the material points at it:

"resources": [{
  "id": "res-white-marble",
  "kind": "material",
  "source": { "kind": "vault", "assetId": "6d7ac215-…", "revision": 2 },
  "integrity": { "sha256": "…" },
  "mimeType": "application/vnd.helix.material+json"
}],
"materials": [{
  "id": "mat-white-marble",
  "resource": "res-white-marble",
  "defaults": { "tint": "#ffffff", "repeatPerMeter": 1, "roughnessScale": 1, "metalnessScale": 1 }
}]

continuum is the semantic-scene identity. A sceneDocument.materialBindings[].binding (helix-manifest/src/continuum/material.ts:111-136) references a Package Version and never owns maps:

{
  "id": "white-marble",
  "binding": {
    "schema": "helix.continuum.material-binding/1",
    "material": { "packageId": "3970d36f-…", "version": "1.0.1", "manifestCid": "cid:sha256:…" },
    "slot": "white-marble",
    "tint": [1, 1, 1, 1],
    "opacity": 1,
    "roughnessScale": 1, "metallicScale": 1, "normalScale": 1, "emissiveScale": 1,
    "uv": { "set": 0, "offset": [0, 0], "scale": [1, 1], "rotationDeg": 0, "tiling": [1, 1] }
  }
}

The same { packageId, version, manifestCid } triple is what a scratch descriptor's source.materials[].sourcePackage and every entry of compilation.dependencyClosures[] require.

How each field is resolved, and what a null means:

| Field | Route | Login | | --- | --- | --- | | vault | GET /api/v1/vault/assets?q=platform-material:<id>&kind=material&match=narrow | not needed — the route is public and the CLI sends no token | | continuum.listingId / version / manifestCid | GET /api/v1/vault/catalog?packageType=material&q=<id, hyphens as spaces> | not needed — public |

  • continuum.packageId is a pure function of the catalog id — no lookup, always present, stable forever: deterministicContinuumUuid("helix.material.catalog/1\0" + id). The catalog page is filtered on that derived id, never on a title, so a same-named Listing cannot become a wrong pin.
  • vault: null means the platform tag matched zero or several assets — exactly one match is the only pinnable state (the same rule the Home scene loader applies). Several also sets vaultWarning: "ambiguous: N matches".
  • version: null means no sealed public Package Version for that id on this lane.
  • A failed lookup never masquerades as an absence: it sets vaultWarning / continuumWarning to lookup failed: … and leaves the rest of the catalog intact.
  • --no-resolve makes no request at all. Entries then carry only the derived continuum.packageId and no vault key — absent, not null, so "not looked up" cannot be read as "not there".
  • Lookups run at most 8 in flight and are cached for the life of the process.

Vehicle validate

helix vehicle validate <vehicle-package.json> [--glb <model.glb>] [--no-simulate] [--offline] [--json] needs no login. This local gate checks the vehicle Package and optional GLB before any publication. When the package declares targets it also drives the car with the platform's engine instrument (helix vehicle simulate; the runtime is fetched once and cached, --offline uses the cache, --no-simulate skips the drive) and reports every figure outside its tolerance as SIMULATED_PERFORMANCE_OUT_OF_TOLERANCE.

Every finding prints as CODE path — message plus a fix: line; --json returns { ok, errors, warnings, issues: [{ code, level, path, message, fix }], engineCommit }.

| Layer | Codes | |---|---| | Engine validator (all its errors and warnings) | ENGINE_VALIDATOR, ENGINE_VALIDATOR_CRASH | | Publish gate + backend pin | PACKAGE_NOT_OBJECT, PACKAGE_VERSION_UNSUPPORTED, SCHEMA_VERSION_MISMATCH, REQUIRED_FIELD_MISSING, REQUIRED_BLOCK_MISSING, MESH_CLASS_ID_MISSING, MESH_PARTS_MISSING, WHEEL_VISUALS_MISSING, WHEEL_VISUALS_INVALID, NOT_DISTRIBUTABLE, MESH_SOURCE_FIXTURE, PUBLISH_GATE | | Cross-field | GROUND_TO_BODY_ORIGIN_MISMATCH (> 5 mm; fix the ride height, the field is locked after first publish), COMPONENT_BUILD_DIGEST_MISMATCH (prints the computed digest), COMPONENT_ACTIONS_MISMATCH (prints the compiled protocol), COMPONENT_BUILD_INVALID, PANEL_SIGNAL_NO_ACTION (warn), SEAT_SOCKET_UNKNOWN (warn) | | Drive (package targets) | SIMULATED_PERFORMANCE_OUT_OF_TOLERANCE (figure, simulated, target, error, tolerance, engine version), SIMULATED_REST_POSE_INVALID, SIMULATION_UNAVAILABLE (warn: no runtime reachable or cached) | | --glb host manifest | HOST_MANIFEST_MISSING, HOST_MANIFEST_MISPLACED (found in scene/document/node extras; the backend reads only asset.extras.HELIX_vehicle), HOST_MANIFEST_EMPTY, HOST_MANIFEST_DIVERGENT, HOST_MANIFEST_SOCKET_INVALID, HOST_MANIFEST_SOCKET_NAME_INVALID, HOST_MANIFEST_SOCKET_NODE_MISSING. helix continuum publish-package refuses a vehicle Package descriptor whose model GLB has any of these errors before sealing. See docs/vehicle-simulate.md | | --glb host-frame bind contract | HOST_BIND_ROOT_MISSING (no vehicle_root node), HOST_BIND_MOUNT_NODE_MISSING (an offered boot/bonnet socket with no node to mount on), HOST_BIND_ROOT_NOT_OWNER (the lid node is outside vehicle_root): a host that offers a lid socket must carry the nodes a host-frame add-on binds to, or the runtime throws while composing the car and it is never presented. vehicle addon check --host, vehicle addon check <dir> and vehicle preview --addon apply the same rule to the add-ons' own bind { socket, root }; vehicle preview refuses before launching a browser and fails at once on a [helix-vehicle] composition failed console line or a page error | | --glb | WHEEL_NODES_MISSING, WHEEL_PIVOT_NOT_AT_HUB (origin > 2 cm from the centre of its geometry — the wheel add-on anchor lands there), WHEEL_ORIGINS_NOT_DISTINCT, WHEEL_FRAME_MIRRORED_X, WHEEL_FRAME_FRONT_NOT_PLUS_Z, WHEEL_NODE_EMPTY (warn), LENGTH_NOT_ALONG_Z (warn), PANEL_NODE_MISSING, PANEL_ROLE_MISSING_DOOR / PANEL_ROLE_NO_NODE_BONNET / PANEL_ROLE_NO_NODE_BOOT / PANEL_ROLE_UNDECLARED_BONNET / PANEL_ROLE_UNDECLARED_BOOT (warn: a four-wheeled car should articulate its doors, bonnet and boot / engine cover), the lamp-coverage gate on the lit geometry (LAMP_INDICATOR_REAR_MISSING / LAMP_INDICATOR_FRONT_MISSING / LAMP_INDICATOR_SIDE_UNLIT / LAMP_INDICATOR_NO_ROWS / LAMP_NODE_UNLIT / LAMP_REPEATER_UNLIT / LAMP_FUNCTION_NO_ROW errors, LAMP_ROW_NO_TARGET / LAMP_NO_LIGHTING_ROWS / LAMP_INDICATOR_REAR_OPTED_OUT warns; the documented opt-out for a car that really has no rear indicators is lights.noRearIndicators: "<why, with source>"), VEHICLE_BUDGET_TRIANGLES / _MATERIALS / _TEXTURE_EDGE / _DRAW_CALLS / _FILE_SIZE |

The model frame is glTF: +X is the vehicle's left, +Y up, +Z forward, metres. Wheel nodes are found by name with the engine's own resolver (wheel_lf / wheel_rf / wheel_lr / wheel_rr and the usual aliases); all four must resolve. The engine validator is vendored by scripts/vendor-vehicle-package-v2.mjs (see vendor/README.md).

Package-first GLB publication

helix continuum publish-glb <mesh.glb> --title <name> seals a Prop Package Version through POST /api/v1/publications/upload. --package-type character seals a Character Package. A Package-only publish creates no Item, Distribution or creator inventory copy. The receipt prints the exact { packageId, version, manifestCid } pin and a retryable intent UUID. Use --private for source resources that