KNAT Application Profile: Vehicle Entity
Purpose
This document defines the KNAT application profile for a Vehicle Entity — a transport-class Janet entity with a mechanical drivetrain, a driver binding slot, and a physics plane shared with other moving actors.
The goal of this profile is to allow KNAT clients to:
- observe the mechanical state of a running vehicle (rpm, gear, steering angle, speed)
- determine which entity is currently mounted as driver
- inject control inputs (where authorized — driver only)
- subscribe to vehicle-scoped event signals (damage, collision, etc.)
- locate the vehicle in world space via its region coupling
This profile intentionally does not model:
- the internal component simulation (that is the vehicle physics engine's concern)
- passenger bindings beyond the driver slot (future: occupant surface)
- cargo inventory (future: inventory surface)
- world-level routing or route state (that is the transport segment surface)
- governance or economic participation (those are Brand surfaces)
Those surfaces exist elsewhere in the system.
This profile exposes the runtime mechanics and binding state of the vehicle actor itself.
Conceptual Model
A vehicle is not an autonomous actor — it is a mechanical extension of its driver. The distinction shapes the entire surface:
Biologic surface → what the body is doing (pose + physiology + intent)
Vehicle surface → what the machine is doing (drivetrain + physics + binding)
The control surface of a vehicle is uniquely access-gated at the binding layer, not just policy-shaped. VehicleEntity.write_control() will reject writes from any entity that is not the currently mounted driver. This is a harder gate than the biologic view projection mechanism.
Within the KNAT graph, a vehicle exposes three categories of surface:
Structural surfaces — compiled from the spec, stable for the lifetime of the entity:
spec provenance, genome, architecture
Live simulation surfaces — updated each frame by the vehicle physics engine:
drivetrain rpm, gear, steering_angle
physics speed (shared /world/physics/* prefix)
driver bound state, entity_id of current driver
Intent surface — written by the mounted driver, not by the simulation:
control steering, throttle, brake, clutch, gear, handbrake
The intent surface is doubly protected: it is absent or redacted in projected view, and every write is rejected at the entity layer unless the caller is the registered driver.
Root Graph
A vehicle entity KNAT surface is rooted at:
/vehicle::<entity_id>/
Top-level families:
/vehicle::<entity_id>/
/spec
/engine
/location
/drivetrain
/physics
/control
/driver
/signals
Shared paths with other entity subtypes:
/world/runtime/state "initializing" → "running"
/world/runtime/frame monotonically increasing integer
/world/physics/speed current speed in m/s (same path used by biologic)
Spec Surface
Provenance of the vehicle spec this entity was instantiated from. Static after instantiation.
/vehicle::<entity_id>/spec/ref
/vehicle::<entity_id>/spec/genome
/vehicle::<entity_id>/spec/architecture
| node | type | capabilities |
|---|---|---|
ref |
String | read |
genome |
String | read |
architecture |
String | read |
architecture is the free-form discriminant from the vehicle spec (e.g. brands.common.vehicle.passenger.beatle.front_engine_rwd). Consumers building renderers or drivetrain visualisers should read it first to determine which components and joint types are valid.
Engine Surface
Identity of the engine presenting this vehicle. Present on all Janet brand objects exposed via KNAT.
/vehicle::<entity_id>/engine/name
/vehicle::<entity_id>/engine/version
| node | type | capabilities |
|---|---|---|
name |
String | read |
version |
String | read |
Location Surface
The cross-domain binding between this vehicle and the region field it currently occupies. Same seam pattern as the biologic location surface — region and vehicle KNAT surfaces are coupled here.
/vehicle::<entity_id>/location/region_id
/vehicle::<entity_id>/location/cell_id
| node | type | capabilities |
|---|---|---|
region_id |
String | read, subscribe |
cell_id |
String | read, subscribe |
region_id matches the id field in the corresponding RegionRuntime snapshot.
cell_id matches a stable key in RegionRuntime.cells.
Drivetrain Surface
Live mechanical state of the powertrain and steering system. Written each step by VehicleSurface.step(). These are outputs, not inputs — they reflect what the machine is currently doing, not what the driver has asked for.
/vehicle::<entity_id>/drivetrain/rpm
/vehicle::<entity_id>/drivetrain/gear
/vehicle::<entity_id>/drivetrain/steering_angle
Mapped from KNAT graph paths:
| surface path | graph path | type | capabilities | notes |
|---|---|---|---|---|
drivetrain.rpm |
/vehicle/state/rpm |
Float | read, subscribe | engine RPM; 0 = off, max ≈ 7000 |
drivetrain.gear |
/vehicle/state/gear |
Integer | read, subscribe | -1 reverse, 0 neutral, 1–6 fwd |
drivetrain.steering_angle |
/vehicle/state/steering_angle |
Float | read, subscribe | radians; ±π/4 maximum |
rpm is lerped toward the throttle target each step (factor RPM_LERP = 0.15). It does not jump instantly — consumers can observe the ramp-up and engine braking behaviour directly.
steering_angle is the actual mechanical angle, derived from control.steering × MAX_STEERING_ANGLE. It tracks the driver input with no lag (direct mapping in the current model).
Physics Surface
Observable physical state shared with other entity subtypes. The /world/physics/ prefix is the canonical path shared by biologic, vehicle, and future droid surfaces — systems tracking all moving actors can use it uniformly.
/world/physics/speed
| node | graph path | type | capabilities | units |
|---|---|---|---|---|
speed |
/world/physics/speed |
Float | read, subscribe | m/s |
Speed is derived from rpm × gear_ratio × SPEED_SCALE, reduced by braking. It is always ≥ 0. Additional physical observables (acceleration, heading, position) will be added under /world/physics/ as the physics model matures.
Control Surface
The driver intent injection surface. Present in full view; absent or redacted in projected view for observers who are not the mounted driver.
/vehicle/control/steering
/vehicle/control/throttle
/vehicle/control/brake
/vehicle/control/clutch
/vehicle/control/gear
/vehicle/control/handbrake
| path | type | range | binding target |
|---|---|---|---|
steering |
Float | -1.0 .. 1.0 | steering_column.angle |
throttle |
Float | 0.0 .. 1.0 | engine_crank.torque |
brake |
Float | 0.0 .. 1.0 | (friction model) |
clutch |
Float | 0.0 .. 1.0 | clutch.engagement |
gear |
Integer | -1 .. 6 | transmission.ratio_index |
handbrake |
Float | 0.0 .. 1.0 | rear_diff.brake_torque |
The binding map (defined in bindings.json) wires each KNAT path to its mechanical simulation target node. When writing, the caller must be the entity currently registered in the driver slot — VehicleEntity.write_control() returns EntityError::EntityNotOwned otherwise.
On dismount (VehicleEntity.dismount_driver()), all control inputs are reset to neutral: throttle 0.0, brake 0.0, steering 0.0, gear 0. This is enforced in code, not by the driver.
Control values may be raw scalars or structured objects with arbitration metadata:
# scalar (direct driver input)
throttle: 0.7
# structured (system-written, e.g. cruise control assist)
throttle:
value: 0.7
source: entity::alice
mode: direct
mode values:
| mode | meaning |
|---|---|
direct |
driver input passthrough |
assist |
driver-assist overlay (steering correction, ABS) |
override |
autonomous system overriding driver input |
Consumers reading control must handle both forms. The structured form does not imply the driver is absent — mode: direct + source: entity::alice is the normal form when full arbitration tracing is enabled.
Driver Surface
Current driver binding state. A vehicle carries exactly one driver at a time.
/vehicle::<entity_id>/driver/bound
/vehicle::<entity_id>/driver/entity_id
| path | type | capabilities | notes |
|---|---|---|---|
bound |
Boolean | read, subscribe | true when a driver is mounted |
entity_id |
String | read, subscribe | absent when bound = false |
Subscribe to bound to detect mount/dismount events. When bound transitions from true to false, all control inputs have already been neutralised by the engine — no further cleanup is needed by the consumer.
entity_id matches an EntityId in the system's EntityRegistry. Cross-reference against the biologic or droid surface at that id to characterize the driver.
Signals Surface
Active signals emitted by or directed at this vehicle. Keyed by signal_id. Vehicle signals are mechanical and environmental events — distinct from game-domain matcher events and from biologic behavioral signals.
/vehicle::<entity_id>/signals/<signal_id>/kind
/vehicle::<entity_id>/signals/<signal_id>/intensity
/vehicle::<entity_id>/signals/<signal_id>/frame
/vehicle::<entity_id>/signals/<signal_id>/source
/vehicle::<entity_id>/signals/<signal_id>/target
| node | type | capabilities | notes |
|---|---|---|---|
kind |
String | read, subscribe | e.g. damage, collision, fuel_low, speed_limit_breach, alert |
intensity |
Float | read, subscribe | 0.0 – 1.0 |
frame |
Integer | read | emission frame |
source |
String | read | optional: cell_id, entity_id, or chem path |
target |
String | read | optional: entity_id or system name |
Drivetrain Constants (Physics Reference)
These constants are baked into VehicleSurface and define the mock physics model. Consumers interpreting telemetry should be aware of them:
| constant | value | meaning |
|---|---|---|
GEAR_RATIOS |
[0,0, 3.5, 2.1, 1.4, 1.0, 0.8, 0.65] |
index by gear+1; rev/neutral both 0 |
MAX_RPM |
7000 | RPM ceiling |
IDLE_RPM |
800 | RPM floor when in gear |
RPM_LERP |
0.15 | RPM approach rate per step |
SPEED_SCALE |
0.002 | rpm × gear_ratio × scale → m/s |
BRAKE_FACTOR |
0.25 | fraction of speed shed per step at full brake |
MAX_STEERING |
±π/4 rad | maximum steering angle (45°) |
Gear index convention: control input and drivetrain.gear use the same range. -1 = reverse (gear_ratio 0 in current model; reverse kinematics not yet implemented), 0 = neutral, 1–6 = forward.
Cross-Domain Joins
| Join | Key | Description |
|---|---|---|
| vehicle → region | location.region_id + location.cell_id |
Vehicle placement in regional space. Same join as biologic location. |
| vehicle → driver | driver.entity_id |
Resolves to the biologic or droid surface of the mounted driver. |
| vehicle → world physics | /world/physics/speed |
Shared speed path across all moving entity subtypes. |
| vehicle ↔ transport segment | region_id + cell_id |
World transport segments reference region nodes; vehicle location determines which segment it occupies. |
Responsibilities Boundary
VehicleOwned — control inputs, drivetrain state, driver binding slot
PhysicsOwned — speed derivation, brake blending, RPM lerp
BindingOwned — write authorization (who may write to control nodes)
RegionOwned — location, terrain friction, cell environment
DriverOwned — intent values written to /vehicle/control/*
The vehicle surface in KNAT only exposes VehicleOwned and PhysicsOwned outputs. Never write executor or region-owned state into this surface.
MVP Presence
A minimal valid vehicle KNAT snapshot must include:
id,frame,engine,viewlocation.region_id,location.cell_iddrivetrain.rpm,drivetrain.gear,drivetrain.steering_anglephysics.speeddriver.boundsignals(may be empty object)spec.ref,spec.genome,spec.architecture
The control surface may be omitted entirely in projected view. Consumers must handle its absence.
Example Snapshot
{
"id": "vehicle::beatle_001",
"spec": {
"ref": "file://defs/vehicle/examples/beatle.yml",
"genome": "000A1C0000",
"architecture": "brands.common.vehicle.passenger.beatle.front_engine_rwd"
},
"engine": { "name": "janet-executor", "version": "0.4.0" },
"view": "full",
"frame": 312,
"location": {
"region_id": "samanga",
"cell_id": "cell_042"
},
"drivetrain": {
"rpm": 3210.4,
"gear": 3,
"steering_angle": 0.18
},
"physics": {
"speed": 9.53
},
"control": {
"steering": { "value": 0.24, "source": "entity::alice", "mode": "direct" },
"throttle": { "value": 0.6, "source": "entity::alice", "mode": "direct" },
"brake": 0.0,
"clutch": 0.0,
"gear": 3,
"handbrake": 0.0
},
"driver": {
"bound": true,
"entity_id": "entity::alice"
},
"signals": {}
}