---
title: "Component Type Definitions"
description: "Sources: `packages/ecs/src/index.ts:COMPONENT_TYPE_DEFINITIONS`, `packages/ecs/src/index.ts:ComponentSchema`, `server/src/modules/runtime/capabilities/packs.ts:runtimeCapabilityPacks`, `web_client/spa/src/views/build/component-palette/taxonomy.ts:PALETTE_TAXONOMY`."
engineVersion: v1.0.234
date: 2026-09-28
license: "(c) Gessa, proprietary. Cite with attribution to https://gessa.ai/docs/. Terms: https://gessa.ai/terms/."
canonical: https://gessa.ai/docs/spec/generated/component-types/
---
<!-- GENERATED FILE: do not edit by hand. -->
<!-- Regenerate with `npm run gen-docs`. -->

Sources: `packages/ecs/src/index.ts:COMPONENT_TYPE_DEFINITIONS`, `packages/ecs/src/index.ts:ComponentSchema`, `server/src/modules/runtime/capabilities/packs.ts:runtimeCapabilityPacks`, `web_client/spa/src/views/build/component-palette/taxonomy.ts:PALETTE_TAXONOMY`.

# Component Type Definitions

This is the canonical generated inventory for the v1 built-in ECS component set. Current engine capability sections may name these component types as shipped ECS components. Other component names must be explicitly marked roadmap-only, stripped-pattern history, or project-defined data.

Current built-in component count: 34.
Current canonical component field count: 613.
Canonical fields without an authored Description: 274.

Component catalog contract hash: `sha256:0af9015f319b9b377b819ed0edd0d597ae82bb45191f9694c7d372fd8673e68c`.
Component field contract version: `ecs.component-field-contract.v1`.
Component field contract hash: `sha256:356e4bf0cdad3dfd562a861224c5c4b676e89c1f32444edc46ad88fccbfb1423`.

All current built-in components are atomic engine components under ADR 0020. Gameplay patterns such as health, damage, spawn pools, collectors, and motion helpers are script patterns, not ECS atomics.

## Inventory

| Component | Category | Class | Palette visibility | Palette notes | Default key | Authored contract version | Packs | Runtime systems | Runtime consumer | Renderer consumer | Replication |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `AnimationStateComponent` | Rendering | atomic | default | Atomic renderer state. | `component.animation_state` | v1 | `rendering.v1` | None | projected | consumed | `public/low` |
| `AudioEmitterComponent` | Audio | atomic | default | Atomic audio primitive. | `component.audio_emitter` | v4 | `audio.v1` | None | consumed | not_applicable | `public/low` |
| `AvatarSlotComponent` | Gameplay | atomic | default | The character always keeps its own colliders, movement, and camera. The game resolves each player's avatar at spawn and records what it applied here, so an avatar never changes how a character moves or collides. | `component.avatar_slot` | v1 | None | None | projected | consumed | `public/low` |
| `CameraComponent` | Rendering | atomic | default | Atomic camera primitive. | `component.camera` | v3 | `camera.v1` | None | projected | consumed | `public/medium` |
| `CharacterMovementComponent` | Physics | atomic | default | Runtime movement behavior; structural hierarchy is projected from entity fields, not a component. | `component.character_movement` | v2 | `physics.v1` | `runtime.physicsSystem`, `runtime.collisionSystem` | consumed | not_applicable | `public/medium` |
| `ColliderComponent` | Physics | atomic | default | Atomic physics primitive. | `component.collider` | v1 | `physics.v1` | `runtime.physicsSystem`, `runtime.collisionSystem` | consumed | not_applicable | `public/medium` |
| `DecalComponent` | Rendering | atomic | default | Renderer presentation component; this contract covers existing metadata/projection without adding decal feature depth. | `component.decal` | v1 | `rendering.v1` | None | projected | consumed | `public/low` |
| `ForceComponent` | Physics | atomic | hidden | Internal Runtime queue; use ctx.physics.command(entityId, bodyCommand). | `component.force` | v1 | `physics.v1` | `runtime.physicsSystem`, `runtime.collisionSystem` | internal | not_applicable | `server/medium` |
| `HairComponent` | Rendering | atomic | default | Single creator-facing hair surface; groom data and binding artifacts live on versioned hair assets. | `component.hair` | v2 | `rendering.v1` | None | projected | consumed | `public/low` |
| `HighlightComponent` | Rendering | atomic | default | Renderer overlay-stage presentation state (D-013); persistent outline, not a cue. The projection authority is protocol resolveHighlightPresentation and only enabled highlights count against the per-tier budget. | `component.highlight` | v2 | `rendering.v1` | None | projected | consumed | `public/low` |
| `InputProfileComponent` | Input | atomic | default | One canonical InputProfileComponent; transient UI contexts remain client-local. | `component.input_profile` | v2 | `input.v1` | `runtime.movementLivenessSystem`, `runtime.movementSystem`, `runtime.actionSystem` | consumed | not_applicable | `owner/high/reliable` |
| `InstanceSetComponent` | Rendering | atomic | default | E1 aggregate substrate: thin ref to an InstanceSet resource; render lowers to InstancedMesh per kind, physics to ONE collider per aggregate. | `component.instance_set` | v1 | None | None | projected | consumed | `public/low` |
| `JointComponent` | Physics | atomic | default | Fixed/revolute/prismatic/spherical/spring/rope; single owner references the connected body. | `component.joint` | v1 | None | None | consumed | not_applicable | `public/medium` |
| `LightComponent` | Rendering | atomic | default | Atomic rendering primitive. | `component.light` | v3 | `rendering.v1` | None | projected | consumed | `public/low` |
| `LocalEnvironmentVolumeComponent` | Rendering | atomic | default | Renderer environment-override primitive (ADR 0178); equal-priority overlaps are warned and resolve deterministically by entity id. | `component.local_environment_volume` | v1 | `rendering.v1` | None | projected | consumed | `public/low` |
| `MaterialInstanceOverrideComponent` | Rendering | atomic | default | Atomic renderer state. | `component.material_override` | v2 | `rendering.v1` | None | projected | consumed | `public/low` |
| `NameplateComponent` | Gameplay | atomic | default | On a character a player controls, the game adds a nameplate and keeps the name correct itself. publicLabel shows to every player; teamLabel shows only to that player's own team. | `component.nameplate` | v1 | None | None | projected | consumed | `public/low`, `teamLabel: team` |
| `NavAgentComponent` | Physics | atomic | default | Runtime navigation behavior; the nav system steers the agent via the existing mover (no second mover) over the recast navmesh resource (no navmesh-as-component). | `component.nav_agent` | v1 | `world.v1` | None | consumed | not_applicable | `public/medium` |
| `NetworkProfileComponent` | Networking | atomic | default | Netcode presentation intent for replicated motion; the runtime interpolation buffer reads this profile (no second mover). | `component.network_profile` | v3 | `network.v1` | None | consumed | not_applicable | `public/high/reliable` |
| `OwnershipComponent` | Gameplay | atomic | default | Records the owning player so the claim survives them leaving and rejoining. The game keeps it in step with every scripted ownership transfer. Carrying something is parenting it, which already counts as held. | `component.ownership` | v1 | None | None | consumed | not_applicable | `public/low` |
| `ParticleEmitterComponent` | Rendering | atomic | default | Default by creator expectation; particles are built-in in comparable creator engines. | `component.particle_emitter` | v4 | `rendering.v1` | None | projected | consumed | `public/low` |
| `PixelSurfaceComponent` | Rendering | atomic | default | Authored pixel-surface primitive; scripts draw a bounded client-local RGBA buffer presented as a camera or world quad. | `component.pixel_surface` | v1 | None | None | projected | consumed | `public/low` |
| `ReflectionProbeComponent` | Rendering | atomic | default | Renderer presentation primitive with explicit runtime/renderer projection status. | `component.reflection_probe` | v1 | `rendering.v1` | None | projected | consumed | `public/low` |
| `RenderableComponent` | Rendering | atomic | default | Atomic rendering primitive. | `component.renderable` | v3 | `rendering.v1` | None | projected | consumed | `public/low` |
| `RigidBodyComponent` | Physics | atomic | default | Atomic physics primitive. | `component.rigid_body` | v1 | `physics.v1` | `runtime.physicsSystem`, `runtime.collisionSystem` | consumed | not_applicable | `public/medium` |
| `ScriptComponent` | Scripting | atomic | default | Atomic behavior attachment. | `component.script` | v1 | `script.v1` | `runtime.scriptSystem`, `runtime.componentPatchSystem`, `runtime.observerCommandSystem` | consumed | not_applicable | `server/low` |
| `SpatialCaptureComponent` | Rendering | atomic | default | Semantic placement primitive for imported captures; renderability is separate from playability proof. | `component.spatial_capture` | v3 | `rendering.v1` | None | projected | consumed | `public/low` |
| `SpawnPointComponent` | Gameplay | atomic | default | Where players appear on join and respawn. The game's character policy names a spawn tag, and only points carrying that tag are eligible, then narrowed to the matching team or reserved player slot and ordered by priority. | `component.spawn_point` | v1 | None | None | consumed | not_applicable | `server/low` |
| `TerrainComponent` | Rendering | atomic | default | Renderer presentation component; this contract covers existing metadata/projection without adding terrain feature depth. | `component.terrain` | v1 | `rendering.v1` | None | projected | consumed | `public/low` |
| `TextRenderableComponent` | Rendering | atomic | default | Renderer presentation component: canonical MSDF text (shared per-family atlas, resolution-independent at any distance) with render-budget proof. | `component.text_renderable` | v2 | `rendering.v1` | None | projected | consumed | `public/low` |
| `TimerComponent` | Runtime | atomic | default | Atomic runtime event source. | `component.timer` | v1 | `timer.v1` | `runtime.timerSystem` | consumed | not_applicable | `public/low` |
| `TransformComponent` | Spatial | atomic | default | Atomic spatial primitive. | `component.transform` | v2 | `hierarchy.v1` | `runtime.hierarchyTransformSystem` | projected | consumed | `public/high/reliable` |
| `VelocityComponent` | Physics | atomic | hidden | Internal runtime lane (W5-01b); launch characters via Physics.command '{' type: "launch" '}' and configure movement on CharacterMovementComponent. | `component.velocity` | v1 | `physics.v1` | `runtime.physicsSystem`, `runtime.collisionSystem` | internal | not_applicable | `public/medium` |
| `WaterComponent` | Rendering | atomic | default | Renderer presentation component; this contract covers existing metadata/projection without adding water feature depth. | `component.water` | v1 | `rendering.v1` | None | projected | consumed | `public/low` |

## Removed Component Contracts

| Component | Contract version | Removed in | Replacement | Migration class | Contract hash |
| --- | --- | --- | --- | --- | --- |
| `StateMachineComponent` | `component.state_machine.removed.v1` | `v1.0.1` | `ScriptComponent` | manual | `sha256:81345b3703cb0f142082b2d4856aa94a716e63c79ddd89cb31f48c2405781b3d` |
| `HierarchyComponent` | `component.hierarchy.removed.v1` | `v1.0.1` | None | safe_auto | `sha256:ff64b237d916282efd0a1ed734fb7b253db699469bde253199051ab8e64cd74c` |
| `InputActionDeclarationComponent` | `component.InputActionDeclarationComponent.removed.v1` | `v1.0.1` | `InputProfileComponent` | safe_auto | `sha256:b1b0e7f08edd7bf2401b705673307e5282e68c443d0dfcc3ca29d7cdad16396e` |
| `InputBindingSetComponent` | `component.InputBindingSetComponent.removed.v1` | `v1.0.1` | `InputProfileComponent` | safe_auto | `sha256:881661044da03c69100dbbdfbf253d3c208ebf16c6483bce6b27042f49848fe5` |
| `InputContextStackComponent` | `component.InputContextStackComponent.removed.v1` | `v1.0.1` | `InputProfileComponent` | safe_auto | `sha256:080e867e1cbdbfcb763797f871049a0373f59e07088faac2c0ee9b216093f768` |

## Recently Stripped Pattern Components

`ActionSourceComponent`, `BoundsComponent`, `CameraTargetComponent`, `CollectibleComponent`, `CollectorComponent`, `CollisionConsumeComponent`, `DamageEventComponent`, `DamageModifierComponent`, `DamageOnContactComponent`, `GravityComponent`, `GridAlignmentComponent`, `HealthComponent`, `HitscanWeaponComponent`, `InputAccessibilityProfileComponent`, `InputFeedbackComponent`, `InteractableComponent`, `JointComponent`, `MotionComponent`, `PlayerControllerComponent`, `ProjectileComponent`, `RespawnTimerComponent`, `SpawnPoolComponent`, `SplitActionComponent`, `StatsComponent`, `WeaponSlotComponent`

## Roadmap Components

These names are not current ECS components and may appear only in explicit roadmap sections.

`AudioListenerComponent`, `BillboardComponent`, `NetworkVisibilityComponent`, `UIPanelLifecycleComponent`

## AnimationStateComponent

Declarative animation clip state scripts can mutate; renderers may map it onto clip playback and blending.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Atomic renderer state. |
| Default component key | `component.animation_state` |
| Authored contract version | v1 |
| Catalog contract ID | `component.animation_state.contract.v1` |
| Contract hash | `sha256:08e76f51a077e22cf56c245353f38f9e4c6c55d16b643a769ae74600051125ec` |
| Field contract hash | `sha256:63176a96ab8df46b2d0d76caa133c971a68ac5e487ab046070b232ea50a655f4` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/sceneGraphAppearance.ts:applyAnimationIfManaged [web_client/spa/src/render/sceneGraphAppearance.ts:applyAnimationIfManaged] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `clipKey`, `playMode`, `speed`, `startTime` |
| Canonical field count | 5 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `clipKey` | Name of the animation clip to play (e.g. idle, walk). | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | maxLength: 160<br>semantic: `intra_asset_key` |
| `playMode` | Loop forever, play once, or bounce back and forth (ping-pong). | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `loop`, `once`, `ping-pong` |
| `speed` | Playback rate multiplier; 1 is normal speed. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number |  |
| `startTime` | Seconds into the clip where playback begins. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "AnimationStateComponent",
  "clipKey": "idle",
  "playMode": "loop",
  "startTime": 0,
  "speed": 1
}
```
## AudioEmitterComponent

Presentation primitive for positional and non-positional audio playback from an audio asset key.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Audio |
| Component palette visibility | default |
| Component palette notes | Atomic audio primitive. |
| Default component key | `component.audio_emitter` |
| Authored contract version | v4 |
| Catalog contract ID | `component.audio_emitter.contract.v1` |
| Contract hash | `sha256:1180b95e9d59e0ad5307897982bf64439af30de2be00dfaba1ed0ff0b16c91e9` |
| Field contract hash | `sha256:17331c2913e64f04a5dd8a917ca6da20d77cc0b74fed7c304d2c2f954e259536` |
| Migration class | none |
| Capability packs | `audio.v1` |
| Runtime systems | None |
| Runtime consumer status | consumed |
| Renderer consumer status | not_applicable |
| Consumer status reason | read by web_client/spa/src/runtime/runtimeSessionProjection.ts:buildRuntimeSessionProjection [web_client/spa/src/runtime/runtimeSessionProjection.ts:buildRuntimeSessionProjection] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `enabled`, `loop`, `maxDistance`, `pitch`, `playOnStart`, `refDistance`, `rolloff`, `soundAssetId`, `spatial`, `targetEntityId`, `volume` |
| Canonical field count | 12 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `enabled` | Master switch; no sound plays while off. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `loop` | Repeat the sound until it is stopped. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `maxDistance` | Hard audible radius: the sound is silent at and beyond this distance under every rolloff, and is not mixed at all outside it. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0.01<br>max: 100000 |
| `pitch` | Playback rate multiplier; higher plays faster and higher-pitched. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0.01 |
| `playOnStart` | Start playing as soon as the emitter becomes active. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `refDistance` | Distance where the sound still plays at full volume. Raise it to keep a sound loud across a wider area before it starts fading. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0.01<br>max: 100000 |
| `rolloff` | Shape of the fade between Ref distance and Max distance. Linear fades evenly; Inverse and Exponential stay louder up close and fall away faster further out. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `linear`, `inverse`, `exponential` |
| `soundAssetId` | Audio ProjectAsset UUID to play. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `asset_id`<br>ref: asset id audio |
| `spatial` | Place the sound in 3D so it pans and fades with distance; off plays it flat everywhere. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `targetEntityId` | Another entity the sound follows instead of this one. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `entity_id`<br>ref: entity id |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |
| `volume` | Loudness from 0 (silent) to 1 (full). | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0<br>max: 1 |

Defaults:

```json
{
  "type": "AudioEmitterComponent",
  "volume": 1,
  "pitch": 1,
  "loop": false,
  "spatial": true,
  "rolloff": "inverse",
  "refDistance": 1,
  "maxDistance": 32,
  "playOnStart": false,
  "enabled": false
}
```
## AvatarSlotComponent

Declares how a player's own avatar is applied to a Character rig: the height it is scaled to, the camera attach point, and the animation set it is expected to provide. At spawn the server resolves the player's avatar and records what it applied here. Colliders, movement, and the camera always stay on the rig, so an avatar never changes physics.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Gameplay |
| Component palette visibility | default |
| Component palette notes | The character always keeps its own colliders, movement, and camera. The game resolves each player's avatar at spawn and records what it applied here, so an avatar never changes how a character moves or collides. |
| Default component key | `component.avatar_slot` |
| Authored contract version | v1 |
| Catalog contract ID | `component.avatar_slot.contract.v1` |
| Contract hash | `sha256:5384e676d0ef5b0920ff162b8da45d76a25ff7dd2fea65644b63c9c7dc2fd00f` |
| Field contract hash | `sha256:acde82ec12d5467bff07abf5903bbc73c92e70511b266f62dcfa66d849f59c32` |
| Migration class | none |
| Capability packs | None |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by server/src/modules/runtime/runtimeCharacterSystem.ts:resolveAvatarSlotForSeat [server/src/modules/runtime/runtimeCharacterSystem.ts:resolveAvatarSlotForSeat] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `animationSet`, `avatarAssetId`, `avatarSource`, `cameraSocket`, `fallbackReason`, `rigHeight` |
| Canonical field count | 10 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `animationSet` | The clip set an applied avatar is expected to provide (idle, walk, and run at minimum). | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | minLength: 1<br>maxLength: 160 |
| `avatarAssetId` | The model asset the server applied into this slot at spawn; empty when the rig's own body was kept. | string | no | hidden | hidden | hidden | hidden | hidden | exposed | projected | consumed | hidden: Server-derived field. | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `asset_id`<br>ref: asset id model |
| `avatarSource` | Where the applied body came from: the player's account avatar, the team uniform, or the rig's own body as the fallback. The server writes it at spawn. | string | no | hidden | hidden | hidden | hidden | hidden | exposed | projected | consumed | hidden: Server-derived field. | enum: `account`, `uniform`, `prefab_fallback` |
| `cameraSocket` | Declared camera attach point in the rig's local space; a first-person rig puts it at eye height. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `cameraSocket.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `cameraSocket.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `cameraSocket.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `fallbackReason` | Why the rig's own body was kept, so the reason is visible instead of silent. The server writes it at spawn. | string | no | hidden | hidden | hidden | hidden | hidden | exposed | projected | consumed | hidden: Server-derived field. | maxLength: 160 |
| `rigHeight` | Height in meters the applied avatar is scaled to. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01<br>max: 20 |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "AvatarSlotComponent",
  "rigHeight": 1.8,
  "cameraSocket": {
    "x": 0,
    "y": 1.6,
    "z": 0
  },
  "animationSet": "humanoid.basic"
}
```
## CameraComponent

Attach to an entity with Transform to make it a viewpoint players see through. The highest-priority active camera is used in Play mode. Follow target drives position; Look at target drives rotation while set.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Atomic camera primitive. |
| Default component key | `component.camera` |
| Authored contract version | v3 |
| Catalog contract ID | `component.camera.contract.v1` |
| Contract hash | `sha256:848bfc8e33b99d5423d5eb1f7822696bd59715be0d6b884f2526809f7845a9dc` |
| Field contract hash | `sha256:30611364294b7104e8aa3e3eeba9e7f4ec73de6dab7b7c8f64280d1bacfc015c` |
| Migration class | none |
| Capability packs | `camera.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/camera/resolveCameraPose.ts:resolveCameraPose [web_client/spa/src/render/camera/resolveCameraPose.ts:resolveCameraPose] |
| Replication policy | `public/medium` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `active`, `clearColor`, `clearMode`, `exposure`, `far`, `followEntityId`, `followOffset`, `followSmoothing`, `fov`, `lookAtEntityId`, `lookAtOffset`, `near`, `orthoSize`, `postProcessing`, `priority`, `projection`, `toneMapping` |
| Canonical field count | 34 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `active` | Lets this camera be picked in Play mode; the highest-priority active camera wins. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `clearColor` | Background color used when Clear mode is color. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `clearMode` | What fills the background: a flat color, the skybox, or nothing. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `color`, `skybox`, `none` |
| `exposure` | Optional renderer exposure override for this camera. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 10 |
| `far` | Farthest distance the camera can see; anything beyond is cut away. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01 |
| `followEntityId` | When set, camera position is driven from this entity plus Follow offset. Clear it to use Transform position. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `entity_id`<br>ref: entity id |
| `followOffset` | Position offset used while Follow target is set. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `followOffset.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `followOffset.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `followOffset.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `followSmoothing` | Blend amount for target-follow movement. 0 snaps immediately; higher values smooth more. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `fov` | How wide the camera sees, in degrees. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 1<br>max: 170 |
| `lookAtEntityId` | When set, camera rotation is driven toward this entity. Clear it to use Transform rotation. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `entity_id`<br>ref: entity id |
| `lookAtOffset` | Target-space offset used while Look at target is set. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `lookAtOffset.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `lookAtOffset.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `lookAtOffset.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `near` | Closest distance the camera can see; anything nearer is cut away. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `orthoSize` | Half the visible height in world units. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01 |
| `postProcessing` | Camera-owned depth of field and motion blur intent. Shared look effects are authored on the World; anti-aliasing is owned by Project Graphics. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `postProcessing.depthOfField` |  | object | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `postProcessing.depthOfField.bokehScale` |  | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 4 |
| `postProcessing.depthOfField.focalLength` |  | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.001<br>max: 128 |
| `postProcessing.depthOfField.focusDistance` |  | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.001<br>max: 1000 |
| `postProcessing.depthOfField.mode` |  | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `off`, `auto`, `on` |
| `postProcessing.depthOfField.quality` |  | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `low`, `medium`, `high` |
| `postProcessing.motionBlur` |  | object | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `postProcessing.motionBlur.mode` |  | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `off`, `auto`, `on` |
| `postProcessing.motionBlur.quality` |  | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `low`, `medium`, `high` |
| `postProcessing.motionBlur.samples` |  | integer | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 2<br>max: 32 |
| `priority` | Tie-breaker among active cameras; higher numbers win. | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: -9007199254740991<br>max: 9007199254740991 |
| `projection` | Perspective gives natural depth; orthographic gives a flat, stylized view. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `perspective`, `orthographic` |
| `toneMapping` | Optional renderer tone-mapping override for this authored camera. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `aces`, `agx`, `reinhard`, `neutral`, `linear` |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "CameraComponent",
  "fov": 60,
  "near": 0.1,
  "far": 2000,
  "projection": "perspective",
  "orthoSize": 10,
  "active": true,
  "priority": 10,
  "followOffset": {
    "x": 0,
    "y": 6,
    "z": 10
  },
  "followSmoothing": 0.15,
  "lookAtOffset": {
    "x": 0,
    "y": 0,
    "z": 0
  },
  "clearMode": "skybox",
  "clearColor": "#0b1020"
}
```
## CharacterMovementComponent

Authored config for the shared kinematic character controller: horizontal speed cap plus deterministic gravity and jumping over authored collision. The explicit KCC-governance flag opts an entity into predicted kinematic movement.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Physics |
| Component palette visibility | default |
| Component palette notes | Runtime movement behavior; structural hierarchy is projected from entity fields, not a component. |
| Default component key | `component.character_movement` |
| Authored contract version | v2 |
| Catalog contract ID | `component.character_movement.contract.v1` |
| Contract hash | `sha256:a68ae332d0d5cbab1ffa0ed001b1babf6d5e349aa6081719eca723080bdba446` |
| Field contract hash | `sha256:f04967d695bb4ca43800d84a87ab603d43e1a4bb38147517ed41398a5854d546` |
| Migration class | none |
| Capability packs | `physics.v1` |
| Runtime systems | `runtime.physicsSystem`, `runtime.collisionSystem` |
| Runtime consumer status | consumed |
| Renderer consumer status | not_applicable |
| Consumer status reason | read by packages/runtime-shared/src/movementParams.ts:deriveMovementParams [packages/runtime-shared/src/movementParams.ts:deriveMovementParams] |
| Replication policy | `public/medium` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `enableKcc`, `gravity`, `jumpSpeed`, `maxSpeed` |
| Canonical field count | 5 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `enableKcc` | Opt this entity into the shared kinematic character controller (predicted walk + jump). | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `gravity` | Downward acceleration for the deterministic kinematic vertical motor. Leave empty for horizontal-only movement. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `jumpSpeed` | Upward velocity applied when a grounded jump is accepted by the vertical motor. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `maxSpeed` | Caps how fast the entity may move; leave empty for the runtime default. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "CharacterMovementComponent",
  "enableKcc": false
}
```
## ColliderComponent

Passive server-owned shape for blocking active movers, physics contacts, triggers, placement diagnostics, and physics queries.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Physics |
| Component palette visibility | default |
| Component palette notes | Atomic physics primitive. |
| Default component key | `component.collider` |
| Authored contract version | v1 |
| Catalog contract ID | `component.collider.contract.v1` |
| Contract hash | `sha256:ca0adf0bc6f1e02d695f1fd5469b0712a65fe94d67f4fcee9e6d4f288a21c6f5` |
| Field contract hash | `sha256:11c06ac29e7b7ff1b20fddfc5dd7118d8b566956c29b8242a0e28c433c725ecd` |
| Migration class | none |
| Capability packs | `physics.v1` |
| Runtime systems | `runtime.physicsSystem`, `runtime.collisionSystem` |
| Runtime consumer status | consumed |
| Renderer consumer status | not_applicable |
| Consumer status reason | read by server/src/modules/runtime/physics/componentSync.ts:colliderDescFor [server/src/modules/runtime/physics/componentSync.ts:colliderDescFor] |
| Replication policy | `public/medium` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `collision`, `friction`, `layers`, `restitution`, `shape`, `size` |
| Canonical field count | 11 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `collision` | Solid blocks movers and physics bodies (world geometry). Trigger is a non-blocking sensor that reports enter/exit to scripts when an active participant overlaps it. None is a non-blocking sensor with no events (query-only). | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `solid`, `trigger`, `none` |
| `friction` | Surface grip from 0 (ice) to 1 (rubber). | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0<br>max: 1 |
| `layers` | Collision group names that decide what this collider interacts with. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `layers[]` | Collision group names that decide what this collider interacts with. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array | minLength: 1<br>maxLength: 80 |
| `restitution` | Bounciness from 0 (no bounce) to 1 (full rebound). | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0<br>max: 1 |
| `shape` | Collision volume around the entity; mesh uses the model's own geometry. | string\|object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/union | enum: `box`, `sphere`, `capsule`, `disc`, `mesh` |
| `size` | Extents of the collision volume per axis, in world units. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/vec3 |  |
| `size.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested |  |
| `size.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested |  |
| `size.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested |  |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "ColliderComponent",
  "shape": "sphere",
  "size": {
    "x": 1,
    "y": 1,
    "z": 1
  },
  "collision": "solid",
  "layers": [
    "default"
  ],
  "friction": 0.5,
  "restitution": 0
}
```
## DecalComponent

Surface-layer projection primitive for markings, scuffs, and signs.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Renderer presentation component; this contract covers existing metadata/projection without adding decal feature depth. |
| Default component key | `component.decal` |
| Authored contract version | v1 |
| Catalog contract ID | `component.decal.contract.v1` |
| Contract hash | `sha256:0aba30c36e3a92609aa515cd402aa3ff611f602a100542f926c6a6b204b2aeee` |
| Field contract hash | `sha256:925d8cdeba04f1a82f6d38e75efbe1f001950d673521c3bcc0c7da378cbefeb0` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/editorSceneProjection.ts:decalFromArtifact [web_client/spa/src/render/editorSceneProjection.ts:decalFromArtifact] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `atlasSlot`, `blendMode`, `color`, `enabled`, `opacity`, `projectionBox`, `projectionDepth`, `textureAssetId` |
| Canonical field count | 16 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `atlasSlot` | Region of the texture to use (u, v, width, height as 0-1 fractions). | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `atlasSlot.height` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.001<br>max: 1 |
| `atlasSlot.u` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `atlasSlot.v` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `atlasSlot.width` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.001<br>max: 1 |
| `blendMode` | How the decal combines with the surface: blend, multiply, or add. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `blend`, `multiply`, `add` |
| `color` | Tint multiplied into the decal image. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `enabled` | Show or hide the decal. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `opacity` | How strongly the decal shows, from 0 (invisible) to 1 (full). | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `projectionBox` | Volume the decal projects through; surfaces inside it receive the image. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `projectionBox.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `projectionBox.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `projectionBox.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `projectionDepth` | How far the decal projects forward onto surfaces, in world units. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01 |
| `textureAssetId` | Image asset projected onto surfaces. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `asset_id`<br>ref: asset id texture |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "DecalComponent",
  "enabled": true,
  "projectionBox": {
    "x": 1,
    "y": 1,
    "z": 0.1
  },
  "color": "#ffffff",
  "opacity": 1,
  "projectionDepth": 0.1,
  "blendMode": "blend",
  "atlasSlot": {
    "u": 0,
    "v": 0,
    "width": 1,
    "height": 1
  }
}
```
## ForceComponent

Private per-tick physics mutation queue consumed and cleared by the physics system.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Physics |
| Component palette visibility | hidden |
| Component palette notes | Internal Runtime queue; use ctx.physics.command(entityId, bodyCommand). |
| Default component key | `component.force` |
| Authored contract version | v1 |
| Catalog contract ID | `component.force.contract.v1` |
| Contract hash | `sha256:0e9e12ae17266ae59c689e8a00c06d428f9fca2bd47640db6c267da985bdfe18` |
| Field contract hash | `sha256:5feda4afd785c230eb5d6c35d3a1fd5d412180034585e87a035d7e2398d69baa` |
| Migration class | none |
| Capability packs | `physics.v1` |
| Runtime systems | `runtime.physicsSystem`, `runtime.collisionSystem` |
| Runtime consumer status | internal |
| Renderer consumer status | not_applicable |
| Consumer status reason | Internal engine queue consumed by runtime systems. |
| Replication policy | `server/medium` |
| Authorization | Internal Runtime physics command queue; scripts request body mutations through ctx.physics.command. |
| Inspector fields | `pending` |
| Canonical field count | 59 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `pending` | Queued physics commands (forces, impulses, teleports) the engine applies and clears each tick; scripts enqueue them via ctx.physics.command. | array | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[]` | Queued physics commands (forces, impulses, teleports) the engine applies and clears each tick; scripts enqueue them via ctx.physics.command. | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].applyAtPoint` |  | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].applyAtPoint.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].applyAtPoint.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].applyAtPoint.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].force` |  | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].force.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].force.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].force.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].impulse` |  | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].impulse.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].impulse.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].impulse.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset` |  | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.angularVelocity` |  | object | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.angularVelocity.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.angularVelocity.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.angularVelocity.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.position` |  | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.position.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.position.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.position.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.rotation` |  | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.rotation.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.rotation.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.rotation.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.velocity` |  | object | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.velocity.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.velocity.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.velocity.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].reset.wake` |  | boolean | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport` |  | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.angularVelocity` |  | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.angularVelocity.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.angularVelocity.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.angularVelocity.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.position` |  | object | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.position.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.position.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.position.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.rotation` |  | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.rotation.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.rotation.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.rotation.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.velocity` |  | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.velocity.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.velocity.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.velocity.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].teleport.wake` |  | boolean | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].torque` |  | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].torque.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].torque.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].torque.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].torqueImpulse` |  | object | no | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].torqueImpulse.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].torqueImpulse.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `pending[].torqueImpulse.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | internal | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "ForceComponent",
  "pending": []
}
```
## HairComponent

Places a canonical hair groom asset on an entity with bounded render, simulation, LOD, binding, and budget overrides.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Single creator-facing hair surface; groom data and binding artifacts live on versioned hair assets. |
| Default component key | `component.hair` |
| Authored contract version | v2 |
| Catalog contract ID | `component.hair.contract.v1` |
| Contract hash | `sha256:c2806c3a5a4440e3fc48599c25d27905e784afed36ebe15ead8fd53b018eb79d` |
| Field contract hash | `sha256:3154a83492c3a9257848bec974168100bc4c76b124df7baeb0d6b7f94c939d8f` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/hair/HairStrandGeometry.ts:buildHairStrandGeometry [web_client/spa/src/render/hair/HairStrandGeometry.ts:buildHairStrandGeometry] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `bindingKey`, `budget`, `enabled`, `groomAssetId`, `materialOverrides`, `render`, `simulation`, `targetEntityId` |
| Canonical field count | 31 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `bindingKey` | Optional generated binding artifact used to validate this instance against its groom (missing or mismatched bindings surface as diagnostics). Does not change how the hair renders. Choices come from the selected groom asset; bindings are generated only by import/editor flows. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | minLength: 1<br>maxLength: 240<br>semantic: `resource_option_key`<br>options: `groomAssetId` → `rendering.hairGroom.bindings.bindingKey` |
| `budget` | Optional per-instance caps applied before renderer quality caps. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `budget.maxDistance` |  | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01<br>max: 100000 |
| `budget.maxGpuBytes` |  | integer | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 1<br>max: 536870912 |
| `budget.maxSimGuides` |  | integer | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1000000 |
| `budget.maxVisibleStrands` |  | integer | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 10000000 |
| `enabled` | Show or hide this hair groom without removing the component. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `groomAssetId` | Canonical versioned hair groom asset. Heavy groom buffers stay on the asset, not this component. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `asset_id`<br>ref: asset id hair |
| `materialOverrides` | Per-instance overrides of the groom's material facts (typed keys only; raw groom data is not accepted). | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `materialOverrides.anisotropy` |  | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `materialOverrides.baseColor` |  | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `materialOverrides.melanin` |  | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `materialOverrides.roughness` |  | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.2<br>max: 1 |
| `render` | Renderer-owned representation and density controls. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `render.density` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `render.hairlineGapDeg` | Front scalp wedge in degrees; 0 keeps a full-circle hairline. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 288 |
| `render.length` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.2<br>max: 2 |
| `render.lodBias` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: -4<br>max: 4 |
| `render.representation` |  | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `auto`, `cards`, `strands`, `ribbons` |
| `render.shadowMode` |  | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `off`, `auto` |
| `render.taper` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.05<br>max: 1 |
| `render.widthScale` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01<br>max: 10 |
| `simulation` | Bounded guide-level simulation intent. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `simulation.collision` |  | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `none`, `floor` |
| `simulation.damping` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `simulation.gravity` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: -100<br>max: 100 |
| `simulation.mode` |  | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `none`, `procedural` |
| `simulation.stiffness` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `simulation.windInfluence` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `targetEntityId` | Optional entity id whose mesh/skeleton identity the binding was generated against; the server compares it against the live target to raise binding-mismatch diagnostics. No render effect. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `entity_id`<br>ref: entity id |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "HairComponent",
  "enabled": false,
  "materialOverrides": {},
  "render": {
    "representation": "auto",
    "shadowMode": "auto",
    "density": 1,
    "widthScale": 1,
    "lodBias": 0,
    "length": 0.95,
    "taper": 0.28,
    "hairlineGapDeg": 0
  },
  "simulation": {
    "mode": "none",
    "stiffness": 0.65,
    "damping": 0.35,
    "gravity": -9.8,
    "windInfluence": 0.15,
    "collision": "none"
  },
  "budget": {}
}
```
## HighlightComponent

Persistent selection or interest treatment drawn around an entity's oriented render bounds at the overlay stage.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Renderer overlay-stage presentation state (D-013); persistent outline, not a cue. The projection authority is protocol resolveHighlightPresentation and only enabled highlights count against the per-tier budget. |
| Default component key | `component.highlight` |
| Authored contract version | v2 |
| Catalog contract ID | `component.highlight.contract.v1` |
| Contract hash | `sha256:ea6428131b00ec02f9cc27f5d22ab8af46df568d37f51ff129319307764ea143` |
| Field contract hash | `sha256:25b6ae0143054cb7eb6093e23f66919291f2d3236082d8509d2f8c1d5cbef08c` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by packages/protocol/src/index.ts:resolveHighlightPresentation [packages/protocol/src/index.ts:resolveHighlightPresentation] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `color`, `depthMode`, `enabled`, `fillOpacity` |
| Canonical field count | 5 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `color` | Bounds-outline and bounds-fill color. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `depthMode` | occluded hides the outline behind geometry; xray always draws it on top so it reads through walls. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `occluded`, `xray` |
| `enabled` | Show or hide the outline. Only enabled highlights count against the per-tier budget. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `fillOpacity` | Opacity of the oriented bounds fill; 0 draws an outline only. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "HighlightComponent",
  "enabled": true,
  "color": "#ffd54a",
  "fillOpacity": 0,
  "depthMode": "occluded"
}
```
## InputProfileComponent

One canonical input contract: action declarations, policy, prediction, and physical bindings nested under each action.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Input |
| Component palette visibility | default |
| Component palette notes | One canonical InputProfileComponent; transient UI contexts remain client-local. |
| Default component key | `component.input_profile` |
| Authored contract version | v2 |
| Catalog contract ID | `component.input_profile.contract.v1` |
| Contract hash | `sha256:aab52e99f0174ee3c19a4327f03ff13843431aedf104f3e0431f5768986cd023` |
| Field contract hash | `sha256:62e5602c9c9c9f39efed4e99d7fd10fad0a53ea1068842ad2c8ac2bc161e6e90` |
| Migration class | none |
| Capability packs | `input.v1` |
| Runtime systems | `runtime.movementLivenessSystem`, `runtime.movementSystem`, `runtime.actionSystem` |
| Runtime consumer status | consumed |
| Renderer consumer status | not_applicable |
| Consumer status reason | read by packages/runtime-shared/src/inputProfileProjection.ts:inputProfileFromUnknown [packages/runtime-shared/src/inputProfileProjection.ts:inputProfileFromUnknown] |
| Replication policy | `owner/high/reliable` |
| Authorization | Owner-private input state; server validates the canonical profile version before command routing. |
| Inspector fields | `actions`, `version` |
| Canonical field count | 102 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `actions` | Canonical actions and their owned physical bindings. Runtime context starts in gameplay; UI overlays use dispatcher push/pop. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[]` | Canonical actions and their owned physical bindings. Runtime context starts in gameplay; UI overlays use dispatcher push/pop. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].bindings` | Physical controls nested under this action; an action-key mismatch is structurally impossible. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].bindings[]` | Physical controls nested under this action; an action-key mismatch is structurally impossible. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].bindings[].id` | A unique name for this binding within the action, so it can be overridden or replaced. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | minLength: 1<br>maxLength: 160 |
| `actions[].bindings[].priority` | Which binding wins when more than one could fire; higher priority is chosen first. | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: -9007199254740991<br>max: 9007199254740991 |
| `actions[].bindings[].processors` | Steps that reshape this binding's input value before the action uses it. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].bindings[].processors[]` | Steps that reshape this binding's input value before the action uses it. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].bindings[].processors[].kind` | Which processing step this is, such as a deadzone, a curve, an inversion, or a clamp. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `deadzone`, `curve`, `invert`, `sensitivity`, `clamp`, `normalize`, `axisComposition`, `stickComposition`, `threshold`, `quantize` |
| `actions[].bindings[].processors[].parameters` | Settings for the chosen processing step, such as the deadzone amount or the clamp range; a step that needs none takes an empty map. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/map |  |
| `actions[].bindings[].processors[].parameters.axes` |  | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested |  |
| `actions[].bindings[].processors[].parameters.axes[]` |  | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | enum: `x`, `y`, `z` |
| `actions[].bindings[].processors[].parameters.exponent` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | max: 16 |
| `actions[].bindings[].processors[].parameters.max` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested |  |
| `actions[].bindings[].processors[].parameters.min` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested |  |
| `actions[].bindings[].processors[].parameters.scale` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | max: 1000 |
| `actions[].bindings[].processors[].parameters.step` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | max: 1000000000 |
| `actions[].bindings[].processors[].parameters.threshold` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | min: 0<br>max: 1 |
| `actions[].bindings[].processors[].parameters.value` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | min: 0 |
| `actions[].bindings[].sources` | The physical controls that feed this binding, such as a key, a mouse button, or a gamepad stick; several are combined into one input. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].bindings[].sources[]` | The physical controls that feed this binding, such as a key, a mouse button, or a gamepad stick; several are combined into one input. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].bindings[].sources[].control` | The specific control on that device, in its own vocabulary, such as KeyW for the keyboard or button.left for the mouse. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | minLength: 1<br>maxLength: 160 |
| `actions[].bindings[].sources[].device` | Which input device this control belongs to. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `keyboard`, `mouse`, `gamepad`, `touch`, `xr` |
| `actions[].bindings[].sources[].event` | Which change on the control feeds the action, such as a press, a release, or continuous movement. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `down`, `up`, `press`, `move`, `axis`, `sample` |
| `actions[].bindings[].sources[].index` | Which device to read when more than one is connected, such as a second gamepad; leave empty for the first. | integer | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0<br>max: 9007199254740991 |
| `actions[].bindings[].sources[].modifiers` | Extra conditions or transforms on this control, such as requiring Shift, or using it as the negative or one axis of a composed value. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].bindings[].sources[].modifiers[]` | Extra conditions or transforms on this control, such as requiring Shift, or using it as the negative or one axis of a composed value. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array | enum: `ctrl`, `shift`, `alt`, `meta`, `negative`, `positive`, `x`, `y` |
| `actions[].bindings[].triggerOverrides` | Triggers that replace the action's own triggers for just this binding. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].bindings[].triggerOverrides[]` | Triggers that replace the action's own triggers for just this binding. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].bindings[].triggerOverrides[].kind` | The kind of trigger condition, such as a press, a release, a hold, a tap, or a repeating tick. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `pressed`, `released`, `held`, `tap`, `doubleTap`, `hold`, `chord`, `continuous`, `continuous_delta`, `step`, `cancel` |
| `actions[].bindings[].triggerOverrides[].parameters` | Settings for the chosen trigger kind, such as its hold time or tap window; a trigger that needs none takes an empty map. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/map |  |
| `actions[].bindings[].triggerOverrides[].parameters.durationMs` |  | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | min: 1<br>max: 60000 |
| `actions[].bindings[].triggerOverrides[].parameters.intervalMs` |  | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | min: 1<br>max: 60000 |
| `actions[].bindings[].triggerOverrides[].parameters.size` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | max: 1000000 |
| `actions[].bindings[].triggerOverrides[].parameters.windowMs` |  | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | min: 1<br>max: 60000 |
| `actions[].contexts` | Which activity modes this action listens in, such as gameplay or a menu overlay; play starts in gameplay and overlays switch the active mode. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].contexts[]` | Which activity modes this action listens in, such as gameplay or a menu overlay; play starts in gameplay and overlays switch the active mode. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array | minLength: 1<br>maxLength: 120 |
| `actions[].key` | The action's unique name; bindings and scripts refer to it, such as player.jump or camera.look. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | minLength: 1<br>maxLength: 160 |
| `actions[].label` | A readable name for the action, shown in a rebinding menu; the key is used when this is empty. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | minLength: 1<br>maxLength: 160 |
| `actions[].policy` | The server-enforced rules for this action: who may perform it, how often, the limits on its value, and which system it drives. This is the authoritative permission, never inferred from bindings. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `actions[].policy.allowedActorKinds` | Restrict who may perform this action to certain kinds of controller, such as a player or a vehicle; empty lets anyone controlling the entity perform it. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].policy.allowedActorKinds[]` | Restrict who may perform this action to certain kinds of controller, such as a player or a vehicle; empty lets anyone controlling the entity perform it. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array | minLength: 1<br>maxLength: 80 |
| `actions[].policy.cameraRelative` | For a movement action, steer the input by where the camera faces, so pressing forward moves the character the way the player is looking; off follows the world axes. | boolean | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `actions[].policy.cooldown` | A minimum wait the server enforces between accepted uses of this action. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `actions[].policy.cooldown.ms` | The shortest time in milliseconds the server allows between accepted uses. | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 1<br>max: 9007199254740991 |
| `actions[].policy.effectSystem` | What performing this action does: move the controlled character, run gameplay logic in scripts, or turn the player's own view. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `movement`, `action`, `script`, `look` |
| `actions[].policy.lagCompensationMode` | How the server checks this action: against the current world, or by rewinding to what the player saw so a well-aimed shot still lands under latency. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `live`, `rewind_to_client_sample` |
| `actions[].policy.look` | Presentation-only look feel; it never mutates authoritative world state. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `actions[].policy.look.invertY` | Flip the vertical look, so pushing up looks down. | boolean | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `actions[].policy.look.pitchMaxDeg` | How far up, in degrees, the view may tilt. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: -89<br>max: 89 |
| `actions[].policy.look.pitchMinDeg` | How far down, in degrees, the view may tilt. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: -89<br>max: 89 |
| `actions[].policy.look.sensitivityDeg` | How far the view turns for a given amount of input; higher turns faster. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0.001<br>max: 45 |
| `actions[].policy.possessionRequired` | Accept this action only while the player is controlling a character. Turn it off for actions a spectator or a menu can perform. | boolean | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `actions[].policy.rateLimit` | Caps how often the server accepts this action, so a client cannot flood it. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `actions[].policy.rateLimit.perSecond` | The most times each second the server will accept this action. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0.001 |
| `actions[].policy.requiresBinding` | Accept this action only when it arrives through one of its declared bindings, so a client cannot invent it. | boolean | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `actions[].policy.targetPolicy` | Rules for an action that aims at a target, such as whether a target is required and how far or what kind it may be. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `actions[].policy.targetPolicy.allowedComponentTypes` | Restrict the target to entities that carry one of these components; empty allows any entity. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].policy.targetPolicy.allowedComponentTypes[]` | Restrict the target to entities that carry one of these components; empty allows any entity. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array | minLength: 1<br>maxLength: 160 |
| `actions[].policy.targetPolicy.allowedTags` | Restrict the target to entities that carry one of these tags; empty allows any entity. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].policy.targetPolicy.allowedTags[]` | Restrict the target to entities that carry one of these tags; empty allows any entity. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array | minLength: 1<br>maxLength: 160 |
| `actions[].policy.targetPolicy.entityRequired` | Require the target to be an entity, not just a point in the world. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `actions[].policy.targetPolicy.maxDistance` | The farthest, in meters, the target may be from the actor. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0.001 |
| `actions[].policy.targetPolicy.required` | Require the action to carry a target, either a point or an entity. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `actions[].policy.targetPolicy.requireLineOfSight` | Require a clear line from the actor to the target, so a blocked view rejects the action. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `actions[].policy.valueBounds` | Limits the server enforces on the action's value, so an out-of-range or malformed value is rejected rather than trusted. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `actions[].policy.valueBounds.enumValues` | Restrict the value to this set of allowed values; empty allows any value within the other bounds. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].policy.valueBounds.enumValues[]` | Restrict the value to this set of allowed values; empty allows any value within the other bounds. | string\|number\|boolean | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].policy.valueBounds.integer` | Require the value to be a whole number. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `actions[].policy.valueBounds.max` | The largest value the action may carry. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `actions[].policy.valueBounds.maxMagnitude` | The longest length a direction or vector value may have, so it cannot be pushed past full strength. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `actions[].policy.valueBounds.min` | The smallest value the action may carry. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `actions[].policy.valueBounds.minMagnitude` | The shortest length a direction or vector value may have. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `actions[].policy.valueBounds.requiredFields` | The named parts a composite or target value must include to be accepted. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].policy.valueBounds.requiredFields[]` | The named parts a composite or target value must include to be accepted. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array | minLength: 1<br>maxLength: 80 |
| `actions[].prediction` | How soon this action takes effect for the player relative to the server confirming it. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `actions[].prediction.mode` | Whether this action is applied on the player's own client right away and then reconciled with the server, kept on their client only, or held until the server confirms it. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `client_predicted`, `client_only`, `server_authoritative` |
| `actions[].processors` | Steps that reshape the raw input value before the action uses it, such as a deadzone, a sensitivity curve, or clamping. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].processors[]` | Steps that reshape the raw input value before the action uses it, such as a deadzone, a sensitivity curve, or clamping. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].processors[].kind` | Which processing step this is, such as a deadzone, a curve, an inversion, or a clamp. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `deadzone`, `curve`, `invert`, `sensitivity`, `clamp`, `normalize`, `axisComposition`, `stickComposition`, `threshold`, `quantize` |
| `actions[].processors[].parameters` | Settings for the chosen processing step, such as the deadzone amount or the clamp range; a step that needs none takes an empty map. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/map |  |
| `actions[].processors[].parameters.axes` |  | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested |  |
| `actions[].processors[].parameters.axes[]` |  | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | enum: `x`, `y`, `z` |
| `actions[].processors[].parameters.exponent` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | max: 16 |
| `actions[].processors[].parameters.max` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested |  |
| `actions[].processors[].parameters.min` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested |  |
| `actions[].processors[].parameters.scale` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | max: 1000 |
| `actions[].processors[].parameters.step` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | max: 1000000000 |
| `actions[].processors[].parameters.threshold` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | min: 0<br>max: 1 |
| `actions[].processors[].parameters.value` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | min: 0 |
| `actions[].touchPolicy` | Whether this action gets an on-screen touch control on a phone: automatic derives one from the action's shape, authored means the game places its own, disabled means none. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `automatic`, `authored`, `disabled` |
| `actions[].triggers` | The conditions a bound control must meet to fire this action, such as a press, a release, a hold, or a tap. | array | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].triggers[]` | The conditions a bound control must meet to fire this action, such as a press, a release, a hold, or a tap. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/array |  |
| `actions[].triggers[].kind` | The kind of trigger condition, such as a press, a release, a hold, a tap, or a repeating tick. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `pressed`, `released`, `held`, `tap`, `doubleTap`, `hold`, `chord`, `continuous`, `continuous_delta`, `step`, `cancel` |
| `actions[].triggers[].parameters` | Settings for the chosen trigger kind, such as its hold time or tap window; a trigger that needs none takes an empty map. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/map |  |
| `actions[].triggers[].parameters.durationMs` |  | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | min: 1<br>max: 60000 |
| `actions[].triggers[].parameters.intervalMs` |  | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | min: 1<br>max: 60000 |
| `actions[].triggers[].parameters.size` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | max: 1000000 |
| `actions[].triggers[].parameters.windowMs` |  | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested | min: 1<br>max: 60000 |
| `actions[].valueType` | The shape of the value this action produces, such as a button, a number, a direction vector, or an aimed world target; it decides which controls and triggers fit. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `boolean`, `float`, `vector1d`, `vector2d`, `vector3d`, `world_target`, `screen2d`, `trigger`, `composite` |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |
| `version` | Revision used for server-authoritative stale-binding rejection. | integer | yes | readonly | hidden | hidden | hidden | hidden | exposed | consumed | not_applicable | readonly/number | min: 1<br>max: 9007199254740991 |

Defaults:

```json
{
  "type": "InputProfileComponent",
  "version": 1,
  "actions": [
    {
      "key": "player.move",
      "label": "Move",
      "valueType": "vector2d",
      "contexts": [
        "gameplay"
      ],
      "triggers": [],
      "processors": [],
      "policy": {
        "possessionRequired": true,
        "requiresBinding": true,
        "allowedActorKinds": [],
        "valueBounds": {
          "maxMagnitude": 1,
          "integer": false,
          "enumValues": [],
          "requiredFields": []
        },
        "lagCompensationMode": "live",
        "effectSystem": "movement"
      },
      "prediction": {
        "mode": "client_predicted"
      },
      "bindings": [
        {
          "id": "binding.move.wasd",
          "sources": [
            {
              "device": "keyboard",
              "control": "KeyW",
              "event": "down",
              "modifiers": []
            },
            {
              "device": "keyboard",
              "control": "KeyA",
              "event": "down",
              "modifiers": []
            },
            {
              "device": "keyboard",
              "control": "KeyS",
              "event": "down",
              "modifiers": []
            },
            {
              "device": "keyboard",
              "control": "KeyD",
              "event": "down",
              "modifiers": []
            }
          ],
          "triggerOverrides": [],
          "processors": [],
          "priority": 0
        }
      ]
    }
  ]
}
```
## InstanceSetComponent

Thin placement/binding component for an aggregate of N packed instances (voxel chunk, foliage field, crowd, or destructible lot) backed by one canonical InstanceSet resource.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | E1 aggregate substrate: thin ref to an InstanceSet resource; render lowers to InstancedMesh per kind, physics to ONE collider per aggregate. |
| Default component key | `component.instance_set` |
| Authored contract version | v1 |
| Catalog contract ID | `component.instance_set.contract.v1` |
| Contract hash | `sha256:2c615e9153fbe7ff4ea45cf2287076c3fb23dc605bbb448b7d4b8023a98fb809` |
| Field contract hash | `sha256:fb6f88e1ecce213103ff23605943d42fe1a24e19564a641103bd6a605693b1c1` |
| Migration class | none |
| Capability packs | None |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/sceneGraphSync.ts:syncSceneEntities [web_client/spa/src/render/sceneGraphSync.ts:syncSceneEntities] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `enabled`, `instanceSetAssetId`, `layout` |
| Canonical field count | 5 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `enabled` | Show or hide the aggregate instance set. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `instanceSetAssetId` | InstanceSet ProjectAsset holding the packed instance data (grid voxel cells or scattered transforms) that backs render + collision. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `asset_id`<br>ref: asset id instanceSet |
| `layout` | Aggregate layout: 'grid' is a dense integer voxel lattice (native voxels collider); 'free' is scattered transforms (baked trimesh collider). Arms are mutually exclusive. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/union |  |
| `layout.kind` |  | string | yes | readonly | readonly | readonly | readonly | readonly | exposed | consumed | not_applicable | hidden: No normal inspector control is declared for this backend field. |  |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "InstanceSetComponent",
  "enabled": true,
  "layout": {
    "kind": "grid"
  }
}
```
## JointComponent

Physical constraint linking this rigid body to another: fixed weld, revolute hinge, prismatic slider, spherical ball socket, spring, or rope. Anchors and axis are in each body's LOCAL space; motors and limits exist only on revolute and prismatic joints. Author intent only - the runtime never writes joint state back.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Physics |
| Component palette visibility | default |
| Component palette notes | Fixed/revolute/prismatic/spherical/spring/rope; single owner references the connected body. |
| Default component key | `component.joint` |
| Authored contract version | v1 |
| Catalog contract ID | `component.joint.contract.v1` |
| Contract hash | `sha256:c1b6c85b1efc5281799240412c4ebadb2bfa9a653bf0ad626fb70f42bfaa1635` |
| Field contract hash | `sha256:b819efe75b308bd02675d3288a913e7b07ed8953219a37ff874589ee1733c49b` |
| Migration class | none |
| Capability packs | None |
| Runtime systems | None |
| Runtime consumer status | consumed |
| Renderer consumer status | not_applicable |
| Consumer status reason | read by server/src/modules/runtime/physics/componentSync.ts:syncJointState [server/src/modules/runtime/physics/componentSync.ts:syncJointState] |
| Replication policy | `public/medium` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `axis`, `collideConnected`, `connectedAnchor`, `connectedEntityId`, `jointType`, `limits`, `localAnchor`, `motor`, `restLength`, `springDamping`, `springStiffness`, `wakeConnectedOnBuild` |
| Canonical field count | 32 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `axis` | Hinge axis (revolute) or slide direction (prismatic), in this body's local space. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `axis.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `axis.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `axis.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `collideConnected` | Whether the two linked bodies still collide with each other. Off by default: jointed bodies interpenetrate at the anchor. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `connectedAnchor` | Attachment point on the connected body, in THAT body's local space. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `connectedAnchor.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `connectedAnchor.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `connectedAnchor.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `connectedEntityId` | The other rigid-body entity this joint attaches to. The joint lives on THIS entity only (single owner) and stays inert until connected. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `entity_id`<br>ref: entity id |
| `jointType` | Constraint kind: Fixed welds, Revolute hinges around the axis, Prismatic slides along it, Spherical ball-sockets, Spring holds a rest length, Rope caps the distance. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `fixed`, `revolute`, `prismatic`, `spherical`, `spring`, `rope` |
| `limits` | Travel bounds along the axis: radians for revolute, meters for prismatic. Only revolute and prismatic joints carry limits (Rapier unit joints); the schema rejects them elsewhere. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `limits.enabled` |  | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `limits.max` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `limits.min` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `localAnchor` | Attachment point on this body, in ITS local space. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `localAnchor.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `localAnchor.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `localAnchor.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `motor` | Drives the joint toward a target position or velocity. Only revolute and prismatic joints carry motors; the schema rejects them elsewhere. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `motor.damping` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `motor.enabled` |  | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `motor.mode` |  | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `position`, `velocity` |
| `motor.model` |  | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `acceleration`, `force` |
| `motor.stiffness` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `motor.targetPosition` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `motor.targetVelocity` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `restLength` | Spring rest length or rope maximum length, in meters. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `springDamping` | Velocity damping applied by the spring. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `springStiffness` | Spring constant; a spring joint requires stiffness above zero. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |
| `wakeConnectedOnBuild` | Wake both bodies when the joint is created or rebuilt. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |

Defaults:

```json
{
  "type": "JointComponent",
  "jointType": "fixed",
  "localAnchor": {
    "x": 0,
    "y": 0,
    "z": 0
  },
  "connectedAnchor": {
    "x": 0,
    "y": 0,
    "z": 0
  },
  "axis": {
    "x": 0,
    "y": 1,
    "z": 0
  },
  "limits": {
    "enabled": false,
    "min": 0,
    "max": 0
  },
  "motor": {
    "enabled": false,
    "model": "acceleration",
    "mode": "velocity",
    "targetPosition": 0,
    "targetVelocity": 0,
    "stiffness": 0,
    "damping": 0
  },
  "restLength": 1,
  "springStiffness": 0,
  "springDamping": 0,
  "collideConnected": false,
  "wakeConnectedOnBuild": true
}
```
## LightComponent

Real-time light source. Directional for sun-style, point for omni, spot for cones, area for windows or panels, ambient for global fill.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Atomic rendering primitive. |
| Default component key | `component.light` |
| Authored contract version | v3 |
| Catalog contract ID | `component.light.contract.v1` |
| Contract hash | `sha256:af985b713027438b28e22c9c83dfb005fd020e149bf40d28136bb23ca02b7bb8` |
| Field contract hash | `sha256:3579762710a82c6e39ee85f98f1395e1175dcb63640868e39a707aa4835e1d4a` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/light.ts:applyLight [web_client/spa/src/render/light.ts:applyLight] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `angleDeg`, `castShadow`, `color`, `decay`, `distance`, `enabled`, `height`, `intensity`, `mode`, `modulation`, `penumbra`, `radius`, `width` |
| Canonical field count | 18 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `angleDeg` | Width of the spot cone, in degrees. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 1<br>max: 180 |
| `castShadow` | Lets this light cast real-time shadows; it costs performance, so save it for key lights. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `color` | Color of the emitted light. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `decay` | How quickly brightness falls off with distance; 2 is physically natural. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `distance` | Maximum reach of the light; 0 means unlimited. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `enabled` | Whether the light contributes to the World right now. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `height` | Height of the rectangular panel, in world units. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01 |
| `intensity` | Brightness of the light; 0 turns it off. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `mode` | Kind of light: directional sun, point omni, spot cone, area panels, or ambient fill. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `directional`, `point`, `spot`, `area_rect`, `area_disc`, `ambient` |
| `modulation` | Deterministic time modulation. Strobe switches intensity hard on/off, Pulse breathes it smoothly, Flicker is seeded candle noise, Disco cycles the light's hue at the set rate; depth is how much of the intensity swings (for Disco, how saturated the cycling color is). | object | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `modulation.depth` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `modulation.mode` |  | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `none`, `strobe`, `pulse`, `flicker`, `disco` |
| `modulation.rateHz` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01<br>max: 60 |
| `modulation.seed` |  | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 1<br>max: 9007199254740991 |
| `penumbra` | Softness of the cone's edge, from 0 (hard) to 1 (fully soft). | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `radius` | Radius of the glowing disc, in world units. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01 |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |
| `width` | Width of the glowing panel, in world units. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01 |

Defaults:

```json
{
  "type": "LightComponent",
  "mode": "directional",
  "color": "#ffffff",
  "intensity": 1,
  "distance": 0,
  "decay": 2,
  "angleDeg": 30,
  "penumbra": 0.2,
  "width": 2,
  "height": 2,
  "radius": 1,
  "enabled": true,
  "castShadow": false
}
```
## LocalEnvironmentVolumeComponent

Bounded region that sparse-overrides the global environment (fog, ambient, aerial haze, exposure-compensation bias) with strict-priority blending toward the world default at its boundary.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Renderer environment-override primitive (ADR 0178); equal-priority overlaps are warned and resolve deterministically by entity id. |
| Default component key | `component.local_environment_volume` |
| Authored contract version | v1 |
| Catalog contract ID | `component.local_environment_volume.contract.v1` |
| Contract hash | `sha256:e52a30ed38046487a717d3aef6181e0531959a22c0d5280de169547e6d8212b3` |
| Field contract hash | `sha256:739bc63e9df9aec55cb400b99fcd67d10e8bba081349e5a69fc321b024ce0f04` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/environment/applyLocalEnvironmentVolumes.ts:volumeOverrideFields [web_client/spa/src/render/environment/applyLocalEnvironmentVolumes.ts:volumeOverrideFields] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `ambientIntensity`, `blendDistance`, `enabled`, `exposureCompensationBias`, `fogDensity`, `overrideAmbient`, `overrideExposureCompensation`, `overrideFog`, `priority`, `radius`, `shape`, `size` |
| Canonical field count | 16 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `ambientIntensity` | Local ambient intensity (used only when Override ambient is on). | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 10 |
| `blendDistance` | World-units over which each override fades to the global default approaching the boundary. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `enabled` | Whether this volume contributes an environment override. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `exposureCompensationBias` | Exposure-compensation bias in stops (used only when Override exposure compensation is on, and only under auto-exposure). | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: -4<br>max: 4 |
| `fogDensity` | Local fog density (used only when Override fog is on, and only on exp2/no-fog worlds - linear fog has no density channel). | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `overrideAmbient` | Author the ambient/skylight intensity inside this volume. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `overrideExposureCompensation` | Push an exposure-compensation stop bias into the meter inside this volume (never a second tonemapper). Reaches pixels only under auto-exposure; manual-exposure worlds ignore it. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `overrideFog` | Author the fog density inside this volume. Applies on worlds using exp2 or no global fog; linear-fog worlds have no density channel, so this override changes nothing there. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `priority` | Strict integer priority. The highest-priority containing volume wins. Equal-priority overlaps trigger an Inspector warning and resolve deterministically by stable volume id; use distinct priorities for an explicit order. | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: -9007199254740991<br>max: 9007199254740991 |
| `radius` | Reach of a sphere volume, in world units. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.1 |
| `shape` | Volume of influence: a sphere or a box around the entity. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `sphere`, `box` |
| `size` | Extents of a box volume per axis, in world units. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `size.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.1 |
| `size.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.1 |
| `size.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.1 |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "LocalEnvironmentVolumeComponent",
  "enabled": true,
  "shape": "sphere",
  "radius": 8,
  "size": {
    "x": 8,
    "y": 8,
    "z": 8
  },
  "priority": 0,
  "blendDistance": 4,
  "overrideFog": false,
  "fogDensity": 0.02,
  "overrideAmbient": false,
  "ambientIntensity": 1,
  "overrideExposureCompensation": false,
  "exposureCompensationBias": 0
}
```
## MaterialInstanceOverrideComponent

Optional per-entity material parameters layered over the referenced material asset at runtime.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Atomic renderer state. |
| Default component key | `component.material_override` |
| Authored contract version | v2 |
| Catalog contract ID | `component.material_override.contract.v1` |
| Contract hash | `sha256:47cca9049f8fce13282e7ab6f87316b3f61da080232038566b21b18a91aec63e` |
| Field contract hash | `sha256:805f5e6d589b43fb9fd9184d5618836da10020e2708f8f32208eb465a890e126` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | the server resolves atlasSelection to a uint texture-array layer via resolveInstanceAtlasLayer and carries it on a per-instance lane; it is intentionally NOT folded into the material payload so per-tile entities keep one shared material and one instancing batch [packages/protocol/src/instanceAppearance.ts:resolveInstanceAtlasLayer] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `atlasSelection`, `baseColorTint`, `emissiveIntensityMultiplier`, `opacityMultiplier`, `overlayStageProgress` |
| Canonical field count | 10 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `atlasSelection` | Per-instance texture tile from the material's declared atlas; does not fork the shared material or its instancing batch. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/union |  |
| `atlasSelection.faces` |  | object | yes | readonly | readonly | readonly | readonly | readonly | exposed | projected | consumed | hidden: No normal inspector control is declared for this backend field. |  |
| `atlasSelection.kind` |  | string | yes | readonly | readonly | readonly | readonly | readonly | exposed | projected | consumed | hidden: No normal inspector control is declared for this backend field. |  |
| `atlasSelection.layer` |  | integer | yes | readonly | readonly | readonly | readonly | readonly | exposed | projected | consumed | hidden: No normal inspector control is declared for this backend field. | min: 0<br>max: 255 |
| `atlasSelection.region` |  | string | yes | readonly | readonly | readonly | readonly | readonly | exposed | projected | consumed | hidden: No normal inspector control is declared for this backend field. | minLength: 1<br>maxLength: 64 |
| `baseColorTint` | Tint multiplied into the material's base color for just this entity. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `emissiveIntensityMultiplier` | Scales how strongly this entity's material glows; 1 keeps it unchanged. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 20 |
| `opacityMultiplier` | Scales the material's opacity for this entity; 1 keeps it unchanged. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 4 |
| `overlayStageProgress` | Normalized 0–1 progress for this instance when its material declares a staged overlay; does not fork the shared material or instancing batch. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "MaterialInstanceOverrideComponent",
  "opacityMultiplier": 1,
  "emissiveIntensityMultiplier": 1
}
```
## NameplateComponent

The name shown over a Character, and who may read it: publicLabel is visible to every player, teamLabel only to that player's own team. On a Character a player controls, the server writes the resolved account identity into the field the character policy picks and keeps it correct, so a name a client typed is never shown. You never need to add it yourself.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Gameplay |
| Component palette visibility | default |
| Component palette notes | On a character a player controls, the game adds a nameplate and keeps the name correct itself. publicLabel shows to every player; teamLabel shows only to that player's own team. |
| Default component key | `component.nameplate` |
| Authored contract version | v1 |
| Catalog contract ID | `component.nameplate.contract.v1` |
| Contract hash | `sha256:e186071b3a946ccb746d3fb09b8d62c1986d38c0aaca05c9f1ccb639291dfe10` |
| Field contract hash | `sha256:7a376c9cf9cea66069466dbf04cd19865879ca5f3d06c1fbfa9fca32e51b0db9` |
| Migration class | none |
| Capability packs | None |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by server/src/modules/runtime/runtimeCharacterSystem.ts:resolveNameplateForSeat [server/src/modules/runtime/runtimeCharacterSystem.ts:resolveNameplateForSeat] |
| Replication policy | `public/low`, `teamLabel: team` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `publicLabel`, `teamLabel` |
| Canonical field count | 3 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `publicLabel` | The name every player may see. On a player-controlled Character the server overwrites it with the resolved account identity. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | maxLength: 160 |
| `teamLabel` | The name only the player's own team sees, when the character policy shows nameplates to a team. The server writes and maintains it. | string | no | hidden | hidden | hidden | hidden | hidden | exposed | projected | consumed | hidden: Server-derived field. | maxLength: 160 |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "NameplateComponent"
}
```
## NavAgentComponent

Path-following navigation agent: steers an entity to a destination (or a followed target) across the world navmesh via the existing mover.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Physics |
| Component palette visibility | default |
| Component palette notes | Runtime navigation behavior; the nav system steers the agent via the existing mover (no second mover) over the recast navmesh resource (no navmesh-as-component). |
| Default component key | `component.nav_agent` |
| Authored contract version | v1 |
| Catalog contract ID | `component.nav_agent.contract.v1` |
| Contract hash | `sha256:5ce457fafb4313765ec94214e6c44a6c819e9c891c68c059128d82f263ec512e` |
| Field contract hash | `sha256:25c451e692afa5f4b2fdafd2afcc7558e7569a37a8802316bf74637147ef8b5b` |
| Migration class | none |
| Capability packs | `world.v1` |
| Runtime systems | None |
| Runtime consumer status | consumed |
| Renderer consumer status | not_applicable |
| Consumer status reason | read by server/src/modules/runtime/runtimeSystems.ts:applyNavAgentSweep [server/src/modules/runtime/runtimeSystems.ts:applyNavAgentSweep] |
| Replication policy | `public/medium` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `arrivalRadius`, `destination`, `enabled`, `speed`, `targetEntityKey` |
| Canonical field count | 9 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `arrivalRadius` | Distance at which the agent stops. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0<br>max: 1000 |
| `destination` | World-space point the navigation agent moves toward when no target entity is set. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/vec3 |  |
| `destination.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested |  |
| `destination.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested |  |
| `destination.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/nested |  |
| `enabled` | Run navigation for this agent. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `speed` | Movement speed in world units per second. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0<br>max: 1000 |
| `targetEntityKey` | Optional: follow this entity instead of the fixed destination. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/reference | minLength: 1<br>maxLength: 160<br>semantic: `project_graph_key`<br>ref: entity key |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "NavAgentComponent",
  "enabled": true,
  "destination": {
    "x": 0,
    "y": 0,
    "z": 0
  },
  "speed": 4,
  "arrivalRadius": 0.5
}
```
## NetworkProfileComponent

How this entity reaches remote clients: whether they receive it at all, and how they present its replicated motion. Owner-only visibility withholds the whole entity from everyone but its owner. Responsive hugs the newest server sample and never bridges gaps (teleporting projectiles, snappy pickups); Smooth doubles the interpolation buffer for fluid slow movers (platforms, doors); Default is the tick-derived baseline.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Networking |
| Component palette visibility | default |
| Component palette notes | Netcode presentation intent for replicated motion; the runtime interpolation buffer reads this profile (no second mover). |
| Default component key | `component.network_profile` |
| Authored contract version | v3 |
| Catalog contract ID | `component.network_profile.contract.v1` |
| Contract hash | `sha256:c9c73cb72004546f308366503984e8bb6bc52f70e16f693184cdf92eabb2f2b3` |
| Field contract hash | `sha256:34bf86d9c254da9f7cb6e64de5825e5bed2f611cd4d0180526f124ff72fbc824` |
| Migration class | none |
| Capability packs | `network.v1` |
| Runtime systems | None |
| Runtime consumer status | consumed |
| Renderer consumer status | not_applicable |
| Consumer status reason | filterRuntimeEntitiesForPlayerInterest reads the relevance selection when composing each player's interest set, so an entity withheld from a player never enters that player's snapshot at all [server/src/modules/runtime/interestGraph.ts:filterRuntimeEntitiesForPlayerInterest] |
| Replication policy | `public/high/reliable` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `alwaysRelevant`, `hidden`, `interestRadius`, `interpolation`, `ownerOnly`, `ownership` |
| Canonical field count | 7 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `alwaysRelevant` | Deliver this entity to every player regardless of distance. Objectives, boundaries, and world-scale landmarks that must never pop in. | boolean | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `hidden` | Withhold this entity from every player, including its owner. Server-side props, spawn markers, and trigger volumes that exist only for simulation. | boolean | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `interestRadius` | Distance within which players receive this entity, overriding the room default. Larger than the default for things seen from far away; smaller for dense local detail. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `interpolation` | Responsive: no gap-bridge extrapolation (never invents motion). Smooth: doubled buffer for fluid slow movers. Default: tick-derived baseline. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `default`, `responsive`, `smooth` |
| `ownerOnly` | Withhold this entity from every player except its owner. The owner is the player possessing it or an ancestor of it, or the recorded owner of it or an ancestor, so a held item parented to a possessed Character is owned by that Character's player. Enforced server-side: a non-owner never receives the entity at all. A camera-space renderable is owner-only whether or not this is set. | boolean | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `ownership` | sunset. Representation-only selection: authority is always derived from physics body type (a dynamic Rigidbody is physics-owned, everything else transform-owned), so server and owner_client have no effect. On its way out; leave it at auto. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | inert | not_applicable | editable/string | enum: `auto`, `server`, `owner_client` |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "NetworkProfileComponent"
}
```
## OwnershipComponent

Records which player owns this entity, so the claim survives them leaving and rejoining. The server keeps it in step with every scripted ownership transfer. Put it on anything a player should keep: a placed block, a claimed vehicle, a base. Something merely carried is parented under the carrier's Character instead.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Gameplay |
| Component palette visibility | default |
| Component palette notes | Records the owning player so the claim survives them leaving and rejoining. The game keeps it in step with every scripted ownership transfer. Carrying something is parenting it, which already counts as held. |
| Default component key | `component.ownership` |
| Authored contract version | v1 |
| Catalog contract ID | `component.ownership.contract.v1` |
| Contract hash | `sha256:4bbbaa9dc325cb8d8e19047274ea9dbac7c4eb5b391fae6dfe4ccd64431c7dce` |
| Field contract hash | `sha256:56e511a38c6872dc1f459480a271194c16fa512b832d9719bed2bd2cdda3a651` |
| Migration class | none |
| Capability packs | None |
| Runtime systems | None |
| Runtime consumer status | consumed |
| Renderer consumer status | not_applicable |
| Consumer status reason | read by server/src/modules/runtime/audienceResolver.ts:runtimeEntityOwner [server/src/modules/runtime/audienceResolver.ts:runtimeEntityOwner] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `acquiredAt`, `ownerProfileId` |
| Canonical field count | 3 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `acquiredAt` | When the current owner acquired this entity; the server writes it on every ownership transfer. | string | no | hidden | hidden | hidden | hidden | hidden | exposed | consumed | not_applicable | hidden: Server-derived field. | minLength: 1<br>maxLength: 64 |
| `ownerProfileId` | Shows the player who owns this entity right now; empty means unowned. The server keeps it in step with the canonical owner, which scripts change through entity ownership transfers. | string | no | hidden | hidden | hidden | hidden | hidden | exposed | consumed | not_applicable | hidden: Server-derived field. | minLength: 1<br>maxLength: 200<br>semantic: `runtime_profile_id` |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "OwnershipComponent"
}
```
## ParticleEmitterComponent

Budgeted billboard VFX emitter: emission mode (continuous stream or one-shot burst), particle size and color ramps, lifetime, velocity, gravity, drag, spawn shape, blend/sort policy, texture, and plane collision. Continuous mode emits emissionRate particles/second and loops (age wraps at lifetime). Burst mode emits burstCount particles once at emitter birth - the same machinery a scripted ctx.fx.particleBurst rides. Particles simulate in the entity's LOCAL space (they move with the entity).

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Default by creator expectation; particles are built-in in comparable creator engines. |
| Default component key | `component.particle_emitter` |
| Authored contract version | v4 |
| Catalog contract ID | `component.particle_emitter.contract.v1` |
| Contract hash | `sha256:d11293cc720e9e699cd63501f6732a32782e1a8435568119840dea23f9b49650` |
| Field contract hash | `sha256:67be2e100757d415ee729cc753d2b586cd0f683389ebc404827a6f0c9ddddcb9` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/editorSceneProjection.ts:editorSceneEntityToRendererEntity [web_client/spa/src/render/editorSceneProjection.ts:editorSceneEntityToRendererEntity] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `blendMode`, `budgetCap`, `burstCount`, `collision`, `color`, `depthWrite`, `drag`, `emissionMode`, `emissionRate`, `enabled`, `endColor`, `endSize`, `gravity`, `lifetimeSeconds`, `maxParticles`, `opacity`, `particleSize`, `schemaVersion`, `seed`, `shape`, `simulationPath`, `sortMode`, `startColor`, `startSize`, `textureAssetId`, `velocity` |
| Canonical field count | 61 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `blendMode` | How particles mix with what is behind them: alpha, additive, multiply, or premultiplied. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `alpha`, `additive`, `multiply`, `premultiplied` |
| `budgetCap` | Hard quality-budget ceiling on particles for this emitter. | integer | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 1<br>max: 9007199254740991 |
| `burstCount` | Particles emitted in one puff (Burst mode). | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 1<br>max: 512 |
| `collision` | Lets particles hit one authored plane and stop. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `collision.enabled` |  | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `collision.planeNormal` |  | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `collision.planeNormal.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `collision.planeNormal.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `collision.planeNormal.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `collision.planeOffset` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number |  |
| `color` | Base tint for all particles; Start/End color override the ramp when set. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `depthWrite` | Whether particles write to the depth buffer; usually off for soft additive effects. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `drag` | Air resistance slowing particles over time. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 16 |
| `emissionMode` | Continuous streams particles at the emission rate and loops; Burst emits a one-shot puff of Burst count particles once, then idles. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `continuous`, `burst` |
| `emissionRate` | Particles spawned per second (Continuous mode). | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `enabled` | Master switch for the whole effect. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `endColor` | Tint at the end of each particle's life; drives the color ramp. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `endSize` | Particle size at death; drives the size-over-life ramp. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01<br>max: 10 |
| `gravity` | Constant pull applied to every particle. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `gravity.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `gravity.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `gravity.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `lifetimeSeconds` | Seconds each particle lives. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01 |
| `maxParticles` | Cap on how many particles are alive at once. | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 9007199254740991 |
| `opacity` | Overall transparency for the effect. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `particleSize` | Uniform particle size in world units; Start/End size override the ends when set. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01<br>max: 10 |
| `schemaVersion` | Format version of this emitter's data; v2 adds one-shot burst mode. | number | yes | readonly | hidden | hidden | hidden | hidden | exposed | projected | not_applicable | readonly/number | min: 1<br>max: 2 |
| `seed` | Random seed; keep the same number to replay the exact same effect. | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: -9007199254740991<br>max: 9007199254740991 |
| `shape` | Where particles spawn: the emitter volume's type, size, and spawn mode. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `shape.angleDeg` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 180 |
| `shape.arcDeg` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 360 |
| `shape.edgeEnd` |  | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `shape.edgeEnd.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `shape.edgeEnd.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `shape.edgeEnd.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `shape.edgeStart` |  | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `shape.edgeStart.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `shape.edgeStart.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `shape.edgeStart.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `shape.radius` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `shape.size` |  | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `shape.size.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `shape.size.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `shape.size.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `shape.spawnMode` |  | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `center`, `surface`, `volume` |
| `shape.type` |  | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `point`, `sphere`, `box`, `cone`, `disc`, `edge` |
| `simulationPath` | Where the simulation runs: auto picks CPU or GPU for you. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `auto`, `cpu`, `gpu` |
| `sortMode` | Draw order for particles; camera distance sorts correctly through transparency. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `none`, `cameraDistance` |
| `startColor` | Tint at the start of each particle's life; drives the color ramp. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `startSize` | Particle size at birth; drives the size-over-life ramp. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01<br>max: 10 |
| `textureAssetId` | Image each particle displays. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `asset_id`<br>ref: asset id texture |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |
| `velocity` | Random starting velocity range (min and max per axis) for new particles. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `velocity.max` |  | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `velocity.max.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `velocity.max.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `velocity.max.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `velocity.min` |  | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `velocity.min.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `velocity.min.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `velocity.min.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |

Defaults:

```json
{
  "type": "ParticleEmitterComponent",
  "schemaVersion": 2,
  "enabled": true,
  "maxParticles": 64,
  "particleSize": 0.08,
  "color": "#ffffff",
  "opacity": 1,
  "lifetimeSeconds": 1,
  "emissionRate": 16,
  "emissionMode": "continuous",
  "burstCount": 32,
  "seed": 1,
  "velocity": {
    "min": {
      "x": -0.1,
      "y": 0.3,
      "z": -0.1
    },
    "max": {
      "x": 0.1,
      "y": 1.2,
      "z": 0.1
    }
  },
  "gravity": {
    "x": 0,
    "y": -0.4,
    "z": 0
  },
  "drag": 0,
  "sortMode": "none",
  "blendMode": "alpha",
  "simulationPath": "auto",
  "depthWrite": false,
  "shape": {
    "type": "box",
    "spawnMode": "volume",
    "size": {
      "x": 1,
      "y": 1,
      "z": 1
    },
    "radius": 0.5,
    "angleDeg": 30,
    "arcDeg": 360,
    "edgeStart": {
      "x": -0.5,
      "y": 0,
      "z": 0
    },
    "edgeEnd": {
      "x": 0.5,
      "y": 0,
      "z": 0
    }
  },
  "collision": {
    "enabled": false,
    "planeNormal": {
      "x": 0,
      "y": 1,
      "z": 0
    },
    "planeOffset": 0
  }
}
```
## PixelSurfaceComponent

Script-driven, tick-cadence pixel surface for authored dynamic imagery: minimap/radar, fog-of-war, drawing and painting, dashboards, animated terminals, heatmaps. The owning script draws it with bounded ctx.surface commands; the RGBA buffer is client-local presentation state and is never replicated, persisted, or serialized. Bind it as a camera_quad (fills the frame, a 2D game inside the normal play surface) or a world_quad (a screen in the world).

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Authored pixel-surface primitive; scripts draw a bounded client-local RGBA buffer presented as a camera or world quad. |
| Default component key | `component.pixel_surface` |
| Authored contract version | v1 |
| Catalog contract ID | `component.pixel_surface.contract.v1` |
| Contract hash | `sha256:39d52e59c611641c6487186677c81bbfc7994fe758963333b5ab265bca0c4728` |
| Field contract hash | `sha256:593e3cabdafcebb2ebb8e30d9887a5db55a5857259587c0e6fcf0ea29d883d79` |
| Migration class | none |
| Capability packs | None |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/editorSceneProjection.ts:pixelSurfaceFromArtifact [web_client/spa/src/render/editorSceneProjection.ts:pixelSurfaceFromArtifact] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `binding`, `colorSpace`, `doubleSided`, `enabled`, `filter`, `logicalHeight`, `logicalWidth`, `opacity`, `programScriptId`, `seedAssetId` |
| Canonical field count | 11 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `binding` | camera_quad composes on the camera-space layer and can fill the frame (a 2D game inside the normal play surface); world_quad is a screen placed in the world via the entity's transform. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `camera_quad`, `world_quad` |
| `colorSpace` | display presents author-referred bytes exactly (pair with identity tone mapping for 1:1 output); scene feeds linear values into the lit pipeline like any texture. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `display`, `scene` |
| `doubleSided` | Draw both faces of the surface. Only applies to a world_quad. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `enabled` | Show or hide the surface. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `filter` | Texture sampling: nearest keeps hard pixels (the retro look); linear smooths when scaled. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `nearest`, `linear` |
| `logicalHeight` | Surface height in pixels. Tier-capped (low 128, medium 256, high 512, ultra 1024); exceeding the active tier's budget is a typed refusal at apply time. | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 1<br>max: 1024 |
| `logicalWidth` | Surface width in pixels. Tier-capped (low 128, medium 256, high 512, ultra 1024); exceeding the active tier's budget is a typed refusal at apply time. | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 1<br>max: 1024 |
| `opacity` | Surface opacity; 0 is fully transparent, 1 is fully opaque. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `programScriptId` | Optional project script whose program drives this surface's contents. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `script_id`<br>ref: script id |
| `seedAssetId` | Optional image asset that fills the surface with its initial contents before the script draws. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `asset_id`<br>ref: asset id texture |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "PixelSurfaceComponent",
  "enabled": true,
  "logicalWidth": 256,
  "logicalHeight": 256,
  "colorSpace": "display",
  "filter": "nearest",
  "binding": "world_quad",
  "opacity": 1,
  "doubleSided": false
}
```
## ReflectionProbeComponent

Local reflection probe used by the renderer to select nearby environment lighting for PBR objects.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Renderer presentation primitive with explicit runtime/renderer projection status. |
| Default component key | `component.reflection_probe` |
| Authored contract version | v1 |
| Catalog contract ID | `component.reflection_probe.contract.v1` |
| Contract hash | `sha256:cb7b4952d5d5e6dc2900d0f7d982bf0c47c9d60b205bbf46fc949e13f5b3ca27` |
| Field contract hash | `sha256:f97f8b34fc3bbee57492621be1221eae77027bea44a0480cf5d8100068732c7d` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/editorSceneProjection.ts:reflectionProbeFromArtifact [web_client/spa/src/render/editorSceneProjection.ts:reflectionProbeFromArtifact] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `cubemapAssetId`, `enabled`, `intensity`, `radius`, `shape`, `size` |
| Canonical field count | 10 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `cubemapAssetId` | Texture asset used as this probe's reflection image. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `asset_id`<br>ref: asset id texture |
| `enabled` | Whether nearby surfaces may use this probe for reflections. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `intensity` | Strength of the reflections this probe contributes. Full 0-10 range applies to authored-cubemap probes; probes without a cubemap use live runtime capture, which clamps effective intensity to 0.35 to prevent capture feedback blowout. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 10 |
| `radius` | Reach of a sphere probe, in world units. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.1 |
| `shape` | Volume of influence: a sphere or a box around the entity. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `sphere`, `box` |
| `size` | Extents of a box probe per axis, in world units. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `size.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.1 |
| `size.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.1 |
| `size.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.1 |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "ReflectionProbeComponent",
  "enabled": true,
  "shape": "sphere",
  "radius": 8,
  "size": {
    "x": 8,
    "y": 8,
    "z": 8
  },
  "intensity": 1
}
```
## RenderableComponent

Presentation primitive used by the shared client renderer.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Atomic rendering primitive. |
| Default component key | `component.renderable` |
| Authored contract version | v3 |
| Catalog contract ID | `component.renderable.contract.v1` |
| Contract hash | `sha256:158e18aecaa93e56f94f1e171a2bd841cf7a99cd55eb3b063f916ef59dab454f` |
| Field contract hash | `sha256:da7f71852c339dd808c7a89dbe48e0f750daaf2af8751370232d164bfdf8e1ec` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/editorSceneProjection.ts:editorSceneEntityToRendererEntity [web_client/spa/src/render/editorSceneProjection.ts:editorSceneEntityToRendererEntity] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `assetId`, `bounds`, `fadeOutDistance`, `lightingMobility`, `materialRef`, `materialSlots`, `maxDrawDistance`, `renderSpace`, `shape`, `visible` |
| Canonical field count | 22 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `assetId` | The imported model drawn when Shape is "Mesh", referenced by ProjectAsset UUID. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `asset_id`<br>ref: asset id model |
| `bounds` | Optional renderer bounds override with center and size vectors. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `bounds.center` |  | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `bounds.center.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `bounds.center.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `bounds.center.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `bounds.size` |  | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `bounds.size.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `bounds.size.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `bounds.size.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `fadeOutDistance` | Optional distance band used to fade this visual before max draw distance. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 100000 |
| `lightingMobility` | Auto bakes world-space surfaces with a static rigid body, or with no rigid body and no enabled character controller. Movable uses probe lighting. Static explicitly opts into a fixed bake even with moving physics; moving it later makes that bake stale. Camera-space viewmodels are always excluded from lightmaps. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `auto`, `static`, `movable` |
| `materialRef` | Explicit whole-renderable material override. For imported models this replaces every embedded material slot. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `material_id`<br>ref: asset id material |
| `materialSlots` | Sparse per-slot material overrides for imported model surfaces. Missing slots preserve embedded model materials. | array | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/array |  |
| `materialSlots[]` | Sparse per-slot material overrides for imported model surfaces. Missing slots preserve embedded model materials. | object | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/array |  |
| `materialSlots[].materialRef` | Material ProjectAsset UUID applied only to this slot. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `material_id`<br>ref: asset id material |
| `materialSlots[].slot` | Stable slot name when available; numeric slot index fallback. | string\|integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string |  |
| `maxDrawDistance` | Optional distance after which this visual is not drawn. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01<br>max: 100000 |
| `renderSpace` | World uses the normal scene transform. Camera treats this entity's Transform as local to the active presentation camera for first-person arms or weapons. Camera-space visuals remain depth-tested and can be clipped by world geometry; use Camera only for viewmodels. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `world`, `camera` |
| `shape` | Geometry drawn for this renderable. Pick a primitive, or "Mesh" to draw an imported Model asset, or "Billboard" for a camera-facing sprite. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `box`, `sphere`, `disc`, `capsule`, `billboard`, `mesh`, `plane` |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |
| `visible` | Show or hide this entity without removing it from the World. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |

Defaults:

```json
{
  "type": "RenderableComponent",
  "visible": true,
  "renderSpace": "world",
  "lightingMobility": "auto",
  "shape": "sphere"
}
```
## RigidBodyComponent

Opts an entity into authoritative Rapier simulation; runtime physics owns dynamic Transform and Velocity readback.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Physics |
| Component palette visibility | default |
| Component palette notes | Atomic physics primitive. |
| Default component key | `component.rigid_body` |
| Authored contract version | v1 |
| Catalog contract ID | `component.rigid_body.contract.v1` |
| Contract hash | `sha256:6f0abcbfeb0df5ffb1da816491653c8cc0b846d38546decd58e110d6557bfb07` |
| Field contract hash | `sha256:5692bdf3c853dcd1ecad983913709c1bd6425b54f91181036e2d7e1fc52db416` |
| Migration class | none |
| Capability packs | `physics.v1` |
| Runtime systems | `runtime.physicsSystem`, `runtime.collisionSystem` |
| Runtime consumer status | consumed |
| Renderer consumer status | not_applicable |
| Consumer status reason | read by server/src/modules/runtime/physics/componentSync.ts:syncRigidBodyState [server/src/modules/runtime/physics/componentSync.ts:syncRigidBodyState] |
| Replication policy | `public/medium` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `angularDamping`, `bodyType`, `ccdEnabled`, `gravityScale`, `linearDamping`, `lockRotations`, `lockTranslations`, `mass` |
| Canonical field count | 15 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `angularDamping` | Drag on spin; higher values stop rotation sooner. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `bodyType` | Dynamic falls and reacts to pushes, kinematic is moved by authored/runtime intent, static never moves but can be queried and contacted. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | enum: `dynamic`, `kinematic`, `static` |
| `ccdEnabled` | Extra-accurate collision for fast movers so they cannot tunnel through thin walls. Leave unset for automatic (on for dynamic bodies, off for kinematic/static); set explicitly to override. | boolean | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `gravityScale` | How strongly gravity pulls this body; 0 floats, 1 is normal. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number |  |
| `linearDamping` | Drag on movement; higher values slow the body down sooner. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0 |
| `lockRotations` | Freeze spin around chosen axes; lock X and Z to keep characters upright. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `lockRotations.x` |  | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `lockRotations.y` |  | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `lockRotations.z` |  | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `lockTranslations` | Freeze movement along chosen axes so physics cannot push the body that way. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/object |  |
| `lockTranslations.x` |  | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `lockTranslations.y` |  | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `lockTranslations.z` |  | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `mass` | Heaviness of the body; heavier bodies are harder to push around. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0.001 |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "RigidBodyComponent",
  "bodyType": "dynamic",
  "mass": 1,
  "linearDamping": 0,
  "angularDamping": 0,
  "gravityScale": 1,
  "lockTranslations": {
    "x": false,
    "y": false,
    "z": false
  },
  "lockRotations": {
    "x": false,
    "y": false,
    "z": false
  }
}
```
## ScriptComponent

Attaches a sandboxed project script asset to an entity.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Scripting |
| Component palette visibility | default |
| Component palette notes | Atomic behavior attachment. |
| Default component key | `component.script` |
| Authored contract version | v1 |
| Catalog contract ID | `component.script.contract.v1` |
| Contract hash | `sha256:73f96b2494263088890c9ae67f1ac0e8ef3a804b13c5244b1a248ab49ba7043c` |
| Field contract hash | `sha256:4f8912d21edadee52b72715cb630b05a129b4efddf95dbda587cb82029e7ed26` |
| Migration class | none |
| Capability packs | `script.v1` |
| Runtime systems | `runtime.scriptSystem`, `runtime.componentPatchSystem`, `runtime.observerCommandSystem` |
| Runtime consumer status | consumed |
| Renderer consumer status | not_applicable |
| Consumer status reason | read by server/src/modules/runtime/runtimeSystems.ts:scriptForEntity [server/src/modules/runtime/runtimeSystems.ts:scriptForEntity] |
| Replication policy | `server/low` |
| Authorization | Server-owned script attachment; runtime reads it and does not replicate it publicly. |
| Inspector fields | `config`, `enabled`, `scriptId` |
| Canonical field count | 4 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `config` | Values handed to the script; variables the script exposes appear here as editable fields. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/map |  |
| `enabled` | Master switch; the script only receives events while on. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `scriptId` | Project script to run on this entity. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `script_id`<br>ref: script id |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "ScriptComponent",
  "enabled": true,
  "config": {}
}
```
## SpatialCaptureComponent

Placed imported spatial capture or splat asset, projected to the renderer without becoming mesh geometry.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Semantic placement primitive for imported captures; renderability is separate from playability proof. |
| Default component key | `component.spatial_capture` |
| Authored contract version | v3 |
| Catalog contract ID | `component.spatial_capture.contract.v1` |
| Contract hash | `sha256:6dda4636c726ec461894c192efb782ef332fd73d69b3eb29c99641c6bff16b83` |
| Field contract hash | `sha256:7b11f5b24a8349e46af395da05945b74d1ce32ea681e5c38c15c0fedb07de8ed` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/splatHandle.ts:attachLoadedSplat [web_client/spa/src/render/splatHandle.ts:attachLoadedSplat] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `assetId`, `colorTint`, `fadeOutDistance`, `maxDrawDistance`, `opacity`, `pointSize`, `sourceVariantKey`, `visible` |
| Canonical field count | 9 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `assetId` | Pick the imported capture asset to place; it must carry rendering.spatialCapture data. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `asset_id`<br>ref: asset id model\|raw |
| `colorTint` | Optional renderer-owned tint; it is not source asset truth. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `fadeOutDistance` | Optional distance range used by renderer fade-out. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 100000 |
| `maxDrawDistance` | Optional renderer culling distance for this placed capture. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.01<br>max: 100000 |
| `opacity` | Renderer-owned capture opacity between 0 and 1. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `pointSize` | Renderer-owned display size for native or degraded point/splat presentation. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.001<br>max: 1 |
| `sourceVariantKey` | Asset variant key used by renderer loading; defaults to source. | string | yes | readonly | hidden | hidden | hidden | hidden | exposed | projected | consumed | readonly/string | minLength: 1<br>maxLength: 120<br>semantic: `spatial_capture_facet_key` |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |
| `visible` | Show or hide this placed spatial capture without removing the entity. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |

Defaults:

```json
{
  "type": "SpatialCaptureComponent",
  "sourceVariantKey": "source",
  "visible": true,
  "pointSize": 0.06,
  "opacity": 0.92
}
```
## SpawnPointComponent

A placed marker where the game puts a player's Character on join, on respawn, and whenever the character policy needs a location. Limit it to one team or one reserved player slot, set a priority, and give it a spread radius so simultaneous spawns do not overlap. Without one, characters appear at the world origin and the pre-Play check calls it out.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Gameplay |
| Component palette visibility | default |
| Component palette notes | Where players appear on join and respawn. The game's character policy names a spawn tag, and only points carrying that tag are eligible, then narrowed to the matching team or reserved player slot and ordered by priority. |
| Default component key | `component.spawn_point` |
| Authored contract version | v1 |
| Catalog contract ID | `component.spawn_point.contract.v1` |
| Contract hash | `sha256:f218bb1a85b34eeed55a38acf46611191ebe17acb3dd5c3058afcb78e7397574` |
| Field contract hash | `sha256:85764a697875efb9cfcb65467e75e8dd941fc7d43c8c159ed8ef4278ef5b49e8` |
| Migration class | none |
| Capability packs | None |
| Runtime systems | None |
| Runtime consumer status | consumed |
| Renderer consumer status | not_applicable |
| Consumer status reason | read by server/src/modules/runtime/spawnPointSelection.ts:selectRuntimeSpawnPoint [server/src/modules/runtime/spawnPointSelection.ts:selectRuntimeSpawnPoint] |
| Replication policy | `server/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `priority`, `radius`, `seatKey`, `teamKey` |
| Canonical field count | 5 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `priority` | Higher priority points are tried first; ties fall to the character policy's spawn selector. | integer | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: -1000<br>max: 1000 |
| `radius` | Distance in meters around the point that counts as occupied while a Character stands in it; simultaneous spawns pick a free point when one exists. | number | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0<br>max: 100 |
| `seatKey` | Only this reserved player slot spawns here; seat keys come from the game's multiplayer settings. Leave empty for any seat. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | minLength: 1<br>maxLength: 160<br>semantic: `runtime_seat_key`<br>options: the game's reserved player slots (from the game's multiplayer settings) |
| `teamKey` | Only players on this team spawn here; team keys come from the game's multiplayer settings. Leave empty for any team. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | minLength: 1<br>maxLength: 160<br>semantic: `runtime_team_key`<br>options: the game's teams (from the game's multiplayer settings) |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "SpawnPointComponent",
  "priority": 0,
  "radius": 1
}
```
## TerrainComponent

Thin placement/binding component for a canonical terrain resource.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Renderer presentation component; this contract covers existing metadata/projection without adding terrain feature depth. |
| Default component key | `component.terrain` |
| Authored contract version | v1 |
| Catalog contract ID | `component.terrain.contract.v1` |
| Contract hash | `sha256:b70f4cccf980c0af9bd16b92574b3e5be5112c360a8e05b8b605f1fc1414942b` |
| Field contract hash | `sha256:4c929e365fb8d90cda470c7ff2f2605d6066daad95f94226964d529e1ad4558e` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/presentationTerrain.ts:terrainLodGeometry [web_client/spa/src/render/presentationTerrain.ts:terrainLodGeometry] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `enabled`, `terrainAssetId` |
| Canonical field count | 3 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `enabled` | Show or hide the terrain placement. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `terrainAssetId` | Terrain ProjectAsset backing render, collision, navigation and query behavior. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `asset_id`<br>ref: asset id terrain |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "TerrainComponent",
  "enabled": true
}
```
## TextRenderableComponent

Budgeted world-space text primitive for floating values, item labels, signs, and quest markers anchored to entities. Player names over Characters come from Nameplate instead.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Renderer presentation component: canonical MSDF text (shared per-family atlas, resolution-independent at any distance) with render-budget proof. |
| Default component key | `component.text_renderable` |
| Authored contract version | v2 |
| Catalog contract ID | `component.text_renderable.contract.v1` |
| Contract hash | `sha256:66058e7c1c583eff7a31721e7d08c4033742b8d7cab7c01b5a5ee86a820adba6` |
| Field contract hash | `sha256:92d2951ac551c2e890c330a1afc8d045bc8471e6ab909264aea1c8613617271b` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/editorSceneProjection.ts:textRenderableFromArtifact [web_client/spa/src/render/editorSceneProjection.ts:textRenderableFromArtifact] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `align`, `anchor`, `backgroundColor`, `billboardMode`, `color`, `depthTest`, `depthWrite`, `enabled`, `fadeDistance`, `fallbackFontKeys`, `fontFamily`, `fontKey`, `fontSize`, `fontWeight`, `letterSpacing`, `lineHeight`, `maxDistance`, `maxWidth`, `offset`, `opacity`, `outlineColor`, `outlineWidth`, `projectionKey`, `statKey`, `text`, `textSource` |
| Canonical field count | 31 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `align` | Horizontal alignment of multi-line text. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `left`, `center`, `right` |
| `anchor` | Which part of the text block sits at the entity position plus offset. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `center`, `top`, `bottom` |
| `backgroundColor` | Fill color behind the text; empty means no backing panel. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `billboardMode` | How the text faces the camera: always, only turning around Y, or fixed in place. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `camera`, `yAxis`, `fixed` |
| `color` | Text color. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `depthTest` | Lets objects hide the text behind them; off draws it on top of everything. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `depthWrite` | Lets the text itself hide things behind it; usually left off. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `enabled` | Show or hide the text. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `fadeDistance` | Camera distance where the text starts fading out. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `fallbackFontKeys` | Font keys tried in order when the primary font is unavailable; the built-in sans face is always the final fallback. | array | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/array |  |
| `fallbackFontKeys[]` | Font keys tried in order when the primary font is unavailable; the built-in sans face is always the final fallback. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/array | minLength: 1<br>maxLength: 240 |
| `fontFamily` | Typeface family for the text. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `sans`, `mono`, `serif` |
| `fontKey` | Font key: a pinned built-in (builtin:ibm-plex-sans / mono / serif) today; uploaded font assets when the bake pipeline lands. Overrides Font family when set; falls back through Fallback fonts, then built-in sans. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | minLength: 1<br>maxLength: 240<br>semantic: `font_key` |
| `fontSize` | Type size of the text. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 6<br>max: 96 |
| `fontWeight` | Type weight of the text. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `regular`, `medium`, `semibold`, `bold` |
| `letterSpacing` | Extra spacing between letters in world units; 0 is the font default. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number |  |
| `lineHeight` | Spacing between wrapped lines; 0 uses the default derived from font size. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `maxDistance` | Camera distance beyond which the text stops drawing. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.1 |
| `maxWidth` | Wrap width in world units; 0 keeps the text on a single line. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `offset` | Shift from the entity's position, in world units; raise Y to float the text overhead. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `offset.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `offset.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `offset.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `opacity` | Text opacity; 0 is fully transparent, 1 is fully opaque. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `outlineColor` | Color of the outline around the letters. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `outlineWidth` | Thickness of the letter outline; 0 disables it. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 8 |
| `projectionKey` | Key of the runtime projection whose value the text shows when Text source is projection. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | minLength: 1<br>maxLength: 160<br>semantic: `runtime_projection_key` |
| `statKey` | Name of the live entity stat shown when Text source is stat (e.g. health, score). | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | minLength: 1<br>maxLength: 160<br>semantic: `runtime_stat_key` |
| `text` | The words shown when Text source is static. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | maxLength: 256 |
| `textSource` | Where the words come from: typed here, the entity's name, a runtime projection, or a live entity stat. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `static`, `entityName`, `projection`, `stat` |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "TextRenderableComponent",
  "enabled": true,
  "text": "Label",
  "textSource": "static",
  "color": "#ffffff",
  "opacity": 1,
  "fontSize": 18,
  "fontFamily": "sans",
  "fontWeight": "regular",
  "align": "center",
  "anchor": "bottom",
  "maxWidth": 0,
  "lineHeight": 0,
  "letterSpacing": 0,
  "offset": {
    "x": 0,
    "y": 1.6,
    "z": 0
  },
  "billboardMode": "camera",
  "depthTest": true,
  "depthWrite": false,
  "maxDistance": 80,
  "fadeDistance": 60,
  "outlineWidth": 0
}
```
## TimerComponent

Fires timer events on a server-owned interval. Scripts subscribe with onTimerFired (event.timerKey, event.iteration) to drive cooldowns, regen tickers, and day/night cycles.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Runtime |
| Component palette visibility | default |
| Component palette notes | Atomic runtime event source. |
| Default component key | `component.timer` |
| Authored contract version | v1 |
| Catalog contract ID | `component.timer.contract.v1` |
| Contract hash | `sha256:27e9c6c8c08419b6c9323cdc2e8f0d493f1168de73598c6ec832eb6e3bd6d46e` |
| Field contract hash | `sha256:caf6bc3b75c2bebae16f7902a6e884823f95da654f446ae954d3886e01856a59` |
| Migration class | none |
| Capability packs | `timer.v1` |
| Runtime systems | `runtime.timerSystem` |
| Runtime consumer status | consumed |
| Renderer consumer status | not_applicable |
| Consumer status reason | read by server/src/modules/runtime/runtimeSystems.ts:latestTimerFiring [server/src/modules/runtime/runtimeSystems.ts:latestTimerFiring] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `enabled`, `intervalSeconds`, `maxFirings`, `repeating`, `timerKey` |
| Canonical field count | 6 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `enabled` | Master switch; the timer only counts and fires while on. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `intervalSeconds` | Seconds between firings. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0.01 |
| `maxFirings` | Total firings allowed over the timer's life; 0 means unlimited. | integer | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/number | min: 0<br>max: 9007199254740991 |
| `repeating` | On: fires every interval while enabled. Off: fires once, then stays quiet (the timer stays enabled but will not fire again). | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/boolean |  |
| `timerKey` | Tag scripts use to tell this timer apart (onTimerFired event.timerKey). | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | consumed | not_applicable | editable/string | minLength: 1<br>maxLength: 80<br>semantic: `component_local_key` |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "TimerComponent",
  "enabled": true,
  "timerKey": "default",
  "intervalSeconds": 1,
  "repeating": true,
  "maxFirings": 0
}
```
## TransformComponent

Local position, rotation, and scale. Root entities treat this as world transform.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Spatial |
| Component palette visibility | default |
| Component palette notes | Atomic spatial primitive. |
| Default component key | `component.transform` |
| Authored contract version | v2 |
| Catalog contract ID | `component.transform.contract.v1` |
| Contract hash | `sha256:36def25f47fca5de2f6ef936d5594be97b5ace51b1bd78111a3def4da2f42d21` |
| Field contract hash | `sha256:f646529e57557e2ecb2c3493434f74dec6c67ef552a141a479c82eb3131aea3b` |
| Migration class | none |
| Capability packs | `hierarchy.v1` |
| Runtime systems | `runtime.hierarchyTransformSystem` |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/editorSceneProjection.ts:authoringRotationToRenderRadians [web_client/spa/src/render/editorSceneProjection.ts:authoringRotationToRenderRadians] |
| Replication policy | `public/high/reliable` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `position`, `rotation`, `scale` |
| Canonical field count | 13 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `position` | Where the entity sits in the World, in world units; children measure from their parent. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `position.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `position.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `position.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `rotation` | Tilt and turn in degrees around the X, Y, and Z axes. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `rotation.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `rotation.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `rotation.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested |  |
| `scale` | Size multiplier per axis; 1 keeps the authored size. Each axis must be at least 0.001. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/vec3 |  |
| `scale.x` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested | min: 0.001 |
| `scale.y` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested | min: 0.001 |
| `scale.z` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/nested | min: 0.001 |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |

Defaults:

```json
{
  "type": "TransformComponent",
  "position": {
    "x": 0,
    "y": 0,
    "z": 0
  },
  "rotation": {
    "x": 0,
    "y": 0,
    "z": 0
  },
  "scale": {
    "x": 1,
    "y": 1,
    "z": 1
  }
}
```
## VelocityComponent

The live movement vector. Runtime-owned - authored only as a reset default; the movement/physics step patches it each tick. Does not opt an entity into collision response or events by itself.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Physics |
| Component palette visibility | hidden |
| Component palette notes | Internal runtime lane (W5-01b); launch characters via Physics.command '{' type: "launch" '}' and configure movement on CharacterMovementComponent. |
| Default component key | `component.velocity` |
| Authored contract version | v1 |
| Catalog contract ID | `component.velocity.contract.v1` |
| Contract hash | `sha256:0c16207cd0d92b93b3146715812f3bb9706606d82f91a4af2992186668b64fc5` |
| Field contract hash | `sha256:0f938cc8d2ddf45e6b89c0738a38e76f84cc8af1f891afcb1f2a3c6fc682f73a` |
| Migration class | none |
| Capability packs | `physics.v1` |
| Runtime systems | `runtime.physicsSystem`, `runtime.collisionSystem` |
| Runtime consumer status | internal |
| Renderer consumer status | not_applicable |
| Consumer status reason | Internal engine queue consumed by runtime systems. |
| Replication policy | `public/medium` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `angularVelocity`, `velocity` |
| Canonical field count | 9 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `angularVelocity` | Current spin in radians per second about each axis; dynamic Rigidbody physics reads it back and re-seeds it on world rebuild so a spinning body keeps its spin. Runtime patches it when the body rotates. | object | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `angularVelocity.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `angularVelocity.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `angularVelocity.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | internal | not_applicable | hidden: Component discriminator is selected by component type. |  |
| `velocity` | Current movement in world units per second along each axis; Rigidbody physics or the kinematic controller decides whether this is consumed. Runtime patches it every tick. | object | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `velocity.x` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `velocity.y` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |
| `velocity.z` |  | number | yes | hidden | hidden | hidden | hidden | readonly | exposed | internal | not_applicable | hidden: Internal engine component not shown in normal inspector authoring. |  |

Defaults:

```json
{
  "type": "VelocityComponent",
  "velocity": {
    "x": 0,
    "y": 0,
    "z": 0
  },
  "angularVelocity": {
    "x": 0,
    "y": 0,
    "z": 0
  }
}
```
## WaterComponent

Planar water surface with quality-gated reflection intent.

| Field | Value |
| --- | --- |
| Classification | atomic |
| Category | Rendering |
| Component palette visibility | default |
| Component palette notes | Renderer presentation component; this contract covers existing metadata/projection without adding water feature depth. |
| Default component key | `component.water` |
| Authored contract version | v1 |
| Catalog contract ID | `component.water.contract.v1` |
| Contract hash | `sha256:0fdd1b6a5ab839d671552076ac76edbad52a00260c3124895af689d9b33aa749` |
| Field contract hash | `sha256:f7673b9dcdb9db08d49cbc342558af94094da4748d54bd46a64f016bd524c33f` |
| Migration class | none |
| Capability packs | `rendering.v1` |
| Runtime systems | None |
| Runtime consumer status | projected |
| Renderer consumer status | consumed |
| Consumer status reason | read by web_client/spa/src/render/presentationWaterDecal.ts:buildWater [web_client/spa/src/render/presentationWaterDecal.ts:buildWater] |
| Replication policy | `public/low` |
| Authorization | Script-mutable only through authorized Entity host operations; Runtime validates payloads with ComponentSchema. |
| Inspector fields | `color`, `depth`, `depthBlendDistance`, `enabled`, `materialRef`, `normalMapAssetId`, `opacity`, `reflectionMode`, `refractionMode`, `shallowColor`, `shoreFoamStrength`, `surfaceLevel`, `waveProfile`, `waveStrength`, `width` |
| Canonical field count | 21 |

Canonical Field Contract:

| Field | Description | JSON | Required | Frontend | AI | MCP | SDK | Script | Runtime | Runtime consumer | Renderer consumer | Inspector | Validation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `color` | Overall water tint. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `depth` | Water surface extent along Z, in world units. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 1 |
| `depthBlendDistance` | Water thickness distance driving screen-space refraction depth, in world units. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `enabled` | Show or hide the water surface. | boolean | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/boolean |  |
| `materialRef` | Optional authored material asset for the water surface; overrides the built-in color/wave shading when set. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `material_id`<br>ref: asset id material |
| `normalMapAssetId` | Texture that adds fine ripple detail to the surface. | string | no | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/reference | pattern: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\|00000000-0000-0000-0000-000000000000\|ffffffff-ffff-ffff-ffff-ffffffffffff)$`<br>semantic: `asset_id`<br>ref: asset id texture |
| `opacity` | How solid the surface looks, from 0 (clear) to 1 (opaque). | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `reflectionMode` | How mirror reflections are produced; costlier modes look sharper. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `none`, `cheap`, `screen`, `planar`, `cube` |
| `refractionMode` | How the underwater view bends: none, or true screen refraction. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/string | enum: `none`, `screen` |
| `shallowColor` | Tint where the water is shallow, near shores. | string | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/color | pattern: `^#(?:[0-9a-fA-F]{6}\|[0-9a-fA-F]{8})$` |
| `shoreFoamStrength` | Amount of foam where water meets land. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `surfaceLevel` | Height of the water plane in the World. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number |  |
| `type` |  | string | yes | hidden | hidden | hidden | hidden | hidden | hidden | consumed | not_applicable | hidden: Component discriminator is selected by component type. |  |
| `waveProfile` | Fine wave shape controls: amplitude, frequency, speed, choppiness, and normal detail. | object | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/object |  |
| `waveProfile.amplitude` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `waveProfile.choppiness` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `waveProfile.frequency` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0.001 |
| `waveProfile.normalScale` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `waveProfile.speed` |  | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0 |
| `waveStrength` | Overall size of the waves, from calm to choppy. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 0<br>max: 1 |
| `width` | Water surface extent along X, in world units. | number | yes | exposed | exposed | exposed | exposed | exposed | exposed | projected | consumed | editable/number | min: 1 |

Defaults:

```json
{
  "type": "WaterComponent",
  "enabled": true,
  "width": 32,
  "depth": 32,
  "surfaceLevel": 0,
  "color": "#3aa7ff",
  "shallowColor": "#72d6ff",
  "opacity": 0.72,
  "waveStrength": 0.15,
  "waveProfile": {
    "amplitude": 0.15,
    "frequency": 1,
    "speed": 1,
    "choppiness": 0.25,
    "normalScale": 0.65
  },
  "reflectionMode": "cheap",
  "refractionMode": "none",
  "shoreFoamStrength": 0.35,
  "depthBlendDistance": 3
}
```
