Grand Diomande Research ยท Full HTML Reader

TrajectoryOS Desktop: Rust + Tauri Implementation

**Status**: ๐Ÿ”ฎ **Planned** - Complete implementation proposal, ready to start **Last Updated**: December 21, 2025 **Alternative to**: Native macOS approach ([see comparison](TRAJECTORYOS_DESKTOP_PROPOSAL.md)) **Integration**: Works with existing [trajectory-core](core/cc-trajectory/services/trajectory-core/README.md) backend

Embodied Trajectory Systems technical note backlog reference score 26 .md

Full Public Reader

TrajectoryOS Desktop: Rust + Tauri Implementation

Status: ๐Ÿ”ฎ Planned - Complete implementation proposal, ready to start
Last Updated: December 21, 2025
Alternative to: Native macOS approach ([see comparison](TRAJECTORYOS_DESKTOP_PROPOSAL.md))
Integration: Works with existing [trajectory-core](core/cc-trajectory/services/trajectory-core/README.md) backend

---

Quick Navigation:
- [Why Tauri?](#-why-tauri-over-native-macos) - Decision rationale
- [15-Min Setup](#-getting-started-no-apple-id) - Get running fast
- [Architecture](#-tauri-architecture) - System design
- [Implementation](#-rust-backend-implementation) - Code examples
- [vs Native](#tauri-vs-electron-vs-native) - Platform comparison

---

๐ŸŽฏ Executive Summary

This document proposes implementing TrajectoryOS Desktop using Rust + Tauri, a modern framework that combines:
- Rust Backend: High-performance native code
- Web Frontend: React/TypeScript UI
- Native Webview: OS-native rendering (not Chromium)
- Cross-Platform: Windows, macOS, Linux from one codebase

Key Advantage: No Apple ID required for development - can develop on any platform.

---

๐Ÿค” Why Tauri over Native macOS?

Your Constraint: Apple ID Issue

Problem: Out of Apple IDs, need to wait for Xcode development
Solution: Tauri development works on any OS (Windows, Linux, macOS)

Development Options:
1. Develop on Windows/Linux โ†’ Build for all platforms
2. Develop on macOS without Xcode โ†’ Use VS Code + Rust
3. Cloud Development โ†’ Use GitHub Codespaces

Tauri vs Electron vs Native

FeatureTauriElectronNative macOS
Bundle Size3-10 MB150+ MB5-15 MB
Memory Usage50-100 MB200-500 MB30-80 MB
Startup TimeFastSlowFastest
Cross-Platformโœ… Win/Mac/Linuxโœ… Win/Mac/LinuxโŒ macOS only
No Apple IDโœ… Yesโœ… YesโŒ Needs Xcode
Code Reuse (iOS)โŒ 0
Development SpeedMediumFastFast (if Swift)
Backend LanguageRustNode.jsSwift
FrontendWebWebSwiftUI

Tauri Sweet Spot: Cross-platform + small size + no Apple dependency

---

๐Ÿ—๏ธ Tauri Architecture

Tech Stack

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         Frontend (Web)                   โ”‚
โ”‚  React + TypeScript + Vite              โ”‚
โ”‚  - UI Components                        โ”‚
โ”‚  - State Management (Zustand)           โ”‚
โ”‚  - Charts (Recharts)                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚ IPC (Commands/Events)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         Backend (Rust)                   โ”‚
โ”‚  Tauri Core                             โ”‚
โ”‚  - Life Physics Engine                  โ”‚
โ”‚  - Ring Memory System                   โ”‚
โ”‚  - Embeddings (TF-IDF)                  โ”‚
โ”‚  - SQLite Persistence                   โ”‚
โ”‚  - File I/O, System APIs                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚ Optional
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   External Backend (TypeScript)          โ”‚
โ”‚   - RAG++ Services                      โ”‚
โ”‚   - IRCP Embeddings                     โ”‚
โ”‚   - PostgreSQL                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Frontend Stack

json
{
  "frontend": {
    "framework": "React 18",
    "language": "TypeScript",
    "build": "Vite",
    "state": "Zustand",
    "ui": "Tailwind CSS + shadcn/ui",
    "charts": "Recharts",
    "routing": "React Router"
  }
}

Backend Stack

toml
[dependencies]
tauri = "1.5"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.35", features = ["full"] }
rusqlite = { version = "0.30", features = ["bundled"] }
reqwest = { version = "0.11", features = ["json"] }
chrono = "0.4"
nalgebra = "0.32"  # For vector operations

---

๐Ÿ“ Project Structure

trajectoryos-desktop/
โ”œโ”€โ”€ src-tauri/              # Rust backend
โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”œโ”€โ”€ main.rs         # Tauri app entry
โ”‚   โ”‚   โ”œโ”€โ”€ commands/       # Tauri commands (IPC handlers)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ physics.rs
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ ring_memory.rs
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ embeddings.rs
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ recommendations.rs
โ”‚   โ”‚   โ”œโ”€โ”€ models/         # Rust data models
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ life_physics.rs
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ ring_memory.rs
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ transition.rs
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ skill.rs
โ”‚   โ”‚   โ”œโ”€โ”€ services/       # Business logic
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ physics_service.rs
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ embedding_service.rs
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ policy_suggester.rs
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ database.rs
โ”‚   โ”‚   โ””โ”€โ”€ utils/
โ”‚   โ”œโ”€โ”€ Cargo.toml
โ”‚   โ””โ”€โ”€ tauri.conf.json     # Tauri configuration
โ”œโ”€โ”€ src/                    # React frontend
โ”‚   โ”œโ”€โ”€ components/
โ”‚   โ”‚   โ”œโ”€โ”€ Dashboard.tsx
โ”‚   โ”‚   โ”œโ”€โ”€ Timeline.tsx
โ”‚   โ”‚   โ”œโ”€โ”€ Analytics.tsx
โ”‚   โ”‚   โ”œโ”€โ”€ Skills.tsx
โ”‚   โ”‚   โ””โ”€โ”€ Projects.tsx
โ”‚   โ”œโ”€โ”€ hooks/
โ”‚   โ”‚   โ”œโ”€โ”€ usePhysics.ts
โ”‚   โ”‚   โ”œโ”€โ”€ useRecommendations.ts
โ”‚   โ”‚   โ””โ”€โ”€ useRingMemory.ts
โ”‚   โ”œโ”€โ”€ store/              # Zustand state
โ”‚   โ”‚   โ”œโ”€โ”€ physicsStore.ts
โ”‚   โ”‚   โ””โ”€โ”€ settingsStore.ts
โ”‚   โ”œโ”€โ”€ lib/                # Utilities
โ”‚   โ”‚   โ”œโ”€โ”€ tauri.ts        # Tauri IPC wrapper
โ”‚   โ”‚   โ””โ”€โ”€ charts.ts
โ”‚   โ”œโ”€โ”€ App.tsx
โ”‚   โ””โ”€โ”€ main.tsx
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ tsconfig.json
โ””โ”€โ”€ vite.config.ts

---

๐Ÿฆ€ Rust Backend Implementation

1. Life Physics Engine (Rust)

File: `src-tauri/src/models/life_physics.rs`

rust
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LifePhysics {
    pub thrust: f64,      // T: Productive power
    pub alignment: f64,   // A: Coherence (0-1)
    pub gravity: f64,     // G: External pressures
    pub mass: f64,        // M: System complexity
}

impl LifePhysics {
    pub fn escape_index(&self) -> f64 {
        (self.thrust * self.alignment) / (self.gravity * self.mass)
    }

    pub fn regime(&self) -> Regime {
        let eta = self.escape_index();

        if eta < 0.5 {
            Regime::Falling
        } else if eta < 0.8 {
            Regime::Approaching
        } else if eta < 1.2 {
            Regime::Threshold
        } else if eta < 1.5 {
            Regime::Escaping
        } else {
            Regime::Free
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Regime {
    Falling,
    Approaching,
    Threshold,
    Escaping,
    Free,
}

2. Ring Memory System (Rust)

File: `src-tauri/src/models/ring_memory.rs`

rust
use std::collections::HashMap;
use uuid::Uuid;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryNode {
    pub id: Uuid,
    pub physics: LifePhysics,
    pub timestamp: i64,
    pub next_id: Option<Uuid>,
    pub prev_id: Option<Uuid>,
    pub delta_eta: f64,
    pub successful: bool,
}

pub struct RingMemory {
    nodes: HashMap<Uuid, MemoryNode>,
    head_id: Option<Uuid>,
    tail_id: Option<Uuid>,
    max_capacity: usize,
}

impl RingMemory {
    pub fn new(max_capacity: usize) -> Self {
        Self {
            nodes: HashMap::new(),
            head_id: None,
            tail_id: None,
            max_capacity,
        }
    }

    pub fn add_node(&mut self, physics: LifePhysics, delta_eta: f64, successful: bool) -> Uuid {
        let node_id = Uuid::new_v4();

        let mut node = MemoryNode {
            id: node_id,
            physics,
            timestamp: chrono::Utc::now().timestamp(),
            next_id: None,
            prev_id: self.tail_id,
            delta_eta,
            successful,
        };

        // Link to tail
        if let Some(tail_id) = self.tail_id {
            if let Some(tail) = self.nodes.get_mut(&tail_id) {
                tail.next_id = Some(node_id);
            }
        }

        // Update head if empty
        if self.head_id.is_none() {
            self.head_id = Some(node_id);
        }

        // Add node
        self.nodes.insert(node_id, node);
        self.tail_id = Some(node_id);

        // Evict if over capacity
        if self.nodes.len() > self.max_capacity {
            self.evict_oldest();
        }

        node_id
    }

    pub fn find_similar(&self, target: &LifePhysics, limit: usize) -> Vec<(Uuid, f64)> {
        let mut scored: Vec<(Uuid, f64)> = self.nodes.iter()
            .map(|(id, node)| {
                let similarity = self.compute_similarity(target, &node.physics);
                (*id, similarity)
            })
            .collect();

        scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
        scored.truncate(limit);
        scored
    }

    fn compute_similarity(&self, a: &LifePhysics, b: &LifePhysics) -> f64 {
        // Euclidean distance in normalized physics space
        let dt = (a.thrust - b.thrust) / a.thrust.max(1.0);
        let da = (a.alignment - b.alignment);
        let dg = (a.gravity - b.gravity) / a.gravity.max(1.0);
        let dm = (a.mass - b.mass) / a.mass.max(1.0);

        let dist = (dt * dt + da * da + dg * dg + dm * dm).sqrt();

        // Convert distance to similarity (0-1)
        1.0 / (1.0 + dist)
    }

    fn evict_oldest(&mut self) {
        if let Some(head_id) = self.head_id {
            if let Some(head) = self.nodes.remove(&head_id) {
                self.head_id = head.next_id;

                if let Some(new_head_id) = self.head_id {
                    if let Some(new_head) = self.nodes.get_mut(&new_head_id) {
                        new_head.prev_id = None;
                    }
                }
            }
        }
    }
}

3. Tauri Commands (IPC Handlers)

File: `src-tauri/src/commands/physics.rs`

rust
use tauri::State;
use std::sync::Mutex;
use crate::models::LifePhysics;
use crate::services::PhysicsService;

#[tauri::command]
pub async fn compute_physics(
    service: State<'_, Mutex<PhysicsService>>
) -> Result<LifePhysics, String> {
    let service = service.lock().map_err(|e| e.to_string())?;

    service.compute_current_physics()
        .map_err(|e| e.to_string())
}

#[tauri::command]
pub async fn get_escape_index(
    physics: LifePhysics
) -> Result<f64, String> {
    Ok(physics.escape_index())
}

#[tauri::command]
pub async fn get_regime(
    physics: LifePhysics
) -> Result<String, String> {
    Ok(format!("{:?}", physics.regime()))
}

File: `src-tauri/src/commands/ring_memory.rs`

rust
use tauri::State;
use std::sync::Mutex;
use uuid::Uuid;
use crate::models::{RingMemory, LifePhysics, MemoryNode};

#[tauri::command]
pub async fn add_memory_node(
    ring_memory: State<'_, Mutex<RingMemory>>,
    physics: LifePhysics,
    delta_eta: f64,
    successful: bool
) -> Result<String, String> {
    let mut memory = ring_memory.lock().map_err(|e| e.to_string())?;

    let node_id = memory.add_node(physics, delta_eta, successful);
    Ok(node_id.to_string())
}

#[tauri::command]
pub async fn find_similar_states(
    ring_memory: State<'_, Mutex<RingMemory>>,
    target: LifePhysics,
    limit: usize
) -> Result<Vec<MemoryNode>, String> {
    let memory = ring_memory.lock().map_err(|e| e.to_string())?;

    let similar_ids = memory.find_similar(&target, limit);

    let nodes: Vec<MemoryNode> = similar_ids.iter()
        .filter_map(|(id, _similarity)| memory.nodes.get(id).cloned())
        .collect();

    Ok(nodes)
}

4. Main Entry Point

File: `src-tauri/src/main.rs`

rust
#![cfg_attr(
    all(not(debug_assertions), target_os = "windows"),
    windows_subsystem = "windows"
)]

mod commands;
mod models;
mod services;
mod utils;

use std::sync::Mutex;
use commands::{physics, ring_memory, embeddings, recommendations};
use services::{PhysicsService, RingMemory};

fn main() {
    tauri::Builder::default()
        .manage(Mutex::new(PhysicsService::new()))
        .manage(Mutex::new(RingMemory::new(20_000))) // 20K nodes
        .invoke_handler(tauri::generate_handler![
            // Physics commands
            physics::compute_physics,
            physics::get_escape_index,
            physics::get_regime,

            // Ring memory commands
            ring_memory::add_memory_node,
            ring_memory::find_similar_states,

            // Embedding commands
            embeddings::embed_text,
            embeddings::compute_similarity,

            // Recommendation commands
            recommendations::get_recommendations,
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

---

โš›๏ธ React Frontend Implementation

1. Tauri IPC Wrapper

File: `src/lib/tauri.ts`

typescript
import { invoke } from '@tauri-apps/api/tauri';

export interface LifePhysics {
  thrust: number;
  alignment: number;
  gravity: number;
  mass: number;
}

export interface MemoryNode {
  id: string;
  physics: LifePhysics;
  timestamp: number;
  deltaEta: number;
  successful: boolean;
}

export interface Recommendation {
  action: string;
  confidence: number;
  reasoning: string;
}

// Physics API
export async function computePhysics(): Promise<LifePhysics> {
  return await invoke('compute_physics');
}

export async function getEscapeIndex(physics: LifePhysics): Promise<number> {
  return await invoke('get_escape_index', { physics });
}

export async function getRegime(physics: LifePhysics): Promise<string> {
  return await invoke('get_regime', { physics });
}

// Ring Memory API
export async function addMemoryNode(
  physics: LifePhysics,
  deltaEta: number,
  successful: boolean
): Promise<string> {
  return await invoke('add_memory_node', { physics, deltaEta, successful });
}

export async function findSimilarStates(
  target: LifePhysics,
  limit: number
): Promise<MemoryNode[]> {
  return await invoke('find_similar_states', { target, limit });
}

// Recommendations API
export async function getRecommendations(): Promise<Recommendation[]> {
  return await invoke('get_recommendations');
}

2. Dashboard Component

File: `src/components/Dashboard.tsx`

typescript
import React, { useEffect, useState } from 'react';
import { computePhysics, getEscapeIndex, getRecommendations } from '../lib/tauri';
import type { LifePhysics, Recommendation } from '../lib/tauri';

export function Dashboard() {
  const [physics, setPhysics] = useState<LifePhysics | null>(null);
  const [escapeIndex, setEscapeIndex] = useState<number | null>(null);
  const [recommendations, setRecommendations] = useState<Recommendation[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    loadData();
  }, []);

  async function loadData() {
    setLoading(true);
    try {
      const p = await computePhysics();
      setPhysics(p);

      const eta = await getEscapeIndex(p);
      setEscapeIndex(eta);

      const recs = await getRecommendations();
      setRecommendations(recs);
    } catch (error) {
      console.error('Failed to load data:', error);
    } finally {
      setLoading(false);
    }
  }

  if (loading) {
    return <div className="flex items-center justify-center h-screen">
      <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-purple-500" />
    </div>;
  }

  return (
    <div className="p-6 space-y-6">
      {/* Escape Index Card */}
      <div className="bg-white rounded-lg shadow p-6">
        <h2 className="text-2xl font-bold mb-4">Escape Index</h2>
        <div className="text-6xl font-bold text-purple-600">
          {escapeIndex?.toFixed(2)}
        </div>
        <div className="text-gray-600 mt-2">
          {getRegimeText(escapeIndex)}
        </div>
      </div>

      {/* Physics Components */}
      <div className="grid grid-cols-2 gap-4">
        <PhysicsCard label="Thrust" value={physics?.thrust} color="blue" />
        <PhysicsCard label="Alignment" value={physics?.alignment} color="green" />
        <PhysicsCard label="Gravity" value={physics?.gravity} color="red" />
        <PhysicsCard label="Mass" value={physics?.mass} color="yellow" />
      </div>

      {/* Recommendations */}
      <div className="bg-white rounded-lg shadow p-6">
        <h2 className="text-2xl font-bold mb-4">Recommendations</h2>
        <div className="space-y-3">
          {recommendations.map((rec, i) => (
            <RecommendationCard key={i} recommendation={rec} />
          ))}
        </div>
      </div>

      {/* Refresh Button */}
      <button
        onClick={loadData}
        className="w-full bg-purple-600 text-white py-3 rounded-lg hover:bg-purple-700"
      >
        Refresh
      </button>
    </div>
  );
}

function PhysicsCard({ label, value, color }: any) {
  return (
    <div className="bg-white rounded-lg shadow p-4">
      <div className="text-sm text-gray-600">{label}</div>
      <div className={`text-3xl font-bold text-${color}-600`}>
        {value?.toFixed(2)}
      </div>
    </div>
  );
}

function RecommendationCard({ recommendation }: any) {
  return (
    <div className="border border-gray-200 rounded-lg p-4">
      <div className="flex items-start justify-between">
        <div className="flex-1">
          <div className="font-semibold">{recommendation.action}</div>
          <div className="text-sm text-gray-600 mt-1">
            {recommendation.reasoning}
          </div>
        </div>
        <div className="text-sm font-semibold text-purple-600">
          {(recommendation.confidence * 100).toFixed(0)}%
        </div>
      </div>
    </div>
  );
}

function getRegimeText(eta: number | null): string {
  if (eta === null) return '';
  if (eta < 0.5) return 'Falling';
  if (eta < 0.8) return 'Approaching';
  if (eta < 1.2) return 'Threshold';
  if (eta < 1.5) return 'Escaping';
  return 'Free';
}

3. State Management (Zustand)

File: `src/store/physicsStore.ts`

typescript
import { create } from 'zustand';
import type { LifePhysics, Recommendation } from '../lib/tauri';
import { computePhysics, getRecommendations } from '../lib/tauri';

interface PhysicsStore {
  physics: LifePhysics | null;
  recommendations: Recommendation[];
  loading: boolean;
  error: string | null;

  refresh: () => Promise<void>;
}

export const usePhysicsStore = create<PhysicsStore>((set) => ({
  physics: null,
  recommendations: [],
  loading: false,
  error: null,

  refresh: async () => {
    set({ loading: true, error: null });
    try {
      const physics = await computePhysics();
      const recommendations = await getRecommendations();
      set({ physics, recommendations, loading: false });
    } catch (error) {
      set({ error: (error as Error).message, loading: false });
    }
  },
}));

---

๐Ÿ”ง Development Setup

Prerequisites

bash
# 1. Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 2. Install Node.js (v18+)
# Download from nodejs.org

# 3. Install Tauri CLI
cargo install tauri-cli

# Or via npm
npm install -g @tauri-apps/cli

Project Setup

bash
# Create new Tauri project
npm create tauri-app@latest

# Project name: trajectoryos-desktop
# Choose template: React + TypeScript
# Use Vite

cd trajectoryos-desktop

# Install dependencies
npm install

# Install additional packages
npm install zustand recharts @tanstack/react-query
npm install -D @types/node

Development Commands

bash
# Start development server
npm run tauri dev

# Build for production
npm run tauri build

# Build for specific platform
npm run tauri build -- --target x86_64-pc-windows-msvc  # Windows
npm run tauri build -- --target x86_64-apple-darwin     # macOS Intel
npm run tauri build -- --target aarch64-apple-darwin    # macOS ARM
npm run tauri build -- --target x86_64-unknown-linux-gnu # Linux

Cross-Platform Build (GitHub Actions)

File: `.github/workflows/build.yml`

yaml
name: Build

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]

    runs-on: ${{ matrix.os }}

    steps:
    - uses: actions/checkout@v3

    - name: Setup Node
      uses: actions/setup-node@v3
      with:
        node-version: '18'

    - name: Setup Rust
      uses: dtolnay/rust-toolchain@stable

    - name: Install dependencies (Ubuntu)
      if: matrix.os == 'ubuntu-latest'
      run: |
        sudo apt-get update
        sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev \
          libappindicator3-dev librsvg2-dev patchelf

    - name: Install frontend dependencies
      run: npm install

    - name: Build Tauri app
      run: npm run tauri build

    - name: Upload artifacts
      uses: actions/upload-artifact@v3
      with:
        name: ${{ matrix.os }}-build
        path: src-tauri/target/release/bundle/

---

๐Ÿ“ฆ Deployment & Distribution

Windows

Output: `trajectoryos-desktop_0.1.0_x64_en-US.msi`

Size: ~5-8 MB

Distribution:
- Direct download from website
- Microsoft Store (optional)
- Winget package manager

macOS

Output: `TrajectoryOS.app` (DMG)

Size: ~4-7 MB

Distribution:
- Direct download (requires notarization)
- Mac App Store (requires Apple Developer account)
- Homebrew (via cask)

Note: For App Store, you'll need Apple ID, but for direct distribution, you can build on Linux and sign later.

Linux

Outputs:
- `.deb` (Debian/Ubuntu)
- `.AppImage` (Universal)
- `.rpm` (Fedora/RHEL)

Size: ~4-6 MB

Distribution:
- Direct download
- Snap Store
- Flatpak
- AUR (Arch User Repository)

---

๐Ÿ”„ Backend Integration

Option 1: Embedded Backend (Rust)

Reimplement core logic in Rust:

rust
// Fully self-contained
// No external backend needed
// Fast, lightweight

Option 2: Call TypeScript Backend

Use existing Node.js services:

rust
// src-tauri/src/services/api_client.rs

use reqwest;
use serde::{Deserialize, Serialize};

pub struct APIClient {
    base_url: String,
    client: reqwest::Client,
}

impl APIClient {
    pub fn new(base_url: String) -> Self {
        Self {
            base_url,
            client: reqwest::Client::new(),
        }
    }

    pub async fn get_recommendations(&self) -> Result<Vec<Recommendation>, reqwest::Error> {
        let url = format!("{}/api/ragpp/recommend", self.base_url);

        self.client
            .get(&url)
            .send()
            .await?
            .json()
            .await
    }
}

#[tauri::command]
pub async fn get_recommendations_from_backend(
    api_client: State<'_, Mutex<APIClient>>
) -> Result<Vec<Recommendation>, String> {
    let client = api_client.lock().map_err(|e| e.to_string())?;

    client.get_recommendations()
        .await
        .map_err(|e| e.to_string())
}

Option 3: Hybrid

Local operations: Fast, offline-capable
Advanced features: Call backend when online

typescript
// Frontend decides
const useBackend = navigator.onLine && hasBackendConfig;

const recommendations = useBackend
  ? await getRecommendationsFromBackend()
  : await getRecommendationsLocal();

---

๐Ÿ’ฐ Cost Comparison

Tauri Development

Costs:
- Development: 120-160 hours ร— $100/hr = **$12,000-16,000
-
No Apple fees (can develop on Linux/Windows)
-
CI/CD: GitHub Actions (free for public repos)
-
Distribution**: Free (direct download)

Total First Year: ~$16,000 (one-time)

vs Native macOS

Native macOS:
- Development: $11,000
- Requires: Apple Developer account ($99/year)
- Requires: Mac for development ($1,000+)
- Platform: macOS only

Tauri:
- Development: $12,000-16,000
- Requires: Any OS (can use existing machine)
- Platform: Windows + macOS + Linux

Winner: Tauri (if you need cross-platform or don't have Mac/Apple ID)

---

โšก Performance Expectations

Bundle Size

PlatformTauriElectronNative
Windows6 MB180 MBN/A
macOS5 MB170 MB10 MB
Linux7 MB185 MBN/A

Memory Usage

Tauri:    50-100 MB (native webview)
Electron: 200-500 MB (bundled Chromium)
Native:   30-80 MB (pure native)

Startup Time

Tauri:    500-800ms
Electron: 1-2 seconds
Native:   200-400ms

---

๐ŸŽฏ Recommended Approach for Your Situation

Given: Apple ID Constraint

Best Option: Rust + Tauri

Development Plan:

1. Phase 1: Rust Core (40 hours)
- Life physics engine
- Ring memory system
- SQLite persistence
- TF-IDF embeddings

2. Phase 2: React Frontend (40 hours)
- Dashboard view
- Timeline view
- Analytics view
- Settings

3. Phase 3: Integration (30 hours)
- Tauri IPC setup
- State synchronization
- Error handling
- Testing

4. Phase 4: Cross-Platform Build (20 hours)
- Windows build
- macOS build (can build on Linux)
- Linux build
- CI/CD setup

Total: 130 hours (~3-4 weeks)

Platform: Develop on Windows or Linux (no Mac/Apple ID needed)

Output: Windows, macOS, Linux apps from one codebase

---

๐Ÿ“Š Feature Parity

FeatureiOS (Swift)Tauri (Rust)Complexity
Life Physicsโœ…โœ… EasyLow
Ring Memoryโœ…โœ… EasyMedium
Embeddingsโœ… TF-IDFโœ… TF-IDFMedium
SwiftDataโœ…โŒ โ†’ SQLiteMedium
RAG++โœ…โœ…High
UISwiftUIReactDifferent
CloudKit Syncโœ…โŒN/A

Can Achieve: 95

Cannot Do: iOS-specific features (CloudKit, Handoff, etc.)

---

๐Ÿš€ Getting Started (No Apple ID)

Step 1: Install Tools (Windows/Linux)

bash
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Install Node.js
# Download from nodejs.org

# Install Tauri CLI
cargo install tauri-cli

# Verify
cargo --version
node --version
cargo tauri --version

Step 2: Create Project

bash
# Create new project
npm create tauri-app@latest trajectoryos-desktop

# Select:
# - Framework: React
# - Language: TypeScript
# - Build tool: Vite

cd trajectoryos-desktop
npm install

Step 3: Start Development

bash
# Run dev server
npm run tauri dev

# Opens window with hot-reload
# Edit src/App.tsx to see changes
# Edit src-tauri/src/main.rs for Rust backend

Step 4: Implement Core Features

bash
# Add Rust dependencies
cd src-tauri
cargo add serde serde_json tokio rusqlite chrono nalgebra

# Create models
touch src/models.rs
# (implement LifePhysics, RingMemory, etc.)

# Create commands
mkdir src/commands
touch src/commands/physics.rs

Step 5: Build for All Platforms

bash
# Build for current platform
npm run tauri build

# Output in src-tauri/target/release/bundle/

# For other platforms, use GitHub Actions
# (builds on Windows, macOS, Linux runners)

---

๐ŸŽฏ Conclusion

For your situation (no Apple ID, want desktop version):

Recommendation: Rust + Tauri

Why:
1. โœ… No Apple ID required
2. โœ… Develop on any OS (Windows, Linux, macOS)
3. โœ… Cross-platform output (one codebase โ†’ 3 platforms)
4. โœ… Small bundle size (5-7 MB vs 150+ MB Electron)
5. โœ… Good performance (native webview + Rust)
6. โœ… Modern stack (React + Rust)
7. โœ… Can call existing backend (TypeScript services)

Timeline: 3-4 weeks to production-ready app

Cost: ~$16K development (no ongoing Apple fees)

Output: Windows .msi + macOS .dmg + Linux .deb/.AppImage

---

Next Step: Set up development environment and create initial Tauri project?

---

Document Version: 1.0
Date: December 21, 2025
Status: Proposal - Alternative to Native macOS

Promotion Decision

Keep in the searchable backlog until it intersects a live paper or system.

Source Anchor

projects/Documentation/05-research/TRAJECTORYOS_TAURI_PROPOSAL.md

Detected Structure

Method ยท Figures ยท Code Anchors ยท Architecture