VibeCody is a Rust workspace (monorepo) with 10 crate members: three binary applications, six shared library crates, and a standalone indexing service, plus editor plugins, an agent SDK, a skills library, a Flutter mobile companion, and native Apple Watch + Wear OS watch clients.
Architecture Diagram
Workspace Layout
vibecody/ ← Cargo workspace root
├── vibecli/
│ └── vibecli-cli/ ← Binary: terminal assistant + HTTP daemon
│ ├── src/ ← ~354 Rust modules
│ ├── tests/ ← 62+ BDD / integration harnesses
│ └── skills/ ← 711 skill files (25+ categories)
├── vibeui/
│ ├── src/ ← React + TypeScript frontend (~293 panels + 42 composites)
│ ├── src-tauri/ ← Binary: Tauri desktop app (1,045+ Tauri commands)
│ └── crates/
│ ├── vibe-core/ ← Library: editor primitives
│ ├── vibe-ai/ ← Library: AI providers + agent (22 providers + openai_compat)
│ ├── vibe-lsp/ ← Library: LSP client
│ ├── vibe-extensions/ ← Library: WASM extensions
│ └── vibe-collab/ ← Library: CRDT collaboration
├── vibeapp/ ← Secondary Tauri shell
├── vibemobile/ ← Flutter mobile companion (iOS, Android, desktop, web)
├── vibewatch/ ← Native watch clients
│ ├── VibeCodyWatch Watch App/ ← Apple Watch (SwiftUI, watchOS 10+)
│ ├── VibeCodyWatchCompanion/ ← iOS WatchConnectivity bridge
│ ├── VibeCodyWear/ ← Wear OS (Kotlin/Compose, Wear OS 3+)
│ └── VibeCodyWearCompanion/ ← Android Wearable Data Layer service
├── vibe-indexer/ ← Standalone indexing service
├── vibe-memory/ ← Library: SQLite vector memory with cognitive sectors
│ ├── src/ ← Global store, project store, hub, schema
│ └── tests/ ← BDD / integration tests
├── vscode-extension/ ← VS Code extension
├── jetbrains-plugin/ ← JetBrains IDE plugin (Gradle)
├── neovim-plugin/ ← Neovim plugin
└── packages/
└── agent-sdk/ ← TypeScript Agent SDK
Dependency Graph
vibecli-cli ──────────────-────┐
▼
vibe-ai ──── reqwest, async-trait, futures
│
vibe-ui (Tauri) ───────-───────┤
▼
vibe-core ── ropey, git2, notify, walkdir,
│ portable-pty, similar, regex
│
vibe-memory ── rusqlite, serde, bincode
│
vibe-lsp ── lsp-types, jsonrpc-core, tokio-util
│
vibe-extensions ── wasmtime, wasmtime-wasi
vibe-core — Editor Primitives
The foundation crate — no AI dependencies, no UI dependencies.
Text Buffer (buffer.rs)
Built on ropey, a rope data structure optimized for text editing:
- O(log n) insert, delete, and slice operations
- Undo/redo stack
- Multi-cursor support
- Line/column indexing
- Unicode-correct
pub struct TextBuffer {
rope: Rope,
undo_stack: Vec<EditOperation>,
redo_stack: Vec<EditOperation>,
cursors: Vec<Cursor>,
}
File System (file_system.rs)
Async file operations built on tokio:
open(path)→ reads file intoTextBuffersave(path, content)→ async write- File watcher via
notify— emits events on external changes - Directory listing for sidebar
Workspace (workspace.rs)
Multi-folder project management:
- Maintains a list of open root paths
- Aggregates file trees across folders
- Coordinates Git status across workspace roots
Git Integration (git.rs)
Uses git2 (libgit2 bindings) for zero-overhead Git operations:
| Function | Description |
|---|---|
get_status(path) |
Branch name + file status map |
get_diff(path, file) |
Unified diff for a single file |
get_repo_diff(path) |
Full workspace diff |
commit(path, message, files) |
Stage and commit |
push(path, remote, branch) |
Push to remote |
pull(path, remote, branch) |
Fast-forward pull |
list_branches(path) |
All local branches |
switch_branch(path, branch) |
Checkout |
get_history(path, limit) |
Recent commits |
discard_changes(path, file) |
Restore from HEAD |
is_git_repo(path) |
Check if path is in a repo |
Diff Engine (diff.rs)
Text-level diffing using the similar crate:
DiffEngine::diff(old, new)→Vec<DiffHunk>- Hunk types: Equal, Insert, Delete
- Inline word-level diff within hunks
- Used by both VibeCLI diff viewer and VibeUI Git panel
Terminal (terminal.rs)
PTY-based terminal using portable-pty:
- Spawns a shell process in a PTY
- Reads/writes raw bytes
- Used by VibeUI’s terminal panel via Tauri IPC
Search (search.rs)
File and content search:
walkdirfor recursive directory traversalregexfor content pattern matching- Respects
.gitignore(via filtering)
Command Executor (executor.rs)
Sandboxed shell command execution:
- Wraps
std::process::Command - Captures stdout/stderr
- Used by VibeCLI’s
!prefix and/execcommand
Codebase Index (index/)
Two-layer indexing for codebase intelligence:
| Module | Description |
|---|---|
mod.rs + symbol.rs |
Tree-sitter based symbol extraction |
embeddings.rs |
HNSW vector index with local (Ollama) or cloud (OpenAI) embedding models |
EmbeddingIndex supports incremental updates, semantic search, and persistence:
impl EmbeddingIndex {
pub async fn build(workspace: &Path, provider: &EmbeddingProvider) -> Result<Self>;
pub async fn update(&mut self, changed_files: &[PathBuf]) -> Result<()>;
pub async fn search(&self, query: &str, k: usize) -> Result<Vec<SearchHit>>;
}
vibe-ai — AI Provider Abstraction
A unified interface over multiple LLM providers.
The AIProvider Trait
#[async_trait]
pub trait AIProvider: Send + Sync {
fn name(&self) -> &str;
async fn is_available(&self) -> bool;
async fn complete(&self, context: &CodeContext) -> Result<CompletionResponse>;
async fn stream_complete(&self, context: &CodeContext) -> Result<CompletionStream>;
async fn chat(&self, messages: &[Message], context: Option<String>) -> Result<String>;
async fn stream_chat(&self, messages: &[Message]) -> Result<CompletionStream>;
}
CompletionStream is a Pin<Box<dyn Stream<Item = Result<String>> + Send>> — a type-erased async stream of text chunks for real-time streaming.
CodeContext
Carries the context for inline code completions:
pub struct CodeContext {
pub language: String,
pub file_path: Option<String>,
pub prefix: String, // Code before cursor
pub suffix: String, // Code after cursor
pub additional_context: Vec<String>,
}
ProviderConfig
Builder pattern for provider configuration:
let config = ProviderConfig::new("claude".into(), "claude-3-5-sonnet-20241022".into())
.with_api_key(std::env::var("ANTHROPIC_API_KEY")?)
.with_max_tokens(4096)
.with_temperature(0.2);
Provider Implementations
All 22 providers follow the same pattern (with shared OpenAI-compat helpers extracted into providers/openai_compat.rs):
- Send HTTP request to provider API using
reqwest - For
chat(): wait for full response - For
stream_chat(): return aCompletionStreambacked by SSE or chunked HTTP - Parse provider-specific JSON envelope to extract text
| Provider | API Type | Base URL |
|---|---|---|
| Ollama | REST + NDJSON streaming | http://localhost:11434 |
| Claude | REST + SSE streaming | https://api.anthropic.com |
| OpenAI | REST + SSE streaming | https://api.openai.com |
| Gemini | REST + SSE streaming | https://generativelanguage.googleapis.com |
| Grok | OpenAI-compatible | https://api.x.ai |
| Groq | OpenAI-compatible | https://api.groq.com |
| OpenRouter | OpenAI-compatible | https://openrouter.ai/api |
| Azure OpenAI | REST + SSE streaming | User-configured endpoint |
| Bedrock | AWS SigV4 signed | AWS regional endpoints |
| Copilot | OpenAI-compatible | https://api.githubcopilot.com |
| LocalEdit | Ollama FIM | http://localhost:11434 |
| Mistral | REST + SSE streaming | https://api.mistral.ai |
| Cerebras | OpenAI-compatible | https://api.cerebras.ai |
| DeepSeek | OpenAI-compatible | https://api.deepseek.com |
| Zhipu | REST + SSE streaming | https://open.bigmodel.cn |
| Vercel AI | REST + SSE streaming | User-configured endpoint |
| Fireworks | OpenAI-compatible | https://api.fireworks.ai |
| Minimax | REST + SSE streaming | https://api.minimax.chat |
| Perplexity | OpenAI-compatible | https://api.perplexity.ai |
| SambaNova | OpenAI-compatible | https://api.sambanova.ai |
| Together | OpenAI-compatible | https://api.together.xyz |
| Failover | Meta-provider | Wraps multiple providers with auto-fallback |
ChatEngine
Session manager on top of AIProvider:
- Maintains
Vec<Message>conversation history - Injects system prompts
- Handles context truncation via message history management
CompletionEngine
Inline code completion coordinator:
- Calls
stream_complete()with currentCodeContext - Debounces requests
- Used by VibeUI for as-you-type completions
Agent Loop (agent.rs)
The autonomous plan→act→observe loop:
- Multi-step execution with configurable approval tiers
- Tool use framework (read, write, patch, bash, search, list, complete)
- Streaming events via
AgentEventenum - 30-step maximum with configurable limit
Planner Agent (planner.rs)
Plan Mode — generates structured execution plans without executing:
PlannerAgent::plan()producesExecutionPlanwith typedPlanStepentries- Used by
/planin VibeCLI and “Plan first” toggle in VibeUI - Steps can be approved individually before execution
Multi-Agent Orchestrator (multi_agent.rs)
Parallel agent execution across isolated git worktrees:
MultiAgentOrchestrator::run_parallel()splits tasks across N agents- Each agent operates on its own worktree
- Results merged back to main branch
- Events streamed via
OrchestratorEvent
Hooks System (hooks.rs)
Event-driven hooks matching Claude Code’s model:
- Events:
SessionStart,PreToolUse,PostToolUse,Stop,TaskCompleted,SubagentStart - Handler types: shell commands or LLM evaluations
HookRunnermatches events against configured patterns and executes handlers- Supports block/modify/react decisions
Skills System (skills.rs)
Context-aware capability snippets:
SkillLoaderdiscovers skills from workspace and global directories- Skills activate based on trigger keyword matching
- YAML frontmatter + Markdown body format
Artifacts (artifacts.rs)
Structured output from agent operations:
- Types:
TaskList,ImplementationPlan,FileChange,CommandOutput,TestResults,ReviewReport,Text ArtifactStorewith annotations and timestamps- Async feedback queue for user input
Admin Policy (policy.rs)
Workspace-level security restrictions:
- Loaded from
.vibecli/policy.toml(workspace) or~/.vibecli/policy.toml(global) - Tool blocking/approval, path allow/deny lists, step limits
- Minimal glob matcher (no external crate dependency)
Trace / Session Resume (trace.rs)
JSONL audit logging and session resume:
TraceWriterrecords every tool call with input, output, timing, and approval sourceload_session()restores fullSessionSnapshotfor--resumelist_traces()for browsing past sessions
OpenTelemetry (otel.rs)
Span attribute constants for the OTLP pipeline:
- Defines
ATTR_SESSION_ID,ATTR_TASK,SPAN_SESSION,SPAN_STEP, etc. - Used by
otel_init.rsin VibeCLI for tracing spans
Red Team Pipeline (redteam.rs)
Autonomous 5-stage penetration testing module:
- Recon → endpoint enumeration, tech fingerprinting
- Analysis → white-box LLM-assisted vulnerability identification using codebase index
- Exploitation → active HTTP-based validation with browser actions
- Validation → confirm exploitability, generate PoC payloads
- Report → markdown report with CVSS scores + remediation
Key types: AttackVector (15 variants), CvssSeverity, RedTeamSession, VulnFinding, RedTeamManager
Sessions persisted as JSON at ~/.vibecli/redteam/. Integrates with bugbot.rs CWE patterns and existing agent browser actions.
BugBot Static Scanner (bugbot.rs)
OWASP/CWE static pattern scanner:
- 15 regex-based vulnerability patterns (CWE-89, CWE-79, CWE-22, CWE-798, CWE-338, CWE-78, CWE-601, CWE-918, CWE-611, CWE-502, CWE-943, CWE-1336, CWE-639, CWE-352, CWE-319)
- Runs before LLM analysis on every diff
- Results merged with LLM findings (static first)
MCP Client (mcp.rs)
Model Context Protocol integration:
- JSON-RPC 2.0 over stdio
- Discovers and invokes tools from external MCP servers
/mcp listand/mcp tools <server>in REPL
vibe-lsp — Language Server Protocol
LSP client infrastructure for editor intelligence features.
Architecture
Editor ──→ LspClient ──→ ChildProcess (language server)
│ │
JSON-RPC request stdin/stdout
│
Response dispatcher
- Uses
jsonrpc-corefor message framing tokio-utilcodec for framed I/O over process stdio- Implements the LSP spec types from
lsp-types
Features
- Go-to-definition
- Hover documentation
- Diagnostics (errors, warnings)
- Code completion
- Code actions (quick fixes)
- Rename symbol
vibe-extensions — WASM Extension System
Provides a sandbox for third-party extensions via WebAssembly.
Design
- Extensions are compiled to WASM (targeting
wasm32-wasi) wasmtimehosts and executes extension moduleswasmtime-wasiprovides WASI system call stubs- Extensions communicate via a VS Code–compatible host API
Extension API
// Extension registers lifecycle hooks
extension.on_file_save(|path| { /* ... */ });
extension.on_key_press(|key| { /* ... */ });
extension.register_command("my-command", |args| { /* ... */ });
VibeCLI TUI Architecture
The TUI is built with Ratatui and follows an Elm-like architecture:
Input Event (crossterm)
│
▼
App::handle_event() ← state mutation
│
▼
App state (AppState)
│
▼
ui::draw(frame, state) ← pure rendering
State (app.rs):
pub struct AppState {
pub mode: AppMode, // Chat | DiffView | FileTree
pub messages: Vec<ChatMessage>,
pub input: String,
pub provider: Box<dyn AIProvider>,
pub scroll_offset: usize,
pub diff_content: Option<String>,
}
Rendering (ui.rs):
Uses Ratatui’s constraint-based layout system:
- Horizontal split: main content + side panels
- Vertical split: message history + input box
- Scrollable list for chat history
- Syntax-highlighted diff viewer
VibeCLI Server (serve.rs)
HTTP daemon mode (vibecli serve) for VS Code extension and Agent SDK:
- Axum-based REST/SSE API
- Endpoints:
/health,/chat,/chat/stream,/agent/start,/agent/{id}/stream - Shared state with event broadcast channels
Code Review Agent (review.rs)
Structured code review mode (vibecli review):
- Reviews git diffs (uncommitted, staged, branch, or PR)
- Produces
ReviewReportwith issues, suggestions, and scoring - Posts directly to GitHub PRs via
ghCLI - Focus areas: Security, Performance, Correctness, Style, Testing
OpenTelemetry Init (otel_init.rs)
OTLP pipeline initialization:
- Sets up
tracing-opentelemetrybridge - Exports spans via OTLP/HTTP to any collector (Jaeger, Grafana, etc.)
OtelGuardensures flush on shutdown
VibeUI Tauri Architecture
Tauri bridges the React frontend and Rust backend via IPC.
Data Flow (AI Chat Example)
User types in AIChat.tsx
│
▼
invoke("ai_chat", { provider, messages })
│ Tauri IPC
▼
Rust command handler in src-tauri/src/
│
▼
vibe_ai::ChatEngine::chat(messages)
│
▼
HTTP request to AI provider
│
▼
Stream response back via Tauri events
│ emit("ai-chunk", text)
▼
AIChat.tsx renders chunks progressively
Tauri Command Pattern
#[tauri::command]
async fn ai_chat(
provider: String,
messages: Vec<Message>,
state: tauri::State<'_, AppState>,
) -> Result<String, String> {
let engine = state.chat_engine.lock().await;
engine.chat(&messages, None).await.map_err(|e| e.to_string())
}
Memory & Context Architecture
VibeCody uses a multi-layer memory system to provide relevant context to AI agents across large codebases and long-running sessions.
The Five Memory Stores
Each store is specialized for different content types — they are not duplicates:
| Store | Content Type | Location | Role |
|---|---|---|---|
| ProjectMemory | Static docs (CLAUDE.md, AGENTS.md) |
Hierarchical (system/user/project/dir) | Constant context floor |
| OpenMemory | Extracted facts, preferences | ~/.vibecli/openmemory/ |
Fact retriever (TF-IDF + sectors) |
| SessionStore | Conversation transcripts | ~/.vibecli/sessions.db |
History retriever (FTS5) |
| JobManager | Agent job state, scratchpad | ~/.vibecli/jobs.db |
Durable working state |
| vibe-indexer | Code + symbols | Project .vibecli/index/ |
Code retriever (HNSW) |
Storage Security
| Store | Encryption | Key Derivation |
|---|---|---|
profile_settings.db |
ChaCha20-Poly1305 | SHA-256(“vibecli-profile-store-v1:” + HOME + “:” + USER) |
workspace.db |
ChaCha20-Poly1305 | SHA-256(“vibecli-workspace-store-v1:” + HOME + “:” + USER + “:” + workspace_path) |
jobs.db |
ChaCha20-Poly1305 | Profile key (machine-bound) |
sessions.db |
None (file-perm) | — |
Context Assembler
The Context Assembler (vibecli/vibecli-cli/src/context_assembler.rs) is the single entry point for building system context under a policy-driven budget:
Workspace + Policy + Budget + Toggles
│
▼
┌─────────────────────┐
│ Context Assembler │
└─────────────────────┘
│
▼
┌───────────────────────────────┐
│ Sections │
│ • project_memory │
│ • orchestration │
│ • project_profile │
│ • task_files │
│ • open_memory │
│ • agent_scratchpad │
└───────────────────────────────┘
Agent Kind Budgets
Different agent types get different budget allocations:
| AgentKind | Total Chars | Primary Section |
|---|---|---|
| Chat | 32,000 | project_memory (16k), orchestration (8k) |
| CodingAgent | 128,000 | task_files (96k), open_memory (16k) |
| ResearchAgent | 128,000 | open_memory (64k), task_files (16k) |
| BackgroundJob | 48,000 | agent_scratchpad (40k) |
Recap & Resume
Recap & Resume provide cross-client session continuity:
- Recap: Structured summary of a unit of work (session, job, diff chain)
- Resume: Pick up where you left off on any client
| Scope | Storage | Table |
|---|---|---|
| Session | sessions.db |
recaps |
| Job | jobs.db |
recaps |
| DiffChain | workspace.db |
diff_chain_recaps |
See memory-architecture.md for complete documentation.
Testing Strategy
11,000+ unit tests + 62 BDD / integration harnesses across the workspace (0 failures in CI).
| Crate | Tests | Key coverage areas |
|---|---|---|
vibecli |
5,500+ unit, 62+ BDD | session store, serve, config, review, workflow, REPL, redteam, gateway, channel daemon, branch agent, spec pipeline, VM orchestrator, transform, marketplace, background agents, TUI, security scan, automations, counsel, superbrain, web client, open memory, auto research, blue/purple team, IDP, watch auth / bridge / session relay, mDNS / Tailscale / ngrok, pairing, all feature modules |
vibe-ai |
1,020+ | 22 providers + openai_compat, tools, trace, hooks, policy, skills, agent, multi-agent, MCP, agent teams |
vibe-core |
370+ | buffer, git, diff, context, file system, workspace, search, terminal, index/embeddings, executor |
vibe-ui |
230+ | Tauri commands, coverage, cost, flow, agent executor, shadow workspace |
vibe-lsp |
74 | LSP client, features, manager |
vibe-collab |
53 | CRDT rooms, server registry, protocol, awareness |
vibe-extensions |
46 | loader, manifest, permissions |
| TypeScript | — | tsc --noEmit type checking (0 errors, 0 warnings) |
Notable BDD harnesses added recently: watch_auth_bdd, watch_bridge_bdd, watch_p256_auth_bdd, watch_session_relay_bdd, mdns_announce_bdd, tailscale_bdd, ngrok_bdd.
VS Code Extension
The vscode-extension/ directory contains a full VS Code extension:
- Chat sidebar — webview panel communicating with VibeCLI daemon
- Inline completions —
VibeCLIInlineCompletionProviderwith debouncing - Agent mode — start agent tasks directly from VS Code
- API client — REST calls to
vibecli serve
Agent SDK
The packages/agent-sdk/ TypeScript package (@vibecody/agent-sdk) provides a programmatic interface for building on VibeCody:
- Connect to VibeCLI daemon
- Start agent sessions, stream events
- Chat and completion APIs
- Typed event handling
Mobile & Watch Surfaces
VibeCLI’s --serve daemon is the single backend for all remote clients. Every non-desktop surface authenticates through the same store (ProfileStore for API keys, WorkspaceStore for project secrets) and shares session history via sessions.db.
VibeMobile (Flutter)
vibemobile/lib/ is a cross-platform Flutter app (iOS, Android, macOS, Linux, Windows, Web):
| Layer | Files | Role |
|---|---|---|
screens/ |
11 screens | Home, chat, sandbox chat, watch chat, pair, manual connect, machines, machine detail, sessions, settings, onboarding |
services/ |
6 services | api_client, auth_service, discovery_service (mDNS), handoff_service (URL race), notification_service (push), watch_sync_service |
models/ |
– | Machine / device / session DTOs mirroring daemon JSON |
The HandoffService never commits to a single URL — on startup and every 60 s it races every reachable candidate (stored baseUrl, beacon lan_ips, Tailscale IP, public URL, mDNS-discovered IPs) with a 3 s timeout on /health. The first success wins until the next probe. This makes the app silently adapt as the user moves between home Wi-Fi, a hotspot, and the office LAN.
VibeWatch (native)
vibewatch/ contains four sibling clients that share the Rust backend but are fully native per platform:
┌───────────────────────────────────────────────────────────────────┐
│ vibecli --serve │
│ ┌──────────────┐ ┌──────────────────┐ ┌─────────────────────┐ │
│ │ watch_auth │ │ watch_session_ │ │ watch_bridge │ │
│ │ .rs │ │ relay.rs │ │ .rs │ │
│ │ P256 ECDSA, │ │ OLED-optimised │ │ Axum /watch/* + SSE │ │
│ │ JWT (HS256) │ │ payload mapping │ │ (11 routes) │ │
│ └──────┬───────┘ └────────┬─────────┘ └──────────┬──────────┘ │
└─────────┼───────────────────┼────────────────────────┼────────────┘
│ │ │
┌─────▼──────┐ ┌─────▼──────┐ ┌──────────▼──────────┐
│ Direct │ │ Tailscale │ │ Phone relay │
│ LAN │ │ mesh │ │ (WatchConnectivity │
│ (mDNS) │ │ │ │ / Wearable DL) │
└─────┬──────┘ └─────┬──────┘ └──────────┬──────────┘
└──────────┬────────┴────────────────────────┘
│
┌───────────┼────────────┐
│ │
┌──────▼─────────┐ ┌───────▼──────────┐
│ Apple Watch │ │ Wear OS │
│ SwiftUI │ │ Jetpack Compose │
│ Secure Enclave │ │ Android Keystore │
│ (P-256 only) │ │ (StrongBox TEE) │
└────────────────┘ └──────────────────┘
Key design notes:
- Algorithm choice — P256 ECDSA (secp256r1). Apple’s Secure Enclave only supports P-256; for symmetry and code-reuse both Wear OS and Apple Watch generate P-256 device keys. Ed25519 was removed for the device-registration path in commit
3308278a. - JWT tokens are HMAC-SHA256 signed with a 32-byte secret that lives in
ProfileStore. Access tokens expire in 15 min; refresh tokens in 7 days. - Wrist-off suspension: a signed
WristActivityEventflips the session’swrist_suspendedflag, which blocks tool execution until the watch is back on the wrist. - Replay prevention: the
NonceRegistryrejects any request whose timestamp is outside a 30-second window or whose nonce has been seen. - Nonce registry, broadcast fan-out, and 11
/watch/*routes live inwatch_bridge.rsas a standalone Axum router that the main daemon mounts.
See docs/WATCH-INTEGRATION.md for complete route tables, claims structure, TDD / BDD coverage, and the watch client implementation details.
Zero-config connectivity (mDNS / Tailscale / ngrok)
Three independent modules in vibecli/vibecli-cli/src/ build the beacon returned by GET /mobile/beacon:
| Module | Responsibility |
|---|---|
mdns_announce.rs |
Broadcasts _vibecli._tcp.local. PTR/SRV/TXT/A records every 60 s on 224.0.0.251:5353; answers active PTR queries within <1 s. No external tools; works on any IP range. |
tailscale.rs |
Shells out to tailscale status --json for the 100.x IP; optionally runs tailscale funnel 7878 and polls for the public https://<machine>.<tailnet>.ts.net URL. |
ngrok.rs |
Probes localhost:4040/api/tunnels on startup; with ngrok_auto_start=true spawns ngrok http <port> using the auth token and polls up to 15 s for the public URL. |
pairing.rs |
Generates a 128-bit random token + pairing URL and renders an ASCII/Unicode QR for terminal display (used by vibecli pair). |
The mobile / watch clients consume these paths through the URL race described above. No single path is required — the app silently uses whichever responds first.
Full protocol + troubleshooting: docs/connectivity.md.