Date: 2026-01-22 Author: Palmer (assisted by Skylar) Status: Draft
Internal agentic coding platform for Motion engineers. Enables parallel workstreams with CLI-based coding agents (starting with Claude Code), accessible via mobile-optimized web UI.
- Enable 2-3 Motion engineers to work with AI coding agents simultaneously
- Mobile-first UX for following conversations and quick approvals
- Extensible to support multiple CLI agents (Claude Code, OpenCode, etc.)
- Self-hosted on Beelink via Tailscale
- Cloud infrastructure (Beelink only for now)
- Slack integration
- Complex RBAC beyond Tailscale auth
- Advanced multi-agent orchestration
┌─────────────────────────────────────────────────────────────────┐
│ BEELINK SERVER │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Claude Code │ │ Claude Code │ │ Claude Code (extensible)│ │
│ │ Session 1 │ │ Session 2 │ │ Session N │ │
│ │ Clone: repo1│ │ Clone: repo2│ │ Clone: repoN │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
│ │ │ │ │
│ └────────────────┴─────────────────────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ API Server │ │
│ │ (Node.js) │ │
│ └───────┬───────┘ │
│ │ │
│ ┌────────────┴────────────┐ │
│ │ │ │
│ ┌──────▼─────┐ ┌──────▼─────┐ │
│ │ SQLite │ │ Tailscale │ │
│ │ Database │ │ Access │ │
│ └─────────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
│ HTTPS (Tailscale Tailnet)
▼
┌─────────────────────────────────────────────────────────────────┐
│ WEB CLIENT (Mobile-First) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Dashboard │ Sessions │ Create Task │ Settings │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Beelink Server
- Intel/AMD processor with 32GB+ RAM
- Runs API server and Claude Code sessions
- Connected to Motion tailnet
API Server
- Node.js/Express or Fastify
- Manages sessions, messages, diffs
- Communicates with Claude Code via STDIN/STDOUT or MCP
SQLite Database
- Simple, file-based, no external dependencies
- Stores users, sessions, messages, tasks
Tailscale
- Authentication (who can access)
- Network connectivity (HTTPS to Beelink)
Claude Code CLI is the reference implementation. The interface should support extensibility.
interface CodingAgent {
// Lifecycle
start(sessionId: string, repoPath: string, task: string): Promise<void>;
send(message: string): Promise<AgentResponse>;
stop(): Promise<void>;
// State
isRunning(): boolean;
getConversation(): Message[];
getDiff(): DiffResult | null;
// Events
onMessage(callback: (msg: Message) => void): void;
onDiff(callback: (diff: DiffResult) => void): void;
onComplete(callback: () => void): void;
}
interface AgentResponse {
message: string;
hasDiff: boolean;
diff?: DiffResult;
isComplete: boolean;
}
interface DiffResult {
files: FileDiff[];
summary: string;
}
interface FileDiff {
path: string;
additions: number;
deletions: number;
patch: string; // Unified diff format
}# Start Claude Code in session mode
claude --session-dir /sessions/{sessionId} --task "{task}"
# Or use MCP protocol (preferred for extensibility)
claude --mcp-server /path/to/mcp.sock// AgentFactory creates agents based on type
class AgentFactory {
create(type: 'claude-code' | 'opencode' | 'custom', config: AgentConfig): CodingAgent {
switch (type) {
case 'claude-code':
return new ClaudeCodeAgent(config);
case 'opencode':
return new OpenCodeAgent(config);
default:
return new CustomAgent(config);
}
}
}| Method | Endpoint | Description |
|---|---|---|
| GET | /api/health |
Health check |
| POST | /api/auth/login |
Tailscale OAuth callback |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/sessions |
List user's sessions |
| POST | /api/sessions |
Create new session |
| GET | /api/sessions/:id |
Get session details |
| DELETE | /api/sessions/:id |
End session, cleanup |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/sessions/:id/messages |
Get conversation history |
| POST | /api/sessions/:id/messages |
Send follow-up message |
| GET | /api/sessions/:id/diff |
Get current diff |
| POST | /api/sessions/:id/approve |
Approve and create PR |
| POST | /api/sessions/:id/reject |
Reject, optionally provide feedback |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/repos |
List available repos |
| POST | /api/repos/:owner/:name/clone |
Request clone (admin) |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/users/me |
Get current user |
| GET | /api/users |
List users (admin only) |
Create Session:
POST /api/sessions
{
"repoOwner": "motion",
"repoName": "backend-api",
"task": "Add user authentication endpoint",
"agentType": "claude-code"
}
Response (201):
{
"sessionId": "sess_abc123",
"status": "starting",
"repoUrl": "https://github.com/motion/backend-api"
}Send Message:
POST /api/sessions/sess_abc123/messages
{
"content": "Can you also add rate limiting?"
}
Response (200):
{
"messageId": "msg_xyz789",
"content": "Sure, I'll add rate limiting to the endpoint.",
"timestamp": "2026-01-22T01:10:00Z"
}Get Diff:
GET /api/sessions/sess_abc123/diff
Response (200):
{
"files": [
{
"path": "src/auth.ts",
"additions": 45,
"deletions": 3,
"patch": "--- a/src/auth.ts\n+++ b/src/auth.ts\n@@ ... @@"
}
],
"summary": "Added JWT authentication with 45 lines added, 3 deleted"
}-- Users (synced from Tailscale or GitHub OAuth)
CREATE TABLE users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT,
avatar_url TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
last_login_at DATETIME
);
-- Sessions
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
repo_owner TEXT NOT NULL,
repo_name TEXT NOT NULL,
repo_path TEXT NOT NULL,
agent_type TEXT DEFAULT 'claude-code',
task TEXT NOT NULL,
status TEXT DEFAULT 'starting',
-- status: starting, running, waiting_approval, completed, rejected, error
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- Messages
CREATE TABLE messages (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
role TEXT NOT NULL,
-- role: user, agent, system
content TEXT NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (session_id) REFERENCES sessions(id)
);
-- Diffs (current state of changes)
CREATE TABLE diffs (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL UNIQUE,
files_json TEXT NOT NULL,
-- JSON array of FileDiff objects
summary TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (session_id) REFERENCES sessions(id)
);
-- Tasks (for tracking work)
CREATE TABLE tasks (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
title TEXT NOT NULL,
description TEXT,
session_id TEXT,
status TEXT DEFAULT 'pending',
-- status: pending, in_progress, approved, rejected, merged
github_pr_url TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (session_id) REFERENCES sessions(id)
);
-- Repos (available for cloning)
CREATE TABLE repos (
id TEXT PRIMARY KEY,
owner TEXT NOT NULL,
name TEXT NOT NULL,
clone_url TEXT NOT NULL,
is_cloned BOOLEAN DEFAULT FALSE,
clone_path TEXT,
last_synced_at DATETIME,
UNIQUE(owner, name)
);
-- Indexes for performance
CREATE INDEX idx_sessions_user ON sessions(user_id);
CREATE INDEX idx_sessions_status ON sessions(status);
CREATE INDEX idx_messages_session ON messages(session_id);
CREATE INDEX idx_tasks_user ON tasks(user_id);- Mobile defaults: Design for phone first, expand for desktop
- Thumb-friendly: Large tap targets, bottom navigation
- Quick actions: Approve/reject prominent on mobile
- Offline-capable: PWA with caching for mobile use
- Responsive: Fluid layouts, not separate mobile/desktop sites
┌─────────────────────────────────┐
│ Motion Agent Platform ⚙️ │
├─────────────────────────────────┤
│ Active Sessions │
│ ┌───────────────────────────┐ │
│ │ 📝 Add auth endpoint │ │
│ │ 🔄 Running - 2 messages │ │
│ └───────────────────────────┘ │
│ ┌───────────────────────────┐ │
│ │ 🔍 Fix bug in payments │ │
│ │ ⏳ Waiting for approval │ │
│ └───────────────────────────┘ │
│ │
│ + New Task │
│ └─────────────────────────────┘ │
│ Recent Activity │
│ • PR merged: Add user profile │
│ • Rejected: Refactor UI │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ ← Back ⚙️ │
├─────────────────────────────────┤
│ 📝 Add auth endpoint │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ 🤖 Agent: │
│ I'll start by reading the │
│ current auth module to │
│ understand the structure. │
│ │
│ ───────────────────────── │
│ You: │
│ Can you also add rate │
│ limiting? │
│ │
│ ───────────────────────── │
│ 🤖 Agent: │
│ Sure! I'll add rate limiting │
│ to the authentication... │
│ │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ 3 files changed │
│ [View Diff] │
│ │
├─────────────────────────────────┤
│ [Message Input...] Send │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ ← Back [Approve] [R] │
├─────────────────────────────────┐
│ 3 files changed, +45/-3 lines │
│ │
│ src/auth.ts +45 │
│ src/middleware/auth.js +0 │
│ tests/auth.test.ts +0 │
│ │
│ ───────────────────────── │
│ src/auth.ts │
│ @@ -1,10 +1,15 @@ │
│ 1 import { │
│ 2 jwt, │
│ 3 { JwtPayload } │
│ 4 } from 'jsonwebtoken'; │
│ 5 +import { RateLimiter │
│ 6 +} from 'express-rate- │
│ 7 +limit'; │
│ 8 │
│ 9 interface User { │
│ 10 id: string; │
│ ... │
│ │
├─────────────────────────────────┤
│ [Reject with feedback] │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ ← Cancel Create│
├─────────────────────────────────┤
│ New Task │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ Repository │
│ [backend-api ▼] │
│ │
│ Task Description │
│ ┌───────────────────────────┐ │
│ │ Add rate limiting to all │ │
│ │ authentication endpoints. │ │
│ │ Use Redis for storage. │ │
│ └───────────────────────────┘ │
│ │
│ Agent │
│ ( Claude Code (default) ) │
│ │
│ [Start Task] │
└─────────────────────────────────┘
| Feature | Mobile | Desktop |
|---|---|---|
| Conversation | Threaded | Split-pane with diff |
| Diff review | Horizontal scroll | Side-by-side |
| Create task | Simplified form | Full form + preview |
| Dashboard | Stacked cards | Grid + filters |
| Navigation | Bottom nav | Sidebar |
Tech Stack:
- React + TypeScript
- Tailwind CSS (mobile-first)
- shadcn/ui components
- TanStack Query for data fetching
- PWA with Vite PWA plugin
Key Components:
SessionCard- Dashboard itemConversationThread- Message listDiffViewer- Code diff displayFileTree- Repo navigationCreateTaskForm- Task creationBottomNav- Mobile navigation
- Tailscale OAuth for Motion tailnet users
- Simple email-based access (allowlist)
- No complex RBAC in v0
- Separate clone per session (prevents cross-contamination)
- Read-only file system where possible
- Timeout for idle sessions (4 hours default)
- HTTPS only (via Tailscale HTTPS)
- No external internet access from agent sessions
- Audit logging of all actions
- SQLite with file-level encryption optional
- Daily backups to encrypted volume
- No sensitive data in logs
- Set up Beelink server
- Install Claude Code CLI
- Basic API server (Node.js + Express)
- SQLite database schema
- Tailscale HTTPS setup
- Session creation/lifecycle
- Claude Code integration (STDIN/STDOUT)
- Message history
- Basic dashboard UI
- User auth via Tailscale
- Diff capture and storage
- Diff viewer UI
- Approve/Reject workflow
- PR creation (GitHub API)
- Mobile-first CSS
- Push notifications (optional)
- PWA manifest
- Testing on mobile devices
- Multiple agent support (OpenCode)
- Slack integration
- Auto-trigger from issues
- Advanced RBAC
- Claude Code License: Can Motion use Claude Code CLI commercially?
- GitHub Integration: PAT or GitHub App for PR creation?
- Rate Limits: Claude Code API limits per day?
- Monitoring: How to track agent usage/effectiveness?
- Ramp Inspect Blog Post
- Claude Code CLI Documentation
- OpenCode Agent Framework
- Modal Infrastructure (reference only, not using)
- Tailscale HTTPS