Conduir Spatial — Technical Specification
Version: 1.0
Date: 2026-03-03
Status: Phase 1 Complete, Phases 2–6 Planned
1. Product Overview
Conduir Spatial is a 3D collaborative workspace where teams interact with interconnected concepts, tasks, decisions, and artifacts in a scene-graph environment. It supports desktop browsers, VR headsets (Meta Quest via WebXR), and voice interaction.
Core Value Proposition
- Spatial thinking: Move beyond flat 2D boards — arrange ideas in 3D space with depth, grouping, and proximity
- Multi-modal input: Mouse, keyboard, voice commands, VR controllers, hand tracking
- Real-time collaboration: Multiple participants see each other's cursors and edits via CRDT sync
- AI-augmented: LLM-backed natural language commands to manipulate the scene graph
2. Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ CLIENT (Browser / Quest) │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Three.js │ │ Zustand │ │ Yjs CRDT │ │
│ │ Canvas + XR │ │ Store │ │ Provider │ │
│ │ │ │ (truth) │ │ (sync) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ ┌──────┴─────────────────┴──────────────────┴───────┐ │
│ │ React Component Tree │ │
│ │ SpatialCanvas → SpatialScene → SceneNode/Edge │ │
│ │ SpatialHUD → Toolbar / Minimap / Chat / Roster │ │
│ └────────────────────────┬──────────────────────────┘ │
│ │ │
│ ┌────────────────────────┴──────────────────────────┐ │
│ │ Intent Pipeline │ │
│ │ Input → Classifier → MutationBus → Store → Render │ │
│ └────────────────────────┬──────────────────────────┘ │
└───────────────────────────┼─────────────────────────────────────────┘
│ HTTP/SSE
┌───────────────────────────┼─────────────────────────────────────────┐
│ NGINX (port 3100) │
│ /api/* → reverse proxy → :49815 │
│ /* → static /out │
└───────────────────────────┼─────────────────────────────────────────┘
│
┌───────────────────────────┼─────────────────────────────────────────┐
│ BACKEND (FastAPI, port 49815) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ SessionStore │ │ Neo4jScene │ │ NATS │ │
│ │ (SQLite WAL) │ │ Store │ │ Broadcaster │ │
│ │ metadata │ │ scene CRUD │ │ pub/sub │ │
│ └──────┬────────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ spatial_sessions.db Neo4j :20687 NATS :44222 │
└─────────────────────────────────────────────────────────────────────┘Tech Stack
| Layer | Technology | Version |
|---|---|---|
| 3D Engine | Three.js | 0.183 |
| React Bindings | React Three Fiber | 9.5 |
| Helpers | React Three Drei | 10.7 |
| VR/AR | React Three XR | 6.6 |
| Framework | Next.js (static export) | 16.0 |
| State | Zustand (persist) | 5.0 |
| CRDT | Yjs | 13.6 |
| CSS | Tailwind CSS | 4.2 |
| Backend | FastAPI + Uvicorn | 0.115 |
| Graph DB | Neo4j | 5.26+ |
| Message Bus | NATS JetStream | 2.9+ |
| Session DB | SQLite (aiosqlite) | 0.21+ |
3. Data Model
3.1 Scene Graph
The scene graph is the central data structure — a directed graph of typed nodes and edges in 3D space.
SceneGraph
├── nodes: Record<string, SceneNode>
│ ├── id: string (nanoid)
│ ├── type: concept | task | agent | person | artifact | decision | group | annotation
│ ├── label: string
│ ├── description?: string
│ ├── position: Vec3 { x, y, z }
│ ├── rotation: Vec3
│ ├── scale: Vec3
│ ├── style: { color, opacity, shape, emissive? }
│ ├── parentId?: string (hierarchy)
│ ├── metadata: Record<string, unknown>
│ ├── createdBy: string
│ └── createdAt: ISO string
│
└── edges: Record<string, SceneEdge>
├── id: string (nanoid)
├── sourceId: string
├── targetId: string
├── type: dependency | association | flow | hierarchy | reference
├── label?: string
├── style: { color, width, dashed, animated }
├── metadata: Record<string, unknown>
├── createdBy: string
└── createdAt: ISO string3.2 Node Visual Mapping
| Node Type | Default Shape | Default Color | Use Case |
|---|---|---|---|
| concept | hexagon | indigo #6366f1 | Ideas, topics, themes |
| task | box | green #22c55e | Action items, work units |
| agent | sphere | purple #a855f7 | AI personas |
| person | sphere | blue #3b82f6 | Human participants |
| artifact | card | amber #f59e0b | Files, docs, services |
| decision | diamond | red #ef4444 | Decision records |
| group | box | gray #6b7280 | Container/folder |
| annotation | card | slate #64748b | Notes, comments |
3.3 Session Model
SpatialSession
├── id: string (nanoid)
├── name: string
├── description?: string
├── status: active | archived | deleted
├── templateId?: string
├── createdBy: string
├── createdAt: ISO string
├── updatedAt: ISO string
├── participantCount: number
└── nodeCount: number3.4 Participant Model
Participant
├── id: string
├── name: string
├── role: owner | editor | viewer
├── isAgent: boolean
├── provider?: claude | openai | gemini
├── avatarUrl?: string
├── cursorPosition?: Vec3
├── selectedNodeId?: string
├── color: string (hex)
├── joinedAt: ISO string
└── lastActiveAt: ISO string4. State Management
Zustand Store (spatialStore)
Persisted (localStorage): showMinimap, showChat, showParticipants, gridVisible
Transient (runtime):
| State | Type | Description |
|---|---|---|
| session | SpatialSession | null | Current session metadata |
| nodes | Record<string, SceneNode> | Scene graph nodes |
| edges | Record<string, SceneEdge> | Scene graph edges |
| participants | Record<string, Participant> | Active participants |
| toolMode | ToolMode | Current interaction tool |
| selectedNodeId | string | null | Currently selected node |
| hoveredNodeId | string | null | Currently hovered node |
| connectSourceId | string | null | First node during connect |
| mutationHistory | SceneMutation[] | Undo stack (max 200) |
| mutationFuture | SceneMutation[] | Redo stack |
Mutation History
All scene modifications are tracked as SceneMutation objects:
- Bounded: Max 200 entries (oldest evicted)
- Undo: Pops from history, pushes to future, applies inverse operation
- Redo: Pops from future, pushes to history, re-applies operation
- New mutation: Clears future stack (no branching)
5. Intent Pipeline
User Input Intent Classifier Mutation Bus Store
────────── ───────────────── ──────────── ─────
"add task auth" ──→ { type: "add_node", ──→ addNode({ ──→ nodes[id] = node
nodeType: "task", type: "task", history.push(mutation)
label: "auth" } label: "auth",
position: random() })
"connect auth ──→ { type: "connect", ──→ addEdge({ ──→ edges[id] = edge
to database" from: "auth", sourceId: found("auth"),
to: "database" } targetId: found("database") })Supported Commands (Phase 1 — regex)
| Pattern | Intent | Action |
|---|---|---|
add <type> <label> | add_node | Creates node at random position |
create <type> <label> | add_node | Alias |
connect <A> to <B> | connect | Creates edge between nodes (label match) |
link <A> and <B> | connect | Alias |
delete <target> | delete | Removes node + connected edges |
highlight <target> [in <color>] | highlight | Sets emissive glow |
annotate <target> with <text> | annotate | Creates annotation node nearby |
layout / arrange | layout | Runs force-directed layout (Phase 5) |
undo / redo | undo/redo | History navigation |
| anything else | chat | Passed to chat panel |
Phase 4+ (LLM-Backed)
Complex natural language will route through the LLM Gateway (local-service) for semantic understanding, entity extraction, and multi-step scene manipulation.
6. 3D Rendering
Scene Composition
<Canvas> (React Three Fiber)
├── XR Store (controllers, hands, gaze)
├── <SpatialScene>
│ ├── Ambient Light (0.4)
│ ├── Directional Light (0.8, shadow 2048x2048)
│ ├── Point Light (at [5,8,5])
│ ├── Stars (2000 particles)
│ ├── Fog (near: 30, far: 80)
│ ├── InfiniteGrid (100x100, fade)
│ ├── <EdgeLine> per edge (Bezier + arrowhead)
│ ├── <SceneNodeMesh> per non-annotation node
│ │ ├── Geometry (Sphere/Box/Diamond/Hexagon/Card)
│ │ ├── Material (color, emissive, roughness, metalness)
│ │ ├── Label (Billboard text)
│ │ └── Selection Ring (white wireframe)
│ ├── <Annotation> per annotation node (Billboard card)
│ └── <AgentAvatar> per AI participant (floating orb)
│
├── <SpatialHUD> (HTML overlay)
│ ├── Top Bar (session info, toggle buttons)
│ ├── SpatialToolbar (left, tool modes + palette)
│ ├── SpatialMinimap (bottom-right, SVG)
│ ├── SpatialChatPanel (right slide-out)
│ └── ParticipantList (bottom-right)
│
└── <DesktopControls>
├── OrbitControls (damped, constrained)
└── Keyboard handlers (Ctrl+Z, G, Escape)Camera
- Position: [0, 8, 12]
- FOV: 60 degrees
- Controls: OrbitControls with damping
- Min distance: 3
- Max distance: 50
- Max polar angle: ~80 degrees (prevent going below grid)
- Enabled only in select/highlight modes
Node Interaction
| Tool Mode | Click Node | Click Void |
|---|---|---|
| select | Select node | Deselect |
| add | — | Create node at position |
| connect | Set source, then target → create edge | Cancel connect |
| annotate | Create annotation near node | Create annotation at click |
| highlight | Toggle emissive glow | — |
| delete | Remove node + edges | — |
7. WebXR Integration
Hardware Support
- Meta Quest 2/3/Pro (primary target)
- Any WebXR-capable browser (Chrome, Edge)
XR Features (Current)
@react-three/xrv6.6 withcreateXRStore()- Hand ray pointers + controller input
- Gaze interaction fallback
- VR entry via HUD button
XR Features (Phase 6 Planned)
- XRPanel: Floating 2D UI panels in VR space
- XRTeleport: Point-and-click locomotion
- Hand gestures: Pinch to select, grab to move, swipe to navigate
- Spatial audio: Positional audio cues for collaboration
8. Voice Integration
Speech-to-Text (useSTT)
- Engine: Web Speech Recognition API (SpeechRecognition / webkitSpeechRecognition)
- Mode: Non-continuous, interim results enabled
- Language: en-US (configurable)
- Flow: Press mic → speak → release → final transcript → intent classifier
Text-to-Speech (useTTS)
- Engine: Web Speech Synthesis API
- Processing: Strips markdown before speaking
- Config: Rate 1.0, pitch 1.0
- Use: Read back AI responses, confirmations
9. Templates
Four built-in scene generators:
| Template | Nodes | Edges | Layout | Purpose |
|---|---|---|---|---|
| Whiteboard | 0 | 0 | Empty | Freeform ideation |
| Architecture | 8 | 7 | 3-tier horizontal | System design review |
| Knowledge Graph | 8 | 12 | Radial | Topic exploration |
| Timeline | 6 | 5 | Linear X-axis | Project planning |
10. Backend API
Port: 49815 (proxied via nginx :3100/api)
Planned Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/sessions | Create new session |
| GET | /api/sessions | List active sessions |
| GET | /api/sessions/:id | Get session details |
| PATCH | /api/sessions/:id | Archive/update session |
| GET | /api/sessions/:id/scene | Load scene graph |
| PUT | /api/sessions/:id/scene | Save scene graph |
| DELETE | /api/sessions/:id/scene | Delete scene |
| POST | /api/sessions/:id/mutations | Apply mutation (broadcast via NATS) |
| POST | /api/chat | SSE endpoint for LLM interaction |
Persistence
| Store | Technology | Data |
|---|---|---|
| SessionStore | SQLite WAL | Session metadata, status, counts |
| Neo4jSceneStore | Neo4j | Full scene graphs (nodes, edges, relationships) |
| NATSBroadcaster | NATS JetStream | Real-time mutation streaming |
Neo4j Schema
(:SpatialSession {id})
-[:HAS_SCENE]->(:SpatialSceneGraph {session_id})
-[:CONTAINS]->(:SpatialSceneNode {id, type, label, ...})
(:SpatialSceneNode)-[:SCENE_EDGE {id, type, label, ...}]->(:SpatialSceneNode)NATS Stream
- Stream:
SPATIAL_SYNC - Subjects:
spatial.{sessionId}.mutations - Retention: limits, max 100k messages, max 24h age
11. Deployment
Static Frontend
cd conduir-spatial
npm run build # → /out directory
# Nginx serves /out on port 3100Backend
cd conduir-spatial/server
uvicorn spatial_channel:app --host 0.0.0.0 --port 49815Infrastructure Dependencies
- Neo4j: bolt://local-service
- NATS JetStream: nats://local-service
- LLM Gateway: local-service (Phase 4+)
- Symposium: local-service (Phase 4+)
12. Security Considerations
- No auth yet — sessions are open by default
- Phase 2+: Integrate with Maestro auth (JWT from API on :44100)
- CSP: X-Frame-Options DENY, X-Content-Type-Options nosniff
- CORS: Backend should restrict to known origins
- Input sanitization: Node labels/descriptions must be sanitized before rendering in Three.js Text components