Architectural overview of Oya, the embodied-robotics "hive" domain: a performance-critical Rust engine, napi/wasm FFI bridges with bit-for-bit Rust↔TypeScript parity, a canonical contracts surface, a TypeScript platform layer, and fifteen Fastify control/coordination services — with an honest account of what is built versus legacy versus planned.
Oya is the Oshun platform's domain for embodied autonomous robots operating as one coordinated fleet — what the code consistently calls the embodied hive. A hive is a heterogeneous mix of physical machines: aerial drones, wheeled ground robots, stair-climbers, floor-care units (vacuum/mop), and manipulator-equipped robots, all sharing one spatial world model, one safety envelope, and one market-based task allocator. Named after the Yoruba goddess of winds, storms, and transformation, Oya spans the full stack from real-time flight control and ISO/TS 15066 human-robot safety up to fleet economics and a home-robot product surface (eldercare, pet care, security, ambient sensing).
This is a substantial, genuinely implemented system — roughly a thousand tracked
source files. An earlier generation of the domain was a single large
TypeScript library, @oya/core (≈101 modules of drone primitives). That library
still exists, but the code now describes it as the layer the services are
migrating off of: the performance-critical core has been ported to a Rust
engine, and the services call that engine through FFI bridges. The previous
version of this page described Oya as "entirely a library domain… no running
applications or services yet, all TypeScript." That is no longer true and this
rewrite corrects it: there are 15 services under apps/oya/, a 21-crate Rust
workspace under libs/oya/engine/, and a canonical contracts package — all with
tests.
The Shape of the System#
Oya is organised as five cooperating layers. Control and data flow downward from the product/service tier, through the typed platform libraries and the canonical contracts, across the FFI seam, into the Rust engine — and telemetry flows back up. The hard architectural rule visible throughout the code is a separation between coordination and control: the fleet orchestrator assigns work and reserves space-time but never emits a motor command; only the flight gateway and on-robot control loops energise actuators, behind a fail-closed gate.
Layer 1 — The Rust Engine (libs/oya/engine/)#
The engine is the authoritative, performance-critical core. It is a per-domain
Cargo workspace (there is no monorepo-root Rust workspace; this mirrors
libs/maya/engine-core), declared in libs/oya/engine/Cargo.toml with 21
member crates, resolver = "2", and a release profile tuned for
embedded/real-time use (lto = true, codegen-units = 1). Each crate is
greenfield Rust written against plain f64/SI types and serde; several carry
their own benches/, tests/, and examples/.
The crates group by responsibility:
Foundations. oya-types (crates/oya-types/src/lib.rs) is the type
vocabulary: branded UUID-v4 IDs (DroneId, SwarmId, MissionId,
WaypointId), geodetic/quaternion/Euler math types, the three coordinate frames
(GPSCoordinates, LocalCoordinates, BodyCoordinates), the flight/state/
airspace enums, and the composite records (Telemetry, Mission, FlightPlan,
GeofenceZone, …). Critically, every type derives serde with
#[serde(rename_all = "camelCase")] so its JSON wire format is identical to
the TypeScript @oya/core/contracts payloads, and its validators
(is_valid_uuid_v4, Quaternion::is_unit, is_valid range checks) mirror the
Zod schemas — its test module explicitly "re-proves the TypeScript schema's
accept/reject behavior." oya-math supplies the numeric toolkit: Vincenty and
Haversine geodesy, coordinate transforms, kinematics, CRC, lookup tables, and a
simd module.
Estimation & navigation. oya-estimation (IMU/GNSS/VIO/INS fusion, Mahony
AHRS, calibration, ZUPT, event-camera, gps_security), oya-navigation (A*,
minimum-snap trajectories, potential-field and sampling planners, social-aware
avoidance), oya-mapping (TSDF, occupancy grids, pose-graph SLAM,
relocalization, multi-floor/multi-session, fiducials), and oya-scenegraph (the
hierarchical Building → Floor → Room → Object scene graph plus a
freshness-tracked spatial memory with a fail-loud QueryResult).
Multi-robot coordination. oya-swarm implements reciprocal collision
avoidance in depth — orca, orca3d, nh_orca, rvo, buffered Voronoi,
collision-cone and collision-probability, consensus, formations, the Hungarian
assignment, and task_allocation. oya-fleet holds the market allocator (CBBA
auction, coalition formation, MAPF). oya-comms provides the hive's nervous
system: gossip, a bandwidth-aware scheduler, descriptor exchange, access
control, and PTP/IEEE-1588 timesync.
Control & protocol. oya-control runs the flight/motion stack — SO(3)
attitude control, velocity/position loops, control allocation, motor-failure
compensation, feedforward, and a full mission lifecycle (mission_modes,
mission_rth, mission_validate). oya-mavlink is a from-scratch MAVLink2
implementation: codec, message signing (sha256/signing), the mission/
parameter/FTP/camera/gimbal sub-protocols, PX4 and ArduPilot dialects, a router,
a tx_queue, and reliability/high-latency handling (with a fuzz_decode test).
Perception, cinematography, audio. oya-perception (filters, two-view and
multi-view geometry, reconstruction, tracking), oya-cinematography (shot
library, camera-MAPF, multicam orchestration, continuity, trajectory generation
— consumed by the dashboard for path preview), and oya-dsp (AEC, beamforming,
FFT, VAD, wake-word, barge-in — the audio front-end for human-robot interaction,
with a real-time audio-path test).
Embodiment & home autonomy. This is what makes Oya a hive rather than a
drone library: oya-manipulation (IK, grasp synthesis, grip control, whole-body
control, visual servoing, real-time action chunking), oya-locomotion
(locomotion-mode arbitration, morphing/reconfiguration, stair-climbing,
telescoping/foldable arms, perch-latching, dock reservation, cost-of-transport,
a capability passport), oya-floorcare (boustrophedon coverage-path planning
over a cellular decomposition, dynamic anytime replanning, multi-robot Voronoi
partition with failure-reabsorption, carpet detection, dirt-adaptive cleaning),
oya-energy (SOC/SOH, battery-swap, charge-banding, dock sizing, grid
scheduling, perch overwatch, wear), and oya-ambient (mmWave-radar CFAR,
range-Doppler, CSI, UWB, sensor-fusion presence detection).
Safety. oya-safety (crates/oya-safety/src/lib.rs) is the
functional-safety heart: ISO/TS 15066 speed-and-separation monitoring
(ssm, the protective separation Sp = Sh + Sr + Ss + C + Zd + Zr),
power-and-force limiting (pfl, reduced mass, transient contact energy,
per-body-region limits), a control-barrier-function shield (cbf, min-norm
velocity projection into the safe set), indoor flight_safety guards,
edge_safety (cliff/stair/threshold drop detection), and child_safety
interlocks — every one fail-closed. oya-integration-tests exercises the crates
together through a pipeline.rs.
Layer 2 — FFI Bridges and Rust↔TS Parity#
The engine reaches TypeScript through two thin bridges, each exposing a typed subset of the crates (not the whole engine):
libs/oya/node-bridge/— a napi-rs crate (src/lib.rs) compiled to a native.nodeaddon for the Node services. It re-exports geodesy (vincenty_distance,haversine_distance,bearing),euler_to_quaternion,crc32, and the 2-D ORCAorca_half_plane, each delegating straight to the engine crates. Its header is explicit about intent: this is "the production Rust↔TypeScript seam: theapps/oya/*services call these native functions instead of the legacy@oya/coreTypeScript."libs/oya/wasm-bridge/— a wasm-bindgen crate (src/lib.rs) compiled towasm32-unknown-unknownfor the browser operator dashboard. It exposes the same geodesy/quaternion/ORCA functions plus the plotting surface the dashboard needs: cinematography camera-path samplers (orbit_camera_path,crane_camera_path,helix_camera_path,bezier_camera_path) andoya-navigationminimum-snap evaluation, all returning flatFloat64Arraybuffers with documented*_STRIDEcolumn layouts to avoid per-point allocation. Absent optional angles are encoded asNaN, never a fabricated0.0.
Because both bridges delegate to the same crates, the napi addon, the wasm
module, and the legacy TS produce bit-for-bit identical values. That equivalence
is enforced, not assumed: libs/oya/engine/parity/check.mjs is a differential
test that runs the Rust producer (cargo run --example parity_dump) and the
TypeScript producer (@oya/core via ts_dump.ts) over identical inputs and
asserts every named float agrees to relative error ≤ 1e-12 (or absolute ≤ 1e-9
near zero) and every integer (CRC) matches exactly, exiting non-zero to gate CI.
The bridges' own tests pin oracle values (e.g. London→Paris Vincenty ≈
343923.12 m).
Layer 3 — Canonical Contracts (@oshun/contracts/oya)#
libs/contracts/src/oya/ is the single source of truth for everything that
crosses a wire or process boundary. Its index.ts documents the surface and its
purpose: schemas "coherent with the Rust engine/hive types in libs/oya/engine
… so payloads validate identically on both sides." Every object schema is
.strict(), so an unexpected key is a loud rejection rather than a silently
dropped field. The modules:
primitives.ts—Vec3, the unit-quaternionQuaternionSchema(norm ≈ 1, tolerance 0.01, matchingoya_types::Quaternion::is_unit),GpsCoordinate, the brandedDroneId/MissionId/DockId, and theCapabilityenum (fly,roll,grasp,climb_stairs,vacuum,mop,camera) — the vocabulary the allocator arbitrates over.telemetry.ts— the downlinkTelemetrysnapshot and the uplinkControlCommand(flight-mode + setpoint vocabulary mirroringoya-control).mission.ts,dock.ts,capability.ts— missions/waypoints/flight plans; dock slot-occupancy and charge handshake (mirroringoya-energy); and the §15.7 hot-swap payload capability passport (mass/CoM/power budget + the capabilities a payload grants when bound).fleet.ts—FleetAgent,FleetTask,FleetState,TaskBid,Allocation, and the hard capability gatebidIsCapabilityValid(agent, bid)— the contract-layer mirror ofFleetAgent::can_serviceinoya-fleet.world-model.ts— the scene graph and the fail-loud discriminatedQueryResultunion:Fresh(a confident remembered value),Stale(an honest "not sure anymore, last known at tick N"), orUnknown(never observed). The store "never fabricates a confident answer for a decayed memory; that refusal is the contract."sensor.ts— observations are compressed descriptors (embeddings, occupancy patches, beamformed spectra) with PTP-disciplined timestamps; there is deliberately no raw-frame field (bandwidth + privacy).safety.ts— the ISO/TS 15066SafetyEnvelope(per-body-regionforceLimitsN,ssmMinSeparationM) and theEStopState(nominal/soft_stop/hard_stop).
Layer 4 — The TypeScript Platform (libs/oya/*)#
A set of focused libraries provides the service runtime on top of the contracts:
@oya/common—Result<T, E>, exact unit conversions, geo helpers, branded IDs, domain constants.@oya/fastify-core— the shared service core:createOyaServer(typed error envelope, 404 handler,/health, request-id propagation, JSON body limit) and a testableregisterGracefulShutdown. Every service is built on it.@oya/service-lib— production-hardening primitives: aCircuitBreaker, retry/backoff, a config schema + loader, health aggregation, a PostgresDbConnectionManagerover a mockable pool boundary, and fail-closed JWT auth middleware.@oya/database— the persistence schema (§3.1): six tables —missions,flights,telemetry,maps,fleet_state,consumables— plus aquery-executorand repository layer.@oya/event-publisher/@oya/event-handlers— a typed event catalogue that validates each payload against its Zod schema before publish, and subscribers that reduce events into an injected in-memory store, gated by the latched per-drone e-stop.@oya/privacy(§9.2) — a deterministic, fail-closed enforcement runtime: redact every unrecognised person by default, planner/perception no-record geofences via exact point-in-polygon, consent policy, and storage policy.@oya/security(§9.3) — realnode:cryptoonly: Ed25519 device identity, AES-256-GCM media encryption, HKDF-SHA256 per-home tenancy isolation, a fail-closed two-factor door-unlock gate, and cross-modal anti-spoofing.@oya/maintenance(§9.4) — consumable-level accounting (filters, brushes, mop pads, charge-cycle budget) and the anti-bricking "dignity" guarantee.@oya/readiness-gates(§10.2/§10.3) — ten anti-fabrication release gates (coverage-completeness, grasp-honesty, map-freshness, energy-reserve, safety-conformance, privacy-enforcement, offline-degradation, mode-economy, economic-viability, live-OTA-safety) plus post-update rollback. Each is a pure function returning{ gate, status, reasons, metrics }that recomputes its invariant from first principles;runAllGatesis a conjunction — the release isBLOCKEDif any gate fails.@oya/sdk(§3.1) — a thin, Zod-validated REST client for external consumers.
Legacy / prior generation. @oya/core (the 101-module TS drone library) and
the single-file sibling readiness evaluators (@oya/flight-control,
@oya/mission-planning, @oya/safety, @oya/swarm-intelligence,
@oya/telemetry) remain in the tree. They are real and tested, but the bridge
headers and contracts describe them as the surface being migrated off of — the
Rust engine is now authoritative for the performance-critical math, with
@oya/core retained as the parity oracle.
Layer 5 — The Service Tier (apps/oya/)#
Fifteen Fastify microservices, each built on createOyaServer, listening on the
401x/402x port band. They split along the coordination/control line the
codebase is careful to maintain:
§4.1 — control & safety plane.
svc-flight-gateway— the secure-link control plane. A per-droneLinkRegistry(gateway.ts) owns oneArmingInterlockFSM and one signed MAVLink2FrameBuilderper vehicle. Every outbound command must clear the fail-closedcommand-gate(command-gate.ts): a disarm (armed: false) is always allowed; an arming/motor command is accepted only when the interlock isArmed, the e-stop isnominal, and the drone is inside the geofence — with the default verdict being reject.svc-assistant(§4.1) — goal decomposition, tool invocation, and free-form planning, every action mediated by a mandatorySafetyGate.svc-director— DroneDirector live composition scoring, a preference-learning loop, and the Yemaya shot-list → flight bridge.svc-perception— deterministic detection post-processing (NMS/thresholding/class-filter/bbox→world projection), a multi-object tracker, and map publishing.
§4.2 — coordination & application plane.
svc-fleet-orchestrator— the router (Open-RMF pattern,app.ts/auction.ts). It ingests pose/battery/capability, runs a CBBA market auction (the SGA / diminishing-marginal-gain consensus-based bundle algorithm, mirroringoya-fleet::auction), and reserves shared resources in space-time via aTrafficSchedule. It "deliberately exposes no endpoint that returns a motor command, velocity setpoint, or actuator value." The auction is fully deterministic (id-ordered tie-breaks) and validates its output againstAllocationSchemabefore returning — fail-loud if it ever produced an off-contract shape.svc-mission— mission CRUD over aMissionRepository(on an injectedQueryExecutor) plus energy-aware and follow-me planning.svc-energy— dock registry / charge broker, fleet SOC/SOH aggregation, and return-to-dock decisions.svc-dock— the dock network, per-slot charging FSMs, perch-routine planning, and battery-swap orchestration.svc-floorcare— coverage jobs, a dirt heatmap, a zone scheduler, and the omni-dock self-empty/refill cycle (overoya-floorcare).svc-home-map— the scene store, spatial query, change tracking, and privacy zones (the service face ofoya-scenegraph+@oya/privacy).svc-hri— hive-wide turn-taking floor arbitration, mic-array speaker localization, per-resident persona/trust, and per-embodiment affect.svc-manipulation— a grasp-confidence gate, an object-handoff coordinator, and per-embodiment VLA (vision-language-action) policy.svc-ambient— a Matter/Thread device registry (capability-validated command dispatch) and sensing escalation.svc-eldercare— multimodal fall-fusion with a trigger→confirm→escalate FSM, closed-loop medication adherence, engagement, and conversation.svc-pet— weight-based pet identification, per-pet calorie budgeting, behaviour, and navigation policy.
Most services keep their state in in-memory, dependency-free, injectable
collaborators so the same handler code runs under fastify.inject() in
tests and in production with no test-only branch; persistence is defined in
@oya/database and reached through injected executors where a service needs it
(e.g. svc-mission).
Cross-Cutting Invariants and Failure Modes#
A competent engineer working on Oya must respect a handful of invariants that the architecture enforces in more than one place:
- The hard capability gate. A task carries the single
Capabilityit requires; an agent may only bid on or win a task whose capability it holds. This rule lives identically in the contract (bidIsCapabilityValid), the TS auction (canService), andoya-fleet. Agrasptask can never be assigned to a non-grasp robot. - Coordination never actuates. The orchestrator assigns and reserves; only the flight gateway and on-robot control loops move motors, behind the fail-closed command gate. New endpoints must preserve this boundary.
- Fail-loud over fabrication. The world-model query union refuses to invent a confident answer for a decayed memory; the auction validates its own output against the schema; the readiness gates recompute invariants from first principles rather than trusting a claimed status. The sensor contract carries no raw frames, only descriptors.
- Determinism. Allocation, the gates, and the gateway decisions are pure,
clock-free functions — identical inputs yield identical outputs regardless of
ordering, which is what makes
fastify.inject()assertions exact. - Parity is enforced, not assumed. Rust ↔ wasm ↔ TS agree to 1e-12, gated
by
parity/check.mjs. Any change to engine math must keep the bridges and the@oya/coreoracle in lock-step. - Offline degradation & timing. The
offline-degradationgate requires every safety-critical loop to survive cloud loss;oya-comms::timesync(PTP/IEEE-1588) makes observations comparable across the hive's clocks.
Cross-Domain Integration Boundaries and Extension Points#
Oya owns flight/actuation authorisation, mission execution, hive coordination,
and robot safety policy; it consumes and exposes typed objects at its edges. The
clearest live boundary is the Yemaya shot-list → flight bridge in
svc-director, which turns a creative shot list into drone cinematography
trajectories (built on oya-cinematography). The @oshun/contracts/oya package
is the integration surface other domains import; the typed event catalogue in
@oya/event-publisher is how Oya broadcasts telemetry, mission lifecycle,
fleet-allocation changes, and e-stop events for cross-domain reaction.
Extension points follow the same grain: add a robot capability by extending the
Capability enum (contract) and the locomotion/manipulation crates; add a new
coordination behaviour as a §4.2 service on @oya/fastify-core with injectable
state; add an engine capability as a crate plus a bridge function (and a parity
oracle); add a release invariant as another @oya/readiness-gates evaluator
folded into the runAllGates conjunction.
Status: Implemented vs Legacy vs Planned#
- Implemented (real code, with tests): the 21-crate Rust engine, the
napi/wasm bridges and the parity differential test, the
@oshun/contracts/oyasurface, the TypeScript platform libraries (fastify-core,service-lib,database,event-publisher/handlers,privacy,security,maintenance,readiness-gates,common,sdk), and all 15apps/oya/services. - Legacy / prior generation (retained):
@oya/core(the 101-module TS drone library) and the single-file sibling readiness evaluators. These remain real and tested but are described in-code as the surface being migrated off of; the Rust engine is now authoritative and@oya/coreserves as the parity oracle. - Honest caveats: the FFI bridges expose a typed subset of the engine (the
geodesy/quaternion/ORCA/cinematography/min-snap functions read in
node-bridge/wasm-bridge), not the entire crate surface — much engine logic is exercised in-crate and via the services rather than re-exported across the boundary. Service state is largely in-memory and injectable by design;@oya/databasedefines the persistence schema and is wired where a service needs durability. This document is grounded in reading the source, not in running the build, so it asserts no pass/coverage metrics; the parity test and the readiness gates are the mechanisms the repo uses to keep those claims honest in CI.