Status Trio
Status Trio is a native macOS menu bar application that integrates Wi-Fi, battery, and volume into a single compact, configurable menu bar icon. It allows for various custom appearance configurations.
AI translation
A new purpose for your Android, built with your agent.
OtherKotlin · JavaScriptGitHub @sponicgardenOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
BRIDGIT Device Manager for Android — Turn any Android device into an agentic appliance.
Taken from the repository README
Loading the full README.
Read the full README ↗Copyright 2026 Sponic Garden, Inc. Licensed under the Apache License 2.0.
BRIDGIT Device Manager for Android turns an Android phone (Android 11+; Android 10 support is built but not yet device-tested) into a device your own AI agents can use: Claude, Grok, Codex or any agent that can make HTTP or MCP calls. With your OK, paired agents can take a photo, record a short clip, read the screen, send you questions and notices, and more (see More → What can Bridgit do? in the app). Agents reach it over your Tailscale network, or without Tailscale through Bridgit's relay (see Connecting without Tailscale); each agent has its own key, every feature switch starts off, the owner sets each agent's permissions on the phone, and every request is logged with the agent's name. (A developer agent the owner has given App settings access can change supported feature switches; Android permissions and owner-only settings stay with the owner.)
Package cc.bridgit.app, version 1.8.x, where x is the build number (see Releasing). The design and requirements
live in docs/bridgit-spec-v2.md. Public release
work is tracked in docs/release-checklist.md.
For agents: ask the connected phone, not these docs. Call
GET /v1/capabilities first and use a capability only when agent_allowed,
enabled and ready are all true. Then read the generated GET /v1/guide
when you need details, and fetch one card template with
GET /v1/samples?id=... only when you need it. The specs in docs/ are design
history; they don't override what the installed app says.
Version 1.6.0 added device controls, context/sensors, files/sharing, media/OCR/transcription, screen-share grants, usage/Health Connect/notification reads, clipboard approval, and per-agent permissions. See implementation and supported bounds for phone entry points, exact endpoint paths, validation and device limitations.
adb install -r app-release.apk, or open it on the phone.Updates install over the top as long as they are signed with the same key.
bridgit helper).If the app is killed before you copy, reopening it within 30 minutes returns to the prompt (the key is kept encrypted with an Android Keystore key until then). After that, use Rotate key on the agent's page.
Pair with code (in the Add agent menu) is the older way: the phone shows
a 6-digit code for 5 minutes and the agent calls POST /v1/pair {"code":"..."} once to receive its key.
Agent page: Rotate key (old key stops at once and gets
unauthorized with "reason":"key_rotated" for 7 days), Remove
("reason":"agent_removed"), Developer switch, notes, recent activity.
Keep each agent's key in a password manager (username = agent name, password = key, URL = base URL). The phone stores only a salted hash.
After a reboot, Bridgit starts listening at the first unlock (Android sends its boot signal only then), with camera and mic off until you open the app once. Home shows a Fix now banner for that.
Agents that can't join your tailnet (a hosted coding agent, a cloud chat
assistant) can use Bridgit's relay at
https://bridgit-relay.bridgit-app.workers.dev instead.
rly_ token, shown once.POST <address>/jobs?wait=30 with
Authorization: Bearer <token> and a body like
{"deadline": <now in ms + 120000>, "request": {"method": "GET", "path": "/v1/health"}}.
The phone's answer is in response.body_base64. MCP clients use
POST <address>/mcp (protocol 2025-11-25) with the same token.The relay passes only what you allowed for that agent, every call is still
logged on the phone, and the relay keeps no copy of requests or answers. The
phone answers while its relay connection is up; turning the relay off in the
app cuts it off. Details: relay/README.md.
Base URL http://my-phone.tailnet-name.ts.net:8734/v1 (debug builds:
port 8735). Every call except the helper download (GET /v1/tools/bridgit), POST /v1/pair
and GET /v1/adb/setup (setup instructions only, no phone data) needs
Authorization: Bearer brg_.... Every response has a request_id
(also X-Request-Id) that matches its log entry. Full reference:
GET /v1/guide, GET /v1/openapi.json, spec section 7 and Appendix A.
| Endpoint | What |
|---|---|
GET /health | Status, calling agent, version, uptime, battery, address |
GET /capabilities | Per capability: enabled, ready, blocked_reason, details |
GET /guide, GET /openapi.json | Self-description, generated from the code |
GET /tools/bridgit | The command-line helper (no key needed) |
POST /camera/snapshot | {"camera":"rear"|"front","flash":false,"max_width_px":2048}, or size=agent|thumb|large|original; raw=1 for JPEG bytes |
POST /mic/record | {"duration_seconds":1-120} |
GET /gallery/recent?limit=20 | Newest images, metadata only |
GET /gallery/photo/{id} | One image; size=, raw=1 |
GET /image/last?type=any|screenshot|photo | Newest image, sized for AI vision (1568 px, q85); since=&wait=55 waits for the next one |
POST /screen/capture | What's on screen now, same sizing, nothing saved. Needs More > Access > Screenshots (Android Accessibility, "Bridgit screenshots") and the Screenshots switch in Features |
GET /log | The log, with filters; order=asc&since=<id>&wait=55 follows it live |
GET /log/{id}, /log/stats, /log/export?format=jsonl|csv | One entry, stats, export |
POST /log | {"message":"..."}: a note in the agent channel |
GET /audit | Deprecated alias of /log?channel=audit (new entry shape) |
POST /pair | {"code":"123456"} during a pairing window |
Errors are {"error","message","request_id"} with the codes in spec
Appendix C. POSTs accept Idempotency-Key. Limits: 5 failed sign-ins per
minute per address (valid keys are never blocked), 10 captures per minute
per agent and 30 per address, 4 open wait requests per agent.
The log setting Agents can read: all entries / only their own (Settings → Privacy) applies to every log endpoint. Developer agents always see all.
POST /v1/mcp speaks MCP (Streamable HTTP, JSON replies, no SSE, no
session id) with the agent's normal key. Put this in the project's
.mcp.json and export the key as BRIDGIT_KEY (never paste it into the
file):
{
"mcpServers": {
"bridgit": {
"type": "http",
"url": "http://<phone>:8734/v1/mcp",
"headers": { "Authorization": "Bearer ${BRIDGIT_KEY}" }
}
}
}
<phone> is the MagicDNS name or Tailscale IP from Home (debug builds: port
8735). Every endpoint the agent may use is a tool named from its path:
camera_snapshot, image_last, gallery_photo (id argument),
interactions_send, interactions_get (wait up to 55), speaker_say, …
Left out: /mcp, /pair, /tools/bridgit, /openapi.json; developer-only
tools appear only for developer agents. Tools whose switch is off are still
listed, with a "switched off" note, and fail with capability_disabled.
Images and clips come back as MCP image/audio content; errors as
isError: true with the usual error JSON. Side-effecting tools take an
idempotency_key argument. speaker_play takes a url only (send raw audio
bytes to the HTTP endpoint). Each tool call is checked like a direct call
(switch, developer, capture and rate limits) and gets its own log entry for
the real endpoint, marked via: mcp. Requests with a browser Origin
header are refused (403); batches get 400. The whole JSON-RPC message is
capped at 2 MB, so base64 images sent with interactions_send or
feedback_file must be smaller over MCP than over HTTP.
The owner can give an agent a Callback URL in its agent screen (tailnet
addresses only, unless Settings > Allow internet callbacks is on; then a public
https:// URL works too). Bridgit then POSTs every event for that agent as
{"event": {id, type, at, data}, "agent": "<name>"}, retrying at 10 s, 60 s and
5 min (GET /v1/events always has them). The callback secret (cbs_…) is shown
once when the URL is saved. Check the signature like this:
import hmac, hashlib, time
def verify(secret: str, headers, raw_body: bytes) -> bool:
ts = headers["X-Bridgit-Timestamp"]
want = "sha256=" + hmac.new(secret.encode(), ts.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
fresh = abs(time.time() - int(ts)) <= 300 # also drop X-Bridgit-Event-Id values you've already seen
return fresh and hmac.compare_digest(want, headers["X-Bridgit-Signature"])
POST /v1/callback/test sends a signed callback.test event right away and
returns {delivered, http_status, host} (502 callback_failed if the
receiver didn't answer 2xx).
rly_ tokens, are refused on the Tailscale listener,
and reach only the endpoints the owner allowed for them.brg_ + 43 base64url characters (256 random bits), one per agent,
stored only as salted SHA-256 hashes. The prefix lets secret scanners
catch leaked keys.Unless turned off under Settings > Privacy > Send feature-use tallies, the app
counts how often each feature is used (api:GET /v1/health:ok, mcp:...,
screen:home, switch:camera:on) and about once a day POSTs the counts to
bridgit.cc/api/usage (core/Usage.kt, website/usage.mjs). A batch is only
a one-off nonce, the version code and the counts: no install id, agent names,
times, parameters or content. Keys use endpoint path templates and must match
a fixed pattern, so free text can't get in. Turning it off deletes unsent
counts; "See what would be sent" shows them. Debug builds never send.
Official builds send crashes and freezes (ANRs) to the publisher's Sentry
project (decision D32). Builds made without a DSN send nothing and hide the
switch: set -PbridgitSentryDsn=..., BRIDGIT_SENTRY_DSN, or sentryDsn= in
keystore.properties to point a build at your own project. Sent: the error and stack, app version,
Android version and basic device facts (model, battery, memory). Not sent:
screenshots, photos, keys, request data, install ids or sessions; the project
drops the IP address and the city inferred from it. Turn it off on
the phone under Settings > Privacy > Send crash reports.
The team gets each crash as a bug report in its bug capture system (see
feedback/).
Needs JDK 17 and the Android SDK (platform 35, build-tools 35).
export JAVA_HOME=/path/to/jdk-17
export ANDROID_HOME=/path/to/android-sdk # or sdk.dir in local.properties
./gradlew assemblePrivateDebug # app/build/outputs/apk/private/debug/app-private-debug.apk
./gradlew assemblePrivateRelease # app/build/outputs/apk/private/release/app-private-release.apk
Release signing reads keystore.properties (gitignored); without it the
release APK is unsigned:
storeFile=/path/to/release.jks
passwordFile=/path/to/keystore.pass
keyAlias=<alias>
sentryDsn=<optional Sentry DSN>
Official releases are signed with the certificate whose SHA-256 is
6bbd482d18efdc58ca28de8358405602ebb0405b479cabd36b37ff8242042f61. A build
signed with another key can't update an installed official build; uninstall
first (this wipes the agents and the log).
Icon (1.4.0): a phone outline with a mint "B" ribbon and cursor, as an adaptive icon
(res/mipmap-*/ic_launcher_foreground.png on #022124, plus a monochrome layer for themed
icons), generated by tools/make-launcher-icon.py from the original artwork.
Publishing a release (maintainers): tools/publish-apk.sh --notes "..." builds, signs, checks the
certificate, uploads the APK and latest.json to the public R2 host (bridget/), verifies the
download and creates the GitHub release. Its settings live in tools/publish.env (gitignored; see
tools/publish.env.example). --dry-run checks everything without uploading.
Debug builds install alongside as cc.bridgit.app.debug, listen on port
8735, and include an ADB-only test hook (DebugSetupReceiver, protected by the DUMP permission
only the ADB shell holds) that sets toggles and adds a test agent without
tapping the screen. Release builds don't contain it.
./gradlew testPrivateDebugUnitTest.BRIDGIT_TEST_PHONE=<tailscale-ip> BRIDGIT_TEST_ADB=<ip>:5555 tools/acceptance.sh --apk app/build/outputs/apk/debug/app-debug.apk.
It writes docs/acceptance/<date>-<version>-debug.md with PASS, FAIL or
UNVERIFIED per check. Camera and mic checks need the phone unlocked.Master spec: docs/bridgit-spec-v2.md (27 Oct 2026) has the requirement IDs, API shapes, screens, roadmap and open questions. These older specs are kept for history; all of them have shipped, sometimes with different paths or parameters, so check the phone's /v1/guide for the current shape:
docs/spec-per-agent-permissions.md: separate permissions per agent.docs/spec-flashlight.md: agents can turn the flashlight on (auto-off).docs/spec-screenshot.md: screenshots on request.docs/spec-access-screen.md: the Access screen listing every Android setting
Bridgit needs, with links straight to each switch.docs/spec-agent-inbox.md: interactions (notices and questions, optionally
spoken aloud), files sent to the phone, and things the owner shares to an
agent. Works for agents without webhooks (long polling).Copyright 2026 Sponic Garden, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License in LICENSE or at http://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Third-party components keep their own licences: Ktor and Netty (Apache 2.0), CameraX and AndroidX (Apache 2.0), Material Components (Apache 2.0), ZXing (Apache 2.0), Kotlin coroutines (Apache 2.0) and the Sentry Android SDK (MIT). Google ML Kit text recognition is not open source: it is distributed under the ML Kit Terms of Service.
The "What can Bridgit do?" illustrations were generated with an AI image model
from the prompts in docs/whatcan-image-prompts.md.
The repository is https://github.com/sponicgarden/bridgit and the selected website domain is https://bridgit.cc. The public product name is BRIDGIT Device Manager for Android.
The Android package is cc.bridgit.app. It replaced an earlier package id, so builds from before that change do not upgrade in place: uninstall the old app and pair agents again.
Since API revision 17 (1.6.0, 2 Nov 2026) the API is spelled Bridgit too, and the old spellings keep working. HTML cards call window.Bridgit.respond(json).
| New | Still accepted (deprecated) |
|---|---|
X-Bridgit-* response and callback headers | every one is also sent as X-Bridget-* with the same value |
GET /v1/tools/bridgit | GET /v1/tools/bridget |
agent=bridgit in /v1/log | agent=bridget |
"bridgit": 1 in the pairing QR | "bridget": 1 (both are sent) |
-PbridgitApplicationIdSuffix, -PbridgitDebugPort, -PbridgitVersionCode, -PbridgitVersionName, -PbridgitDocsVersion | the -Pbridget… spellings |
Unchanged on purpose: the updater's R2 paths (bridget/latest.json, bridget/bridget-<version>.apk, bridget/api.html), which installed apps poll; the bridget-v<version> release tags; Android Keystore aliases (bridget_callback_secret, bridget_pending_key), whose keys can't be moved; and wake-word examples like "hey bridget", the spelling the offline speech model knows. Use tools/bridgit; tools/bridget remains a compatibility entry point.
New CLI configuration uses ~/.config/bridgit/env and BRIDGIT_KEY, BRIDGIT_HOST, BRIDGIT_PORT (or BRIDGIT_ENV for an explicit config path). The old names remain accepted; new names take precedence.
The private control build preserves the existing v1 API and adds scoped control sessions, reviewed actions and bounded sequences, durable operations/events, native workflows, selected notification replies, local text/translation tools, viewer-scoped JPEG monitoring and explicit multi-device routing. Start with /v2/capabilities and /v2/tools; the new MCP endpoint is /mcp. Open Agent control and appliances from Home’s menu to grant new scopes and review requests. New scopes start denied.
See implementation and acceptance for all twenty feature families, test evidence, distribution limits and hardware checks still needed. The website describes the published release, read from the public latest.json at build time. The normal signed update is built and published only by tools/publish-apk.sh (Gradle refuses release packaging without it); the limited variant is assemblePlayRelease and does not include the Accessibility or notification-listener declarations. Devices get production builds from the in-app updater, never by sideloading. Builds are numbered from git: versionCode is the commit count and versionName is <major>.<minor>.<count>, with the major and minor in version.properties. Only builds of main are published; see Releasing.