Initializing
Back to Projects
Year2025
DomainDevOps
AccessPrivate Repo
Complexity7.7 / 10
Video DemoArchitecture Docs
TypeScriptNode.jsHonoSQLiteDrizzle ORMSocket.IOReactViteWebSocketsJWTPinoTailscale
DevOpsArchived

GATOR — AI-Powered Orchestration System

A lean, single-process AI orchestration system providing chat-first, intent-based remote control over IDE workspaces. Built with Hono, SQLite, and WebSockets.

Startup Time<0.0s
RAM Usage<0MB
Process Count0
Docker Containers0
ArchitectureSingle Process
Offline SupportIndexedDB + SQLite WAL

Table of Contents


The Challenge

Remote control of a development workstation from a mobile device typically requires over-engineered solutions that consume massive resources:

  • Resource overhead: GATOR v1 required ~720MB RAM across 5 Docker containers (NestJS, Next.js, PostgreSQL, Redis, Worker Agent)
  • Startup time: 30+ seconds to bring all services online
  • LSP bottleneck: Language Server polling at 50ms intervals caused constant CPU and I/O churn
  • Cross-process IPC tax: Gateway to Worker Agent communication via Redis queues added 5-20ms latency
  • Bundle bloat: Next.js PWA generated 250KB+ for a chat interface that could be 12KB

The requirement: Build a lean, single-process system that provides chat-first, intent-based remote control from any device — with sub-second startup, under 150MB RAM, and zero external dependencies — using the phone as a control surface that emits intent, never commands.


Architecture & Solution

GATOR Lite replaces the v1 heavyweight architecture with a single Hono process containing embedded SQLite, in-memory priority queue, and the LSP-Bridge directly embedded.

Parsing system architecture diagram...

Component Responsibilities

ComponentRuntimeResponsibility
Web SPABrowserRender UI, emit intent, display steps + logs
APEX CoreNode.jsAuth, intent parsing, task validation, queue, WS broadcast
LSP-BridgeInside CorePORTA LSP logic: discovery, RPC relay, delta-polling
Task RunnerInside CoreShell sandbox, whitelisted execution, log streaming
SQLiteFile on diskDurable record store: devices, audit, tasks, conversations
In-Memory QueueIn CorePriority buckets (low/med/high), SQLite-backed recovery

Tech Stack

LayerTechnologyRole
RuntimeNode.js 20+ LTSESM-native, stable
Web FrameworkHono 4.xFastest Node.js framework, 5x faster than NestJS
DatabaseSQLite (better-sqlite3)Embedded, WAL mode, zero setup
ORMDrizzle ORMTypeScript-first, zero-runtime overhead
Real-timeSocket.IOWebSocket with rooms and reconnection
Task QueueCustom In-Memory Priority QueueMap<priority, Queue> with AsyncLocalStorage
LSP-BridgePORTA (direct port)Connect RPC protocol, delta-polling
AuthCustom JWT (HS256) via joseLightweight token authentication
LoggingPino 9.xFast structured JSON logger
FrontendVite + ReactInstant HMR, minimal bundle (~12KB)
Process ManagertsxTypeScript execution without compilation
NetworkTailscaleEncrypted private mesh, zero port-forwarding

Key Engineering Decisions

1. Hono over NestJS — 5x Performance

typescript
// apps/core/src/index.ts — Hono server setup
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { authMiddleware } from './middleware/auth';
import { chatRouter } from './routes/chat';
import { wsRouter } from './routes/ws';

const app = new Hono();

// Global middleware
app.use('*', logger());
app.use('*', cors({ origin: '*', credentials: true }));

// Routes
app.get('/health', (c) => c.json({ status: 'ok', uptime: process.uptime() }));
app.route('/api/chat', chatRouter);
app.route('/api/ws', wsRouter);

// Start with tsx — no compilation needed
export default app;

2. SQLite over PostgreSQL — Single File, Zero Setup

typescript
// packages/database/src/schema.ts — Drizzle schema
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';

export const devices = sqliteTable('devices', {
  id: text('id').primaryKey(),
  name: text('name').notNull(),
  platform: text('platform').notNull(),
  isActive: integer('is_active').default(1),
  lastSeen: integer('last_seen'),
  createdAt: integer('created_at').notNull(),
});

export const tasks = sqliteTable('tasks', {
  id: text('id').primaryKey(),
  deviceId: text('device_id').notNull(),
  intent: text('intent').notNull(),
  status: text('status').notNull(),
  priority: text('priority').default('medium'),
  result: text('result'),
  createdAt: integer('created_at').notNull(),
  completedAt: integer('completed_at'),
});

3. Single Process Architecture — 720MB → 150MB

v1 (GATOR)v2 (GATOR Lite)Reduction
NestJS Gateway (~120MB)Hono Core (~15MB)-105MB
Next.js PWA (~250MB)Vite SPA (~25MB)-225MB
Node.js Worker (~80MB)Merged into Core-80MB
PostgreSQL Docker (~250MB)Embedded SQLite (~20MB)-230MB
Redis Docker (~20MB)In-Memory Queue-20MB
Total~150MB-570MB

4. In-Memory Priority Queue with SQLite Persistence

typescript
// packages/queue/src/priority-queue.ts
type Task = {
  id: string;
  priority: 'low' | 'medium' | 'high';
  payload: unknown;
  createdAt: number;
};

class PriorityQueue {
  private queues = {
    high: [] as Task[],
    medium: [] as Task[],
    low: [] as Task[],
  };

  async enqueue(task: Task): Promise<void> {
    this.queues[task.priority].push(task);
    // Persist to SQLite for restart recovery
    await db.insert(tasks).values(task);
  }

  async dequeue(): Promise<Task | undefined> {
    for (const priority of ['high', 'medium', 'low'] as const) {
      const task = this.queues[priority].shift();
      if (task) return task;
    }
    return undefined;
  }
}

5. Intent-Based Interface — Phone as Control Surface

Instead of sending raw commands, users type intent:

code
User: "Run tests on backend"
GATOR: Parses intent"Run test:unit on apps/api"
GATOR: Executes whitelisted taskStreams output
GATOR: Reports back with results

LSP-Bridge System

GATOR inherits the verified PORTA LSP-Bridge for IDE communication:

Discovery Protocol

  1. Daemon scan: Find running language server processes
  2. Port probe: Identify LSP ports
  3. RPC enrich: Connect via JSON-RPC/HTTPS
  4. Cache: 10-second TTL for discovery results

Delta Polling State Machine

code
IDLE (20000ms heartbeat)

(signals.activate())
ACTIVE (150ms serial polling)

(signals.deactivate())
IDLE

Step Recovery

  • Oversized payload: Chunk into smaller delta messages
  • UTF-8 corruption: Skip malformed chunks with MAX_SKIP guard
  • Connection drop: Resume from last confirmed step

Security Architecture

Whitelist-Only Task Execution

typescript
// Task whitelist from task-registry.json
const taskRegistry = {
  'test:unit': {
    command: 'pnpm --filter apps/api test',
    description: 'Run API unit tests',
    timeout: 120000,
  },
  'build:core': {
    command: 'pnpm --filter apps/core build',
    description: 'Build core application',
    timeout: 60000,
  },
  // Unknown tasks are rejected before execution
};

async function executeTask(taskName: string) {
  if (!taskRegistry[taskName]) {
    throw new Error(`Task "${taskName}" not whitelisted`);
  }
  return spawn(taskRegistry[taskName].command);
}

Network Exposure Guard

The Core binds only to:

  • 127.0.0.1 (loopback)
  • Private LAN IP
  • Tailscale CGNAT

Wildcard binds (0.0.0.0) throw at startup.

JWT Authentication

typescript
// Using jose library for lightweight JWT
import { SignJWT, jwtVerify } from 'jose';

const secret = new TextEncoder().encode(process.env.JWT_SECRET);

async function signToken(payload: object): Promise<string> {
  return new SignJWT(payload)
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime('7d')
    .sign(secret);
}

async function verifyToken(token: string): Promise<object> {
  const { payload } = await jwtVerify(token, secret);
  return payload;
}

Offline & Resilience

Client-Side Offline Queue

  • IndexedDB stores commands when offline
  • On reconnect, replay in order
  • No commands lost during connection drops

Server-Side Persistence

  • SQLite WAL mode for concurrent reads
  • In-memory queue persisted to SQLite on every write
  • Tasks survive Core restarts

Reconnection Logic

  • Socket.IO with automatic reconnection
  • Delta-poller resumes from last confirmed step
  • Task queue recovers from SQLite on startup

Deployment

Zero-Config Startup

bash
# No Docker required
pnpm install
pnpm dev

# System starts in <1.5 seconds with:
# - Embedded SQLite (auto-created)
# - In-memory queue (empty)
# - LSP-Bridge ready

Tailscale Integration

bash
# On the host machine, ensure Tailscale is running
tailscale up --autoconnect=true

# GATOR binds to Tailscale IP automatically
# Access from phone via: https://100.x.x.x:3000

Production Checklist

bash
# Install dependencies
pnpm install

# Build for production
pnpm build

# Start the core
pnpm start:core

# Access from mobile via Tailscale
# https://<tailcale-ip>/app

Roadmap

Phase 1 — Core Foundation (Complete)

  • Single Hono process architecture
  • Embedded SQLite with Drizzle ORM
  • Basic chat + intent parsing

Phase 2 — LSP-Bridge Integration (Complete)

  • PORTA LSP logic ported
  • Delta-polling state machine
  • Step recovery mechanisms

Phase 3 — Mobile SPA (Complete)

  • Vite + React frontend
  • Real-time log streaming via Socket.IO
  • IndexedDB offline queue

Phase 4 — Security Hardening (Complete)

  • Task whitelist enforcement
  • Network exposure guard
  • JWT authentication

Phase 5 — Performance Tuning (In Progress)

  • Startup time <1s target
  • Memory <100MB target

Phase 6 — Multi-Device Support (Planned)

  • iOS companion app
  • Desktop SPA refinements

Phase 7 — Advanced Automation (Future)

  • AI-powered intent classification
  • Predictive task execution

Engineering Proof

Real-world validation, system demonstrations, and interface captures of the execution states.

System Demonstration

Video walkthrough detailing core logic, interactions, and system behaviors in action.

System Captures

Architecture Feedback

Spotted a potential optimization or antipattern? Let me know.

Submit a Technical Suggestion