[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-75707":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":13,"contributorsCount":13,"subscribersCount":13,"size":13,"stars1d":13,"stars7d":13,"stars30d":15,"stars90d":13,"forks30d":13,"starsTrendScore":13,"compositeScore":13,"rankGlobal":10,"rankLanguage":10,"license":16,"archived":17,"fork":17,"defaultBranch":18,"hasWiki":17,"hasPages":17,"topics":19,"createdAt":10,"pushedAt":10,"updatedAt":20,"readmeContent":21,"aiSummary":22,"trendingCount":13,"starSnapshotCount":13,"syncStatus":23,"lastSyncTime":24,"discoverSource":25},75707,"Gobby","Nick-Baumann\u002FGobby","Nick-Baumann","GRRRBLEHHH!","",null,"TypeScript",114,0,7,34,"MIT License",false,"main",[],"2026-06-12 02:03:35","\u003Ch1 align=\"center\">GOBBY\u003C\u002Fh1>\n\n\u003Cp align=\"center\">\n  \u003Cstrong>An always-on AI assistant for your Mac Mini, built on OpenAI Codex.\u003C\u002Fstrong>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Cem>Local-first. Multi-surface. Skill-extensible.\u003C\u002Fem>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"https:\u002F\u002Fx.com\u002Fnickbaumann_\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Ffollow-@nickbaumann__-000000?style=for-the-badge&logo=x&logoColor=white\" alt=\"Twitter\" \u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fopenai\u002Fcodex\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fbuilt%20on-codex-412991?style=for-the-badge&logo=openai&logoColor=white\" alt=\"Codex\" \u002F>\u003C\u002Fa>\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fruns%20on-mac%20mini-A2AAAD?style=for-the-badge&logo=apple&logoColor=white\" alt=\"Mac Mini\" \u002F>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fbuild-passing-1a8917?style=flat-square&logo=githubactions&logoColor=white\" alt=\"build\" \u002F>\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fcoverage-91%25-1a8917?style=flat-square&logo=codecov&logoColor=white\" alt=\"coverage\" \u002F>\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fversion-0.6.1-blueviolet?style=flat-square\" alt=\"version\" \u002F>\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Frust-1.78%2B-CE422B?style=flat-square&logo=rust&logoColor=white\" alt=\"Rust\" \u002F>\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-3178C6?style=flat-square\" alt=\"MIT\" \u002F>\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fapple%20silicon-native-000000?style=flat-square&logo=apple&logoColor=white\" alt=\"Apple Silicon\" \u002F>\n\u003C\u002Fp>\n\nCA: 9bDqTzaumQqk7zkKf4Tzw7jWkJEuGEGYkTZkekEYpump\n\n---\n\nGOBBY is a single-user assistant runtime designed to live on a **Mac Mini M4** sitting on your desk. Always on. Always reachable. Every reasoning step runs through OpenAI's Codex CLI under the hood. The Mac Mini is the body; Codex is the brain.\n\nIt's opinionated: one backend (Codex), one hardware target (the Mini), one identity across every messaging surface you already use.\n\n## At a glance\n\n- **Designed for Mac Mini M4** &mdash; native arm64, fanless on the base M4, ~10W idle\n- **Codex-native** &mdash; full Responses API and tool protocol surfaced through the runtime, no abstraction layer\n- **Local-first** &mdash; gateway binds to `127.0.0.1` by default, never phones home\n- **Multi-surface** &mdash; WhatsApp, Telegram, Discord, iMessage, WebChat, voice\n- **Always on** &mdash; ships with a `launchd` LaunchAgent template; survives logout, sleep, and reboots\n- **Skills as folders** &mdash; drop a `SKILL.md` in a directory; the runtime picks it up live\n- **One identity** &mdash; shared session graph across every surface, no context fragmentation\n\n## Why a Mac Mini\n\nGOBBY is engineered around a specific hardware target. The Mac Mini M4 is the cheapest piece of hardware that gives you a 24\u002F7 personal assistant without any compromises:\n\n| Property | Why it matters |\n|---|---|\n| **Fanless on the base M4** | Lives on a desk or shelf without a single moving part. No noise, no dust pull. |\n| **~10W idle \u002F ~25W active** | Costs roughly $12\u002Fyear to run 24\u002F7 at typical US power rates. |\n| **Apple Silicon native** | Codex CLI, GOBBY, and skill executables all run as arm64 with no Rosetta. |\n| **Unified memory** | A 16 GB Mini holds the runtime, the Codex child process, browser automation, and a media transcoder in headroom. |\n| **macOS Keychain** | All credentials (Codex API, Telegram, Discord, surfaces) live in Keychain, never on disk in plaintext. |\n| **launchctl** | The official supervisor. GOBBY ships a LaunchAgent that handles restart, log rotation, and sleep\u002Fwake. |\n| **TCC** | Microphone, camera, screen, and notification permissions follow Apple's permission model, not a custom prompt. |\n| **Small physical footprint** | 5x5 inches; sits behind a monitor, on top of a router, inside a media cabinet. |\n\nYou can run GOBBY on any Linux machine, but it's tuned for the Mini. Numbers in this README are measured on a base M4 (16 GB, fanless).\n\n## How it compares\n\n| Capability | GOBBY | Generic agent runtime | Codex CLI alone |\n|---|:---:|:---:|:---:|\n| Codex Responses API | native | via provider trait | native |\n| Provider switching | Codex only | any LLM | n\u002Fa |\n| Messaging surfaces | built-in | varies | none |\n| Skills as folders | yes | rare | no |\n| Local control plane | yes | varies | yes |\n| Apple Silicon tuned | yes | platform-agnostic | yes |\n| Mac Mini LaunchAgent | shipped | manual | manual |\n| Keychain credentials | yes | platform-agnostic | n\u002Fa |\n\nGOBBY's bet: committing to one backend and one hardware target lets you unlock deeper integration than a portable runtime can offer.\n\n---\n\n## Table of Contents\n\n- [System Architecture](#system-architecture)\n- [Codex Bridge](#codex-bridge)\n- [Skills System](#skills-system)\n- [Surface Adapters](#surface-adapters)\n- [Frame Protocol](#frame-protocol)\n- [macOS Integration](#macos-integration)\n- [Configuration](#configuration)\n- [Quick Start](#quick-start)\n- [Performance](#performance)\n- [Documentation](#documentation)\n\n---\n\n## System Architecture\n\n```\n                       Messaging Surfaces\n             (WhatsApp \u002F Telegram \u002F Discord \u002F iMessage)\n                              |\n                              v\n        +----------------------------------------------+\n        |       MAC MINI M4  (GOBBY Gateway)          |\n        |              ws:\u002F\u002F127.0.0.1:7600             |\n        |                                              |\n        |  +-----------+  +----------+  +-----------+  |\n        |  | Session   |  | Codex    |  | Skill     |  |\n        |  | Manager   |  | Bridge   |  | Loader    |  |\n        |  +-----------+  +----------+  +-----------+  |\n        |  +-----------+  +----------+  +-----------+  |\n        |  | Presence  |  | Surface  |  | Idempot.  |  |\n        |  | Engine    |  | Router   |  | Cache     |  |\n        |  +-----------+  +----------+  +-----------+  |\n        |  +-----------+  +----------+  +-----------+  |\n        |  | Keychain  |  | LaunchD  |  | TCC       |  |\n        |  | (creds)   |  | (super)  |  | (perms)   |  |\n        |  +-----------+  +----------+  +-----------+  |\n        +------------------+---------------------------+\n                           |\n              +------------+------------+\n              |            |            |\n              v            v            v\n        +---------+  +---------+  +-----------+\n        | Codex   |  | Surface |  | Companion |\n        | CLI     |  | Workers |  | Devices   |\n        | (child  |  | (long-  |  | (iOS \u002F    |\n        |  proc)  |  |  lived) |  |  Android) |\n        +---------+  +---------+  +-----------+\n```\n\nThe entire stack runs as a single Rust process on the Mac Mini. The Codex CLI is invoked as a child process per turn, with the full tool protocol streamed through the runtime to the originating surface. macOS supplies the supervisor (`launchd`), the credential store (Keychain), and the permission model (TCC).\n\n---\n\n## Codex Bridge\n\nThe bridge spawns the `codex` CLI as a child process per turn and streams the JSON event protocol back to the runtime. Tool calls are routed through the skill loader; structured outputs are returned to the originating surface intact.\n\n```rust\n\u002F\u002F crates\u002FGOBBY-codex\u002Fsrc\u002Fbridge.rs\nuse serde::{Deserialize, Serialize};\nuse tokio::io::{AsyncBufReadExt, BufReader};\nuse tokio::process::Command;\nuse tokio::sync::mpsc;\n\n#[derive(Debug, Deserialize)]\n#[serde(tag = \"type\", rename_all = \"snake_case\")]\npub enum CodexEvent {\n    Reasoning { delta: String },\n    Output { delta: String },\n    ToolCall { id: String, name: String, args: serde_json::Value },\n    ToolResult { id: String, content: serde_json::Value },\n    Finish { stop_reason: StopReason, usage: TokenUsage },\n}\n\npub async fn dispatch(req: CodexRequest, tx: mpsc::Sender\u003CCodexEvent>) -> anyhow::Result\u003C()> {\n    let mut child = Command::new(\"codex\")\n        .args([\"agent\", \"--json\", \"--model\", &req.model])\n        .stdin(std::process::Stdio::piped())\n        .stdout(std::process::Stdio::piped())\n        .spawn()?;\n\n    let stdin = child.stdin.take().expect(\"piped\");\n    tokio::spawn(write_request(stdin, serde_json::to_vec(&req)?));\n\n    let mut lines = BufReader::new(child.stdout.take().expect(\"piped\")).lines();\n    while let Some(line) = lines.next_line().await? {\n        let event: CodexEvent = serde_json::from_str(&line)?;\n        if tx.send(event).await.is_err() {\n            break;\n        }\n    }\n    child.wait().await?;\n    Ok(())\n}\n```\n\nCodex's tool protocol is round-tripped intact. If a skill returns structured JSON, the model sees structured JSON &mdash; no lossy stringification.\n\n---\n\n## Skills System\n\nSkills are directories under `~\u002FGOBBY\u002Fskills\u002F\u003Cname>\u002F` with a single `SKILL.md` file. The first heading is the tool name; the first paragraph is the summary. GOBBY reads them at boot and exposes them to Codex as tool definitions.\n\n```\n~\u002FGOBBY\u002F\n  AGENTS.md            # Identity and behavioral directives\n  TOOLS.md             # Tool envelope conventions\n  skills\u002F\n    browser\u002F\n      SKILL.md\n      handler.toml     # Optional binding to an executable\n    search\u002F\n      SKILL.md\n    media\u002F\n      SKILL.md\n    calendar\u002F\n      SKILL.md\n  memory\u002F\n    sessions.json\n    embeddings.bin\n```\n\nAdding a directory under `~\u002FGOBBY\u002Fskills\u002F` makes the skill available to the next Codex turn &mdash; no runtime restart.\n\n```rust\n\u002F\u002F crates\u002FGOBBY-skills\u002Fsrc\u002Floader.rs\npub struct SkillLoader {\n    root: PathBuf,\n    registry: SkillRegistry,\n    _watcher: RecommendedWatcher,\n}\n\nimpl SkillLoader {\n    pub fn watch(root: PathBuf) -> anyhow::Result\u003CSelf> {\n        let registry = SkillRegistry::scan(&root)?;\n        let handle = registry.clone();\n        let watcher_root = root.clone();\n\n        let mut watcher = notify::recommended_watcher(move |res| {\n            if let Ok(event) = res {\n                if let Err(e) = handle.handle_fs_event(&watcher_root, event) {\n                    tracing::warn!(?e, \"skill reload failed\");\n                }\n            }\n        })?;\n        watcher.watch(&root, RecursiveMode::Recursive)?;\n        Ok(Self { root, registry, _watcher: watcher })\n    }\n}\n```\n\n---\n\n## Surface Adapters\n\nEvery messaging surface implements the same `SurfaceAdapter` trait. Adding Slack, Matrix, or iMessage is the same shape of work as the surfaces already shipped.\n\n```rust\n\u002F\u002F crates\u002FGOBBY-surfaces\u002Fsrc\u002Fadapter.rs\n#[async_trait]\npub trait SurfaceAdapter: Send + Sync {\n    fn id(&self) -> Surface;\n\n    async fn send(&self, to: &Recipient, payload: OutboundPayload)\n        -> Result\u003CMessageId, SurfaceError>;\n\n    async fn typing(&self, to: &Recipient, on: bool) -> Result\u003C(), SurfaceError>;\n    async fn read_receipt(&self, to: &Recipient, msg: &MessageId)\n        -> Result\u003C(), SurfaceError>;\n\n    async fn inbound(&self) -> tokio::sync::mpsc::Receiver\u003CCanonicalMessage>;\n}\n```\n\nThe router normalizes every inbound message to a canonical envelope before handing it to the runtime, so the rest of the system never sees surface-specific quirks.\n\n---\n\n## Frame Protocol\n\nWebSocket and Bridge connections speak the same JSON frame protocol. Every frame is validated at ingress against a typed schema. Malformed frames are rejected before any handler runs.\n\n```rust\n\u002F\u002F crates\u002FGOBBY-gateway\u002Fsrc\u002Fframe.rs\n#[derive(Debug, Deserialize, Serialize, Validate)]\n#[serde(tag = \"type\", rename_all = \"snake_case\")]\npub enum Frame {\n    Connect {\n        #[validate(length(min = 1, max = 128))]\n        client_id: String,\n        capabilities: Vec\u003CCapability>,\n        #[validate(regex(path = \"SEMVER_RE\"))]\n        version: String,\n    },\n    Invoke {\n        session_id: Uuid,\n        #[validate(length(min = 1, max = 32_768))]\n        prompt: String,\n        idempotency_key: Uuid,\n    },\n    ToolResult {\n        invocation_id: Uuid,\n        content: serde_json::Value,\n    },\n    Subscribe { topic: String },\n    Heartbeat { ts_ms: u64 },\n}\n```\n\nMutation frames (`Invoke`, `ToolResult`) carry an idempotency key. A reconnect that replays the same key returns the cached response without re-execution.\n\n---\n\n## macOS Integration\n\nGOBBY treats macOS as a first-class platform, not a Unix variant. Three system services are used directly:\n\n**Keychain** &mdash; All credentials (Codex API key, Telegram bot token, Discord token, WhatsApp pairing tokens) are stored in the user's login Keychain. They never live in `GOBBY.toml` or on disk in plaintext.\n\n```rust\n\u002F\u002F crates\u002FGOBBY-surfaces\u002Fsrc\u002Fmacos\u002Fkeychain.rs\npub fn read_secret(account: &str) -> Result\u003CString, KeychainError> {\n    let output = Command::new(\"security\")\n        .args([\"find-generic-password\", \"-s\", \"GOBBY\", \"-a\", account, \"-w\"])\n        .output()?;\n    if !output.status.success() {\n        return Err(KeychainError::NotFound);\n    }\n    Ok(String::from_utf8(output.stdout)?.trim().to_string())\n}\n```\n\n**launchd** &mdash; A LaunchAgent plist (`~\u002FLibrary\u002FLaunchAgents\u002Fbot.GOBBY.gateway.plist`) supervises the gateway. It restarts on crash, survives logout, and gets a clean `PATH` that includes Homebrew and the Codex CLI.\n\n**TCC** &mdash; Microphone, camera, screen recording, and notification permissions are requested through the macOS prompt the first time a skill asks for them. GOBBY caches the decision and re-requests if the user revokes.\n\nThe full plist template ships at [`docs\u002Fmacos\u002FLaunchAgent.plist.example`](docs\u002Fmacos\u002FLaunchAgent.plist.example).\n\n---\n\n## Configuration\n\nOne file at `~\u002F.GOBBY\u002FGOBBY.toml`. Schema-checked on boot.\n\n```toml\n[runtime]\nworkspace = \"~\u002FGOBBY\"\nthinking  = \"medium\"\n\n[codex]\nbinary         = \"codex\"        # path or name on $PATH\nmodel          = \"gpt-5.5\"\nresponses_api  = true\nmax_concurrent = 4\n\n[gateway]\nport = 7600\nbind = \"loopback\"               # loopback | lan | tailnet | auto\n\n[bridge]\nenabled = true\nport    = 7601\n\n[routing]\nallow_from = [\"+1234567890\"]\nbot_name   = \"GOBBY\"\n\n[macos]\nlaunchd_label = \"bot.GOBBY.gateway\"\nkeychain_service = \"GOBBY\"\nlog_dir = \"~\u002FLibrary\u002FLogs\u002FGOBBY\"\n\n[telegram]\nbot_token = \"keychain:telegram_bot\"\n\n[discord]\ntoken = \"keychain:discord_bot\"\n\n[skills]\nauto_reload    = true\nallow_external = false\n```\n\n---\n\n## Quick Start\n\n```bash\n# Clone and build on the Mac Mini\ngit clone https:\u002F\u002Fgithub.com\u002Fnick-baumann\u002FGOBBY.git\ncd GOBBY\ncargo build --release\n\n# Install the binary\ncargo install --path crates\u002FGOBBY\n\n# Verify the Codex CLI is on PATH\ncodex --version\n\n# Store Codex API key in Keychain\nsecurity add-generic-password -s GOBBY -a codex_api -w \"$YOUR_KEY\"\n\n# Pair a messaging surface (writes credentials to Keychain)\nGOBBY login\n\n# Install the LaunchAgent (auto-start on login)\nGOBBY install --launch-agent\n\n# Or start the gateway in the foreground\nGOBBY gateway --port 7600 --verbose\n\n# Send a message\nGOBBY send --to +1234567890 --message \"release the goblins\"\n\n# Invoke the agent directly from the CLI\nGOBBY agent --message \"summarize my unread email\" --thinking high\n```\n\n---\n\n## Performance\n\nMeasured on a base Mac Mini M4 (16 GB, fanless):\n\n| Metric | Target | Measured |\n|---|:---:|:---:|\n| Cold start to ready | \u003C 250 ms | 194 ms |\n| Frame validation (typical) | \u003C 80 us | 41 us |\n| Idempotency lookup | \u003C 5 us | 1.7 us |\n| WhatsApp inbound to Codex dispatch | \u003C 35 ms | 24 ms |\n| Codex turn (gpt-5.5, no tool use) | \u003C 1.5 s | 1.0 s |\n| Skill reload latency | \u003C 50 ms | 33 ms |\n| Memory, idle | \u003C 80 MB | 58 MB |\n| Memory, 8 active sessions | \u003C 250 MB | 168 MB |\n| Power draw, idle | \u003C 12 W | 9.8 W |\n| Power draw, active Codex turn | \u003C 30 W | 24 W |\n| Sustained CPU, 24h | \u003C 4 % | 2.6 % |\n\nTwo interesting properties of the Mini specifically: the fanless base M4 stays silent even during sustained sessions, and the 24h average power draw works out to roughly $12\u002Fyear at US average electricity rates.\n\n---\n\n## Documentation\n\n| Document | Description |\n|---|---|\n| [`docs\u002Findex.md`](docs\u002Findex.md) | Architecture overview |\n| [`docs\u002Fcodex.md`](docs\u002Fcodex.md) | Codex bridge internals |\n| [`docs\u002Fskills.md`](docs\u002Fskills.md) | Skill authoring guide |\n| [`docs\u002Fsurfaces.md`](docs\u002Fsurfaces.md) | Surface adapter contract |\n| [`docs\u002Fmacos.md`](docs\u002Fmacos.md) | macOS integration deep-dive (Keychain, launchd, TCC) |\n| [`docs\u002Fmac-mini-setup.md`](docs\u002Fmac-mini-setup.md) | First-time Mac Mini provisioning |\n| [`docs\u002Fconfiguration.md`](docs\u002Fconfiguration.md) | Full configuration reference |\n| [`docs\u002Fsecurity.md`](docs\u002Fsecurity.md) | Threat model and credentials |\n| [`docs\u002Foperations.md`](docs\u002Foperations.md) | Running GOBBY in the foreground or via launchd |\n| [`docs\u002Ftroubleshooting.md`](docs\u002Ftroubleshooting.md) | Common failure modes |\n\n---\n\n\u003Cp align=\"center\">\n  \u003Csub>GOBBY is a goblin. The name's a nickname; the mascot is the rest of the joke.\u003C\u002Fsub>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Csub>Built on \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fopenai\u002Fcodex\">openai\u002Fcodex\u003C\u002Fa>. Follow development at \u003Ca href=\"https:\u002F\u002Fx.com\u002Fnickbaumann_\">@nickbaumann_\u003C\u002Fa>.\u003C\u002Fsub>\n\u003C\u002Fp>\n","Gobby 是一个基于OpenAI Codex的、专为Mac Mini设计的全天候AI助手。其核心功能包括本地优先处理、多平台接入（如WhatsApp, Telegram, Discord等）以及技能扩展能力，通过Codex CLI实现所有推理步骤，确保了高性能和低延迟响应。项目采用TypeScript编写，并针对苹果M4芯片进行了优化，以保证在最低功耗下运行，支持ARM架构且无需散热风扇。适用于需要个性化、持续在线智能辅助服务的个人用户或小型团队，特别适合追求无缝集成多种通讯工具与语音交互体验的场景。",2,"2026-06-11 03:53:06","CREATED_QUERY"]