Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ body:
id: version
attributes:
label: Version / commit
placeholder: v0.0.1 or a short SHA
placeholder: v0.1.0 or a short SHA
validations:
required: true
- type: input
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ Or `make ci`. CI enforces Rust checks on macOS/Linux and the UI build on Ubuntu.
Push a version tag to publish binaries via GitHub Actions:

```sh
git tag v0.0.1
git push origin v0.0.1
git tag v0.1.0
git push origin v0.1.0
```

Assets are attached to the GitHub Release; users can run
`./scripts/install-from-release.sh v0.0.1`.
`./scripts/install-from-release.sh v0.1.0`.

## Commits and PRs

Expand Down
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ members = [
]

[workspace.package]
version = "0.0.1"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/DevVig/microbridge"
Expand Down
51 changes: 36 additions & 15 deletions Formula/microbridge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# frozen_string_literal: true

# Homebrew formula for Microbridge — menu bar app + daemon (not CLI-only).
# Installs prebuilt GitHub Release assets (fast). Source builds: use HEAD or
# clone + ./scripts/install.sh.
#
# brew tap DevVig/microbridge https://github.com/DevVig/microbridge
# brew install microbridge
Expand All @@ -14,37 +16,53 @@
class Microbridge < Formula
desc "Open-source control plane for the Codex Micro (menu bar + daemon)"
homepage "https://github.com/DevVig/microbridge"
url "https://github.com/DevVig/microbridge/archive/refs/tags/v0.0.1.tar.gz"
sha256 "f171c275890add016045b0bbde54330f104b6d5db3a9d16c8d366cd5fcdde599"
version "0.1.0"
license any_of: ["MIT", "Apache-2.0"]
head "https://github.com/DevVig/microbridge.git", branch: "main"

depends_on "rust" => :build
depends_on "node" => :build
depends_on :macos

def install
system "cargo", "build", "--release", "--locked", "-p", "microbridged", "-p", "microbridgectl"
bin.install "target/release/microbridged"
bin.install "target/release/microbridgectl"
on_macos do
on_arm do
url "https://github.com/DevVig/microbridge/releases/download/v#{version}/microbridge-v#{version}-aarch64-apple-darwin.tar.gz"
# sha256 filled by scripts/bump-formula.sh after each release
sha256 "0000000000000000000000000000000000000000000000000000000000000000"

resource "ui" do
url "https://github.com/DevVig/microbridge/releases/download/v#{version}/microbridge-ui-v#{version}-aarch64-apple-darwin.tar.gz"
sha256 "0000000000000000000000000000000000000000000000000000000000000000"
end
end
on_intel do
url "https://github.com/DevVig/microbridge/releases/download/v#{version}/microbridge-v#{version}-x86_64-apple-darwin.tar.gz"
sha256 "0000000000000000000000000000000000000000000000000000000000000000"

cd "apps/microbridge-ui" do
system "npm", "ci"
system "npm", "run", "tauri", "build", "--", "--bundles", "app"
resource "ui" do
url "https://github.com/DevVig/microbridge/releases/download/v#{version}/microbridge-ui-v#{version}-x86_64-apple-darwin.tar.gz"
sha256 "0000000000000000000000000000000000000000000000000000000000000000"
end
end
end

app = Dir["apps/microbridge-ui/src-tauri/target/release/bundle/macos/*.app"].first
odie "Microbridge.app missing after Tauri build" if app.nil?
prefix.install app
def install
# Release tarball layout: microbridge-vX.Y.Z-<target>/{microbridged,microbridgectl,…}
bin.install Dir["**/microbridged"].first
bin.install Dir["**/microbridgectl"].first
Comment on lines +47 to +50

resource("ui").stage do
app = Dir["**/Microbridge.app"].first
odie "Microbridge.app missing from UI release archive" if app.nil?
prefix.install app
Comment on lines +52 to +55

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Keep the Homebrew ownership marker outside the signed app bundle.

Line 55 installs the prebuilt signed app, but post_install subsequently writes .microbridge-brew inside that bundle. This changes its sealed contents and can invalidate strict signature verification. Store the marker beside the app instead.

Proposed fix
     apps = Pathname.new(Dir.home)/"Applications"
     apps.mkpath
     dest = apps/"Microbridge.app"
-    marker = dest/".microbridge-brew"
+    marker = apps/".Microbridge.app.microbridge-brew"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Formula/microbridge.rb` around lines 52 - 55, Update post_install to write
the .microbridge-brew ownership marker beside the installed Microbridge.app
rather than inside its signed bundle. Keep the resource("ui").stage installation
flow and app path unchanged, and ensure any marker path references use the
external location consistently.

end

# INSTALL.md ships inside the daemon archive when present.
doc.install "INSTALL.md" if File.exist?("INSTALL.md")
end

def post_install
apps = Pathname.new(Dir.home)/"Applications"
apps.mkpath
dest = apps/"Microbridge.app"
# Only replace if missing or previously installed by this formula.
marker = dest/".microbridge-brew"
if dest.exist? && !marker.exist?
ohai "Leaving existing ~/Applications/Microbridge.app in place (not brew-managed)"
Expand Down Expand Up @@ -75,6 +93,9 @@ def caveats
Open the app once (or add Login Items) so the menu bar icon appears:
open ~/Applications/Microbridge.app

Hardware LEDs/keys need a connected Codex Micro (HID packing landing
after device captures). Until then the UI shows Simulator / Detected.

Upgrade: brew update && brew upgrade microbridge
EOS
end
Expand Down
5 changes: 4 additions & 1 deletion INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,16 @@ arch-specific menu bar app). On macOS, releases also include a

```sh
./scripts/install-from-release.sh # latest (prefers DMG on macOS)
./scripts/install-from-release.sh v0.0.1
./scripts/install-from-release.sh v0.1.0
```

Or open the DMG from the GitHub Release page and drag Microbridge into
Applications, then install/start the daemon via Homebrew or the daemon
archive.

**Note:** Homebrew installs **prebuilt** release binaries (not a from-source
Tauri build). The formula checksums are refreshed by CI after each `v*` tag.

## Layout after install

| Path | Purpose |
Expand Down
35 changes: 35 additions & 0 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Privacy

Microbridge is a **local-only** control plane. It does not phone home.

## What stays on your machine

| Data | Where | Why |
|---|---|---|
| Agent session journals | Read from paths like `~/.codex/sessions` and Claude project folders | Derive session titles/state for the menu bar and LED mapping |
| Config | `~/.microbridge/config.toml` | Key source, lighting, appearance |
| Daemon log | `~/.microbridge/daemon.log` (or Homebrew service logs) | Debug |
| Unix socket | `~/.microbridge/microbridged.sock` (mode `0600`) | Local IPC for UI + adapters |

## What we do **not** do

- No telemetry, analytics, or crash upload
- No update pings or cloud accounts
- No network client in the daemon (auditable in `Cargo.lock`)
- No uploading of session text or source code

## Adapters

First-party adapters watch **local** session stores. Community adapters must
follow the same rule (see [docs/adapters.md](docs/adapters.md)): talk only to
local runtimes; no network I/O.

## Hardware

USB presence probing (macOS `system_profiler`) and future HID traffic stay on
the host. Device captures used for reverse-engineering are documented in
[docs/device-hid.md](docs/device-hid.md) and are not sent anywhere by Microbridge.

## Questions

Security-sensitive reports: [SECURITY.md](SECURITY.md).
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Microbridge is a tiny local daemon that bridges AI coding agents — Codex CLI, Claude Code, Cursor, T3 Code, and anything else with an adapter — to the [Work Louder Codex Micro](https://worklouder.cc/). Per-key RGB mirrors live agent state; the keys drive agent actions (approve, reject, interrupt, switch focus). No vendor desktop app required.

> **Status: early alpha.** Protocol v0 with UI/control, in-process Codex/Claude watchers, mock device, `microbridgectl`, and a Tauri companion shell. Real HID packing waits on device captures — see [ROADMAP.md](ROADMAP.md).
> **Status: early public alpha (`v0.1.x`).** Menu bar UI, local daemon, in-process Codex/Claude watchers, and signed macOS packages are shipping. **Real Micro HID packing lands after hardware arrives (target 2026-07-22)** — until then LEDs/keys use Simulator / Detected. See [ROADMAP.md](ROADMAP.md) and [PRIVACY.md](PRIVACY.md).

## Screenshots

Expand Down
23 changes: 20 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
# Security policy

Microbridge runs unprivileged, listens only on a local Unix socket in your
home directory, and performs no network I/O. Still, it sits between your
coding agents and a hardware input device, so we take reports seriously.
Microbridge runs unprivileged on your Mac, listens only on a **local Unix
socket** (mode `0600` under `~/.microbridge/`), and performs **no network I/O**.
Comment on lines +3 to +4
It sits between your coding agents (session journals / adapter IPC) and —
eventually — a USB input device (Codex Micro).

## Scope we care about

- Unauthorized clients attaching to the daemon socket
- Privilege escalation via the menu bar app or daemon
- Unexpected network egress from daemon / first-party adapters
- Leaking session contents beyond the local machine
- Malicious adapter PRs that scrape private Electron internals or add idle
footprint / network I/O (declined on sight — see CONTRIBUTING)

## Reporting

**Report vulnerabilities privately** via GitHub's
[private vulnerability reporting](../../security/advisories/new) on this
repository. Please do not open public issues for security problems.

You can expect an acknowledgment within a week. Supported version: the
latest release (pre-1.0, there are no backports).

## Out of scope (for now)

- Full HID claim / exclusive USB ownership (landing with device captures)
- Guarantees about third-party community adapters you choose to run
4 changes: 2 additions & 2 deletions apps/microbridge-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/microbridge-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "microbridge-ui",
"private": true,
"version": "0.0.1",
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
2 changes: 1 addition & 1 deletion apps/microbridge-ui/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "microbridge-ui"
version = "0.0.1"
version = "0.1.0"
description = "Microbridge menu bar app (primary UI)"
authors = ["Microbridge contributors"]
edition = "2021"
Expand Down
2 changes: 1 addition & 1 deletion apps/microbridge-ui/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Microbridge",
"version": "0.0.1",
"version": "0.1.0",
"identifier": "ai.microbridge.ui",
"build": {
"beforeDevCommand": "npm run dev",
Expand Down
14 changes: 7 additions & 7 deletions apps/microbridge-ui/src/lib/bus.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { DaemonConfig, Snapshot } from "./types";

/** Talks to microbridged via Tauri when available; demo snapshot in browser. */
/** Talks to microbridged via Tauri when available; demo snapshot in browser only. */

const DEMO: Snapshot = {
sessions: [
Expand Down Expand Up @@ -28,8 +28,8 @@ const DEMO: Snapshot = {
],
focused_session_id: "s1",
agent_key_session_ids: ["s1", "s2", "s3", null, null, null],
device_connected: true,
device_name: "mock",
device_connected: false,
device_name: "demo-browser",
config: {
key_source: "most_recent",
pinned_session_ids: [],
Expand Down Expand Up @@ -61,6 +61,10 @@ export async function fetchSnapshot(): Promise<Snapshot> {
return snap ?? DEMO;
}

export function isDemoSnapshot(snapshot: Snapshot): boolean {
return snapshot.device_name === "demo-browser";
}

export async function setConfig(config: DaemonConfig): Promise<DaemonConfig> {
const next = await invoke<DaemonConfig>("set_config", { config });
return next ?? config;
Expand Down Expand Up @@ -96,7 +100,3 @@ export async function subscribeSnapshot(
return () => window.clearInterval(id);
}
}

export function isDemoSnapshot(snapshot: Snapshot): boolean {
return snapshot.device_name === "mock" && snapshot.sessions.some((s) => s.id === "s1");
}
Loading
Loading