Created
July 12, 2026 15:36
-
-
Save kenwheeler/6acfc102eae5f57129a40c643df5a99e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Build a Private Remote AI Engineering Cockpit | |
| You are a principal engineer responsible for designing and implementing a production-quality private remote AI system consisting of: | |
| 1. A secure bridge server running on a trusted home or office computer. | |
| 2. A native mobile application for interacting with the agent remotely. | |
| 3. A durable workspace system for projects, tasks, automations, source files, Git state, and engineering history. | |
| The result should feel like a private combination of an AI chat client, Linear, Vercel, GitHub, and a lightweight engineering control plane. It must be reliable enough for daily use—not a demo held together by scripts. | |
| ## Operating principles | |
| - Prefer boring, explicit, testable architecture over cleverness. | |
| - Treat concurrency, cancellation, persistence, recovery, authentication, and resource limits as first-class requirements. | |
| - Preserve a clear security boundary: private network access does not replace authentication. | |
| - Never embed production secrets directly in source control. | |
| - Make every destructive action explicit and confirmed. | |
| - Keep valid API behavior stable once the mobile client depends on it. | |
| - Build incrementally, with characterization tests before major refactors. | |
| - Do not claim success without running tests and exercising the system end to end. | |
| ## Product goal | |
| A user should be able to open the mobile app and: | |
| - Create and switch between persistent AI conversations instantly. | |
| - Send text, photos, camera captures, documents, and files. | |
| - Watch answer text, provider-supplied reasoning summaries, tool activity, and progress stream live. | |
| - Leave the app while work continues safely on the server. | |
| - Reconnect after suspension or network loss without losing the job. | |
| - Receive completion notifications. | |
| - Browse projects, tasks, source files, Git status, diffs, branches, commits, runs, logs, and artifacts. | |
| - Create projects from deterministic templates. | |
| - Ask the agent to work inside a selected project with authoritative project context. | |
| - Review and approve sensitive operations rather than allowing silent execution. | |
| ## Required architecture | |
| ### 1. Private bridge server | |
| Build a single-owner service that runs on the trusted host and is exposed only through a private overlay network such as Tailscale. Use private HTTPS in production and bind the application process to loopback when a private HTTPS proxy is available. | |
| The server must provide: | |
| - Bearer-token authentication for every `/api/*` endpoint. | |
| - An unauthenticated minimal `/health` endpoint. | |
| - Persistent conversation registration and metadata. | |
| - Durable background jobs for AI turns. | |
| - One writer at a time per conversation, while allowing separate conversations to run concurrently. | |
| - Reconnectable Server-Sent Events for answer, reasoning-summary, tool, and state updates. | |
| - Polling endpoints as the durable fallback and source of final authoritative state. | |
| - Job cancellation with terminate → grace period → kill → reap semantics. | |
| - Recovery of interrupted jobs after service restart. | |
| - Bounded logs, artifacts, partial text, reasoning summaries, and process output. | |
| - Concurrent stdout/stderr draining so child processes cannot deadlock. | |
| - Strict request framing, body-size limits, socket timeouts, and bounded request concurrency. | |
| - Chunked media responses rather than loading entire files into memory. | |
| - Race-safe staged uploads with exact-length validation, private permissions, unique temporary names, active claims, retry-safe release, and expiration. | |
| - Atomic persistence using unique same-directory temporary files, flush, `fsync`, `os.replace`, and restrictive permissions. | |
| - Consistent locking around every read-modify-write transaction. | |
| - Structured errors with stable HTTP status codes and response envelopes. | |
| Do not hold one fragile HTTP request open for the entire AI turn. Submission should return a job ID immediately. SSE should accelerate live updates; it must not be the only recovery mechanism. | |
| ### 2. Streaming contract | |
| Consume the agent runtime's structured event stream rather than scraping terminal text. | |
| Support at least: | |
| - answer text start/delta/end | |
| - provider-supplied reasoning-summary start/delta/end | |
| - tool execution start/update/end | |
| - job queued/working/completed/failed/cancelled/recovered | |
| - generated UI blocks and artifacts | |
| Never fabricate or expose hidden chain-of-thought. Only display reasoning summaries explicitly emitted by the provider/runtime. | |
| Each SSE connection must: | |
| - require authentication | |
| - begin with an authoritative full job snapshot | |
| - include event IDs | |
| - send keepalives | |
| - tolerate event-counter resets after server restart | |
| - terminate after a terminal job state | |
| - allow the client to reconnect and then fall back to polling | |
| Throttle growing snapshots to avoid quadratic serialization and network use. | |
| ### 3. Native mobile app | |
| Build a native mobile client—SwiftUI for iOS unless another platform is explicitly requested. | |
| Use a compact, high-density dark interface inspired by Linear and Vercel rather than oversized consumer-style cards. | |
| Required behavior: | |
| - Home, Chat, Work, and Apps/control-plane destinations. | |
| - Persistent conversation switcher with rename and delete. | |
| - Optimistic New Chat: insert and select a provisional conversation synchronously, show `Creating…`, disable premature sending, replace it atomically with the server conversation, and roll back cleanly on failure. | |
| - Independent activity and unread state per conversation. | |
| - In-place streaming assistant message updates. | |
| - Expandable THINKING/REASONING panel that starts expanded during reasoning and collapses when answer text begins. | |
| - Visible tool name and concise tool detail. | |
| - SSE consumption using the platform's streaming HTTP APIs. | |
| - Polling fallback after stream failure, suspension, or network transition. | |
| - No duplicate messages when authoritative history refreshes during a stream. | |
| - Native Markdown rendering, fenced code, links, lists, and quotes. | |
| - File picker, photo library, camera, document scanner, OCR, and background uploads. | |
| - Native inline image, audio, and video rendering with authenticated media requests. | |
| - Push notifications, notification actions, badges, and deep links where feasible. | |
| - Model and reasoning-level controls per conversation. | |
| - Proper cancellation and recovered-job error handling. | |
| - Accessibility labels, Dynamic Type compatibility, and visible loading/error states. | |
| Never leave an empty assistant placeholder after failure. Never send a prompt to a provisional or stale conversation ID. | |
| ### 4. Durable workspace system | |
| Create a durable project ledger outside transient chat history. Chat history is context; the workspace is the source of truth. | |
| Use a workspace root with one directory per project: | |
| ```text | |
| workspace/ | |
| projects/ | |
| <project-slug>/ | |
| STATUS.md | |
| YYYY-MM-DD.md | |
| metadata.json | |
| tasks.json | |
| automations.json | |
| ``` | |
| Rules: | |
| - `STATUS.md` is the current snapshot: state, priority, source paths, current focus, next actions, risks, and blockers. | |
| - Daily Markdown files are append-only history with timestamped objectives, changes, decisions, verification, commits, deployments, artifacts, blockers, and next actions. | |
| - `metadata.json` is generated; do not hand-edit it. | |
| - Tasks represent bounded work and may be associated with projects. | |
| - Automations represent recurring or event-driven processes and include schedule, runtime, source, and health metadata. | |
| - Never record passwords, tokens, private keys, cookies, or sensitive payloads. | |
| - Reindex searchable metadata after ledger changes. | |
| - Project chats automatically receive authoritative project metadata, current status, open tasks, source path, and Git state. | |
| ### 5. Project control plane | |
| Expose authenticated project-scoped APIs and native UI for: | |
| - project catalog and project detail | |
| - deterministic project creation templates | |
| - status and dated ledger entries | |
| - tasks and inbox items | |
| - bounded source search | |
| - confined file browsing and text preview | |
| - optimistic file editing with original-content checks | |
| - Git status, diff, branches, stage, unstage, and local commit | |
| - job/run history, logs, artifacts, cancellation, and retry | |
| - approval records for sensitive actions | |
| Security requirements: | |
| - Resolve every project path and prove it remains under an allowlisted project root. | |
| - Reject absolute traversal, symlink escapes, `.git` mutation through file APIs, dependency/build directories, and oversized files. | |
| - Use subprocess argument arrays, never interpolated shell commands. | |
| - Do not add Git push or arbitrary command execution without a separate approval design. | |
| - Approval records must not silently execute actions. | |
| ### 6. Generated UI and mini apps | |
| Support a small validated declarative UI schema rather than executable mobile code. | |
| Permitted block examples: | |
| - choices | |
| - actions | |
| - key/value summaries | |
| - progress | |
| - image/audio/video | |
| - private preview links | |
| - bounded vertical/horizontal layouts | |
| - text/headings | |
| - toggles | |
| - gauges | |
| - dividers/spacers | |
| Validate version, depth, node count, text lengths, URL hosts, media paths, MIME types, and capabilities. Historical transcript parsing must be side-effect-free; only a newly completed assistant turn may persist a generated mini app. | |
| ## Suggested module boundaries | |
| Keep the entry point thin. Use modules similar to: | |
| ```text | |
| config | |
| storage | |
| http_api | |
| auth | |
| process_supervisor | |
| chat_sessions | |
| chat_jobs | |
| job_stream | |
| uploads | |
| media | |
| projects | |
| git_service | |
| control_plane | |
| notifications | |
| ``` | |
| Avoid one giant file that owns routing, persistence, process management, projects, uploads, Git, and notifications. | |
| For a single-process personal deployment, well-tested locked atomic JSON stores are acceptable initially. Prefer SQLite in WAL mode once relational querying, transactions across stores, or multiple writer processes become requirements. Do not perform a database migration casually; include backup, import verification, schema versioning, and rollback. | |
| ## Testing requirements | |
| Use temporary directories and fake agent/APNs implementations. Tests must never mutate production projects, sessions, credentials, or device registrations. | |
| Cover at minimum: | |
| - authentication and health-route behavior | |
| - every route's normal status and response envelope | |
| - malformed, negative, missing, oversized, and truncated request bodies | |
| - concurrent JSON transactions and permission modes | |
| - task and mini-app mutation races | |
| - noisy child stdout/stderr | |
| - callback failure and cancellation without leaked threads or zombies | |
| - SIGTERM-ignoring children requiring kill escalation | |
| - same-session serialization and cross-session concurrency | |
| - upload exact-size, short-write, duplicate PUT, claim/sweep race, retry, and expiration | |
| - SSE initial snapshot, live updates, reconnect with stale event ID, keepalive, cancellation, and terminal completion | |
| - multi-turn answer and reasoning aggregation around tool calls | |
| - optimistic new-chat success and rollback | |
| - stream-to-poll fallback | |
| - history refresh during an active stream | |
| - recovered jobs after service restart | |
| - project path confinement and Git safety | |
| Treat warnings about leaked file descriptors, subprocess pipes, tasks, or threads as failures. | |
| ## Delivery phases | |
| ### Phase 1 — Foundations | |
| - Define API contracts and data schemas. | |
| - Add characterization tests. | |
| - Implement secure storage, authentication, configuration, and health. | |
| ### Phase 2 — Durable chat | |
| - Persistent conversations. | |
| - Background jobs. | |
| - Process supervision. | |
| - Polling, cancellation, recovery, and attachments. | |
| ### Phase 3 — Streaming | |
| - SSE job snapshots. | |
| - Answer and provider reasoning-summary deltas. | |
| - Tool activity. | |
| - Reconnection and polling fallback. | |
| ### Phase 4 — Mobile client | |
| - Conversation UI. | |
| - Optimistic creation. | |
| - Streaming rendering. | |
| - Attachments, media, errors, and notifications. | |
| ### Phase 5 — Workspace and control plane | |
| - Project ledger. | |
| - Tasks and automations. | |
| - Files, Git, runs, search, briefs, and approvals. | |
| ### Phase 6 — Hardening | |
| - Concurrency and failure injection. | |
| - Resource limits. | |
| - Permission migration. | |
| - Recovery testing. | |
| - End-to-end physical-device validation. | |
| ## Definition of done | |
| The system is done when: | |
| - A physical mobile device connects over private HTTPS. | |
| - New Chat opens instantly and selects the correct durable conversation. | |
| - A message submits as a background job. | |
| - Answer text and available provider reasoning summaries stream live. | |
| - Tool state updates without blocking the answer stream. | |
| - Network interruption recovers through SSE reconnection or polling. | |
| - App suspension does not lose the server job. | |
| - Cancellation reliably reaps the child process. | |
| - Restarted services mark interrupted jobs recovered. | |
| - Attachments upload safely and remain retryable through their retention window. | |
| - Projects, tasks, files, Git state, runs, and ledger history are usable from mobile. | |
| - All automated tests, compilation, static checks, and end-to-end smoke tests pass. | |
| - Secrets and runtime state are excluded from source control. | |
| - Operational documentation explains installation, configuration, backup, restart, logs, and recovery. | |
| ## Working method | |
| Before writing code: | |
| 1. Inspect the host, mobile platform, agent runtime, networking, and signing constraints. | |
| 2. Produce a concise architecture decision record and phased plan. | |
| 3. Identify risky decisions requiring explicit approval. | |
| 4. Establish the test harness and API contracts. | |
| During implementation: | |
| - Make small, reviewable commits. | |
| - Run the full test suite after each architectural slice. | |
| - Use one writer for shared files and independent read-only reviewers. | |
| - Record observed failures and their actual root causes. | |
| - Do not hide partial failures behind optimistic status messages. | |
| At the end of each phase, report: | |
| - files and modules changed | |
| - API/schema changes | |
| - tests and builds run | |
| - observed results | |
| - commits and deployed versions | |
| - unresolved risks | |
| - exact next action | |
| Begin by asking only the minimum questions needed to identify the host OS, mobile platform, agent CLI/runtime, private-network option, and preferred workspace root. Then produce the architecture and implementation plan before coding. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment