Motty
原生桌面 SSH 工作台:终端、SFTP/SCP、端口转发、VNC 与 RDP 远程桌面、AI 助手都在一个窗口里。Go 编写,没有 Electron,空闲时保持安静。免费开源,支持 macOS、Windows 和 Linux。
翻譯準備中
Experimenting with bare-android
翻譯準備中
正在載入完整 README。
檢視完整 README ↗One soul, many platforms. One codebase and identity across Android, iOS, web, and desktop.
Docs: https://anderscan.github.io/ekrooh
The boring bootstrap for cross-platform apps on the Bare runtime (holepunch): a binary wire protocol, plugin kernel, RPC messenger, transports, and native host bridges, plus a reference app.
vision.md: what this project is and is not.AGENTS.md: how to develop it and the canonical commands.CONTRIBUTING.md: how to contribute.RELEASING.md: how to cut a release.docs/: internal ADRs (docs/adr/) and agent operations; not the public site.The frontend (browser or WebView) is written as if only a backend is reachable over a WebSocket carrying binary framed messages.
ws://location.host.VITE_TRANSPORT_MODE=mock selects the mock transport (tests).VITE_BARE_WS_URL.The UI never branches on worklets, Bare, or host IPC. @ekrooh/bare/core
describes the wire protocol, not the runtime that implements it.
| Term | Meaning |
|---|---|
| Frontend | Web UI: Vite bundle in the browser, or the same bundle in a WebView. |
| Backend | Runs the core bundle and speaks the framed protocol with the frontend. |
| Host | Native shell (Android/iOS): starts the backend, owns system APIs, injects the session bootstrap. |
core/: wire codec, plugin router, RPC messenger, host IPC (core/messages),
loopback server (core/server), worklet entry (core/main.core.ts).plugins/: canonical plugins (core.health, core.discovery,
core.permissions, vendor.media) and typed event builders.web/transports/: MessageTransport, WebSocket and mock transports.android/: host library :bare-host (IPC coordinator, plugin registry, WebView client).ios/: host Swift package BareHost (root Package.swift).examples/: web/, android-app/, ios-app/, consumer-basic/.e2e/: Playwright specs on the mock transport. scripts/: dev runner, Playwright wrapper, prebuild fetcher.apps/docs/: public docs site (Astro Starlight, GitHub Pages).templates/, research/: shared deploy templates; design notes.prebuilds/: Bare Kit prebuilds (gitignored build output).The public surface is the exports map in package.json (@ekrooh/bare/core,
/runtime, /plugins, /plugins/*/events, /transports). It points at
compiled ESM and type declarations in dist/ (built with vp pack). Consumers
never see TypeScript source.
| Runtime | Status | Transport | Notes |
|---|---|---|---|
| Browser (dev) | first-class | WebSocket | Vite + local backend (VITE_BARE_WS_URL) |
| Browser (test) | first-class | Mock | Deterministic Playwright runs |
| Android | first-class | WebSocket | Same-origin loopback socket, cookie auth |
| iOS | first-class (reference parity) | WebSocket | Same-origin loopback socket, cookie auth; host ships as source (ios/, SPM) on the release tag |
| Desktop | reference (macOS) | WebSocket | Zig host + system webview + sidecar bare; see examples/desktop-app |
Parity policy: core protocol and plugin contracts are runtime-agnostic. Capabilities are opt-in plugins and may be runtime-specific. Plugins report unsupported behavior with deterministic errors. Feature code calls plugin events, never raw platform APIs.
Binary frame: version (1 byte), type (1 byte), headerLen (2 bytes, BE),
payloadLen (3 bytes, BE), header (UTF-8 JSON), payload (optional).
Implemented by MessageProtocol in core/messages (wire-codec.ts).
Header types: DISPATCH, INVOKE_REQUEST, INVOKE_RESPONSE (responses echo
requestId). dispatch is fire-and-forget; invoke is request/response and
timeout-bound.
Core ships no product features. Plugin IDs are namespaced (app.photos;
core.* and vendor.* are reserved for the framework); events are
plugin-local (health.ping). Runtime adapters: web, android, ios, bare.
Canonical plugins are listed under Repository layout.
Media never crosses the wire (frame cap 16 MiB). vendor.media is the reference
for out-of-band bytes: the host picks or captures a file natively, the worklet
mounts it on the loopback server and returns a URL, and the web layer loads that
URL with cookie auth.
One server (core/server/static-file-server.ts) serves the web app at /, plugin
media mounts, and the WebSocket upgrade. On device it binds 127.0.0.1 on an
ephemeral port. Every request is gated by a per-session token:
以上為 README 的開頭部分。其餘內容請閱讀原文。 檢視完整 README ↗