The official Relay connects devices signed in to the same GitHub identity. GitHub identity is shared with the marketplaces. Users sign in from OpenBitFun; they do not create a Relay account or deploy a server.
The official endpoint is https://remote.openbitfun.com/v/1.0.0. This release is deployed with
its own process, database, assets, and reverse-proxy location. An existing
/relay deployment remains on its existing binary and data directory.
The Relay forwards opaque encrypted messages. Each device generates an X25519 private key locally and registers its public key after GitHub identity verification. Peers obtain public keys through the authenticated same-account directory and derive an AES-GCM key with X25519 and HKDF-SHA256. The Relay does not receive device private keys or upload copies of settings and sessions. Sessions and files are read from the owning online device on demand.
Selecting Same network starts the same Relay implementation inside the
Desktop host at http://<LAN-IP>:9700. Its SQLite database is local to that
host (<product-home>/relay-v1.0.0/local-server/relay.db), separate from the
official server database. Login, device registration, device discovery,
public-key lookup, RPC and presence all use the selected Relay endpoint.
The two modes differ only in endpoint and host startup; no device traffic is
forwarded from the local Relay to the official Relay.
Both modes verify GitHub identity through auth.openbitfun.com, so signing in
requires internet access. An invitation contains only the selected endpoint
and device id (/#/pair?did=<device-id>); scanning it grants no authority.
The controller must sign in and resolve that id in its same-account directory.
Anonymous room pairing and tunnel-provider startup have been removed.
SSH and Docker workspace connections remain independent of Relay login.
Self-hosting is supported through source and deployment scripts. The public mode uses a fixed official endpoint; it has no deployment wizard or editable Relay URL. A private Relay therefore needs a matching client build.
- Fork this workspace and read
CONTRIBUTING.md. Build the Relay and mobile controller from the same revision. Keep your fork's changes in source control. - Select your HTTPS endpoint in
product-domains/src/account.rs, then align the frontend constants insrc/web-ui/src/infrastructure/remote-connect/remoteConnectionState.tsandsrc/mobile-web/src/services/pairingLink.ts. Native clients have matching constants in KMPcore-transport/AccountDeviceLink.ktand HarmonyOSservices/AccountDeviceLink.ets; update the HarmonyOS account-link parser too. Search forhttps://remote.openbitfun.com/v/1.0.0to verify every runtime reference and corresponding test before building your distribution. - Decide who owns identity. You can retain the official GitHub identity
authority, or run the shared identity service
with your own GitHub OAuth application. For an independent authority, change
IDENTITY_ME_URLinrelay-service/src/identity.rsandDEFAULT_ACCOUNT_API_URLinservices-integrations/src/account_identity/mod.rstogether, and adapt the market sign-in links and callback/completion host.OPENBITFUN_ACCOUNT_API_URLoverrides the desktop/CLI identity API for development; the previousOPENBITFUN_MINIAPP_MARKET_API_URLalias remains readable. Relay never accepts an identity authority from a client request. - Use separate persistent data and asset directories, configure exact browser CORS origins, then put the service behind your own TLS reverse proxy. Build and exercise two devices using the same GitHub identity before distributing your fork. The public web controller must come from that matching build.
The scripts remain in this directory: deploy.sh deploys on the machine where
it runs, common.sh contains Docker/health helpers, and mirror.sh and
release-download.sh support mirrors and published images. Inspect
bash deploy.sh --help first. For fork code use
bash deploy.sh --build-from-source --global-mirror; the default image path
pulls a published upstream release, so it will not include your modifications.
An empty account database is normal: successful GitHub verification creates an
identity. Do not run retired add-user or password-reset commands.
The legacy script uses its own Compose project and defaults. For a fresh versioned deployment, prefer the isolated v1 Compose project and adapt its host paths, bind port, proxy host, and trusted upstream ranges to your infrastructure. Never reuse production data directories or an existing container name for a development deployment. There is no need to restore the removed deployment wizard to operate these scripts.
This directory owns the official service binary and maintenance tools. The
shared HTTP/WebSocket implementation lives in src/crates/services/relay-service.
Set RELAY_DB_PATH to a persistent SQLite database before starting the service.
Startup fails if it is missing; anonymous public relay mode is unsupported.
The service validates OpenBitFun access tokens against the fixed GitHub identity
authority at https://auth.openbitfun.com/api/v1/me.
cargo build --release -p openbitfun-relay-server
RELAY_PORT=9700 RELAY_DB_PATH=/var/lib/openbitfun-relay-v1/relay.db \
RELAY_ASSET_DIR=/var/lib/openbitfun-relay-v1/assets \
./target/release/openbitfun-relay-serverUse the isolated v1 Compose project.
Set RELAY_LISTEN_ADDR=127.0.0.1:19700 with host networking so the service can
verify the immediate loopback proxy peer. Invalid listener values fail startup.
Expose only the TLS reverse proxy. Keep the database and asset paths distinct from older deployments.
relay-admin supports listing and explicitly deleting accounts; GitHub login
creates identities. Password provisioning, password reset, and user-entered
Relay server URLs are retired.
These limits protect the service independently of reverse-proxy configuration. They are implemented in the shared Relay service, not in the agent loop.
| Resource | Limit and overload behavior |
|---|---|
| Authentication request body | 16 KiB; oversized bodies return 413 |
| Buffered HTTP request bodies | 512 MiB total reserved before buffering; overload returns 503 |
| Concurrent HTTP API requests | 2,048; overload returns 503 |
| Body read / device RPC handler | 15 seconds / 130 seconds |
| HTTP request rate | 6,000/minute per source IP; device APIs also per account; overload returns 429 |
| GitHub authorization start / poll | 10 / 120 per minute per IP |
| Identity exchanges | 10/minute per IP; 64 concurrent outbound identity requests |
| WebSocket upgrades | 120/minute per IP; 4,096 active sockets globally |
| WebSocket authentication | Must complete within 10 seconds |
| WebSocket ingress | 16 KiB per message/frame; 4 KiB read buffer per connection |
| WebSocket messages | 12,000/minute per connection |
| WebSocket outgoing queue | 128 messages per socket, 256 MiB total queued/writing bytes |
| Slow WebSocket writes | Close after a 15-second write timeout |
| RPC response memory | 256 MiB covering queued payloads and serialized replies, retained until read or disconnect |
| Pending device RPCs | 2,048 globally; 64 per account; cancellation releases capacity |
| Registered devices / active credentials | 64 / 256 per account; database-atomic admission |
| Device RPC ciphertext | 48 MiB, with JSON envelope allowance |
Existing devices can reconnect at the registration limit. Idempotent token replays remain valid at the credential limit. Limits never delete a user's session, device, workspace, or other product data.
Bearer authentication precedes body buffering on device APIs. Device discovery, public-key lookup, message routing, and RPC correlation all enforce account ownership. Delegated controller credentials cannot register sockets, mint more credentials, or delete devices; revoking their parent device revokes them. Account-enabled services reject the retired anonymous pairing-room endpoints.
The identity HTTP client rejects redirects, bounds response size and duration,
and never accepts a caller-provided identity authority. Browser CORS uses an
explicit origin list; wildcard CORS is rejected by the standalone host when
account APIs are enabled. Published Pages must use an origin separate from the
account sign-in surface. Without both isolated origins, the standalone host
returns 503 for /api/pages, /api/page-auth, and /p routes.
Application limits do not replace network-layer protection. Public deployment also requires bounded proxy connections and request bodies, TLS, upstream DDoS protection, and alerts for saturation, rejected requests, and disk growth. Do not log Authorization headers, OAuth transaction secrets, tokens, request bodies, or URL query strings containing sign-in state.
Strip only the new version prefix when forwarding. Do not replace the existing
/relay location. The proxy must overwrite forwarded IP headers with its own
observed source address, and the upstream port must be unreachable externally.
The Relay trusts forwarded client IPs only from an immediate loopback peer.
location ^~ /v/1.0.0/ {
proxy_pass http://127.0.0.1:19700/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
client_max_body_size 49m;
client_body_timeout 15s;
proxy_read_timeout 140s;
proxy_send_timeout 30s;
}Define the standard $connection_upgrade map and deployment-specific
limit_req/limit_conn zones in the owning Nginx configuration. Tune worker and
file-descriptor limits against measured concurrent sockets; daily active users
alone are not a capacity measurement. Preserve ordinary streaming and attachment
traffic in the load test when tuning rate limits.
Before opening the new location, exercise invalid/expired credentials, cross-account access, concurrent quota exhaustion, oversized/slow bodies, unauthenticated and slow-reader sockets, cancellation, reconnect, and normal streaming. Verify that overload returns promptly and releases memory. Confirm that the old service remains healthy and that rollback only removes the new location and process.
| Endpoint | Purpose |
|---|---|
GET /health, GET /api/info |
Health and service version |
POST /api/auth/github/start, /api/auth/github/poll |
Browser GitHub authorization |
POST /api/auth/login |
Exchange verified identity for a keyed device credential |
POST /api/auth/logout |
Revoke a credential |
POST /api/auth/delegate |
Issue a separately keyed, restricted controller credential |
POST /api/auth/provision-device |
Authorized SSH host bootstrap |
GET /api/devices |
Same-account device directory |
GET /api/devices/{id}/key |
Same-account device public key |
POST /api/devices/{id}/rpc |
Encrypted request/response forwarding |
POST /api/devices/{id}/messages |
Authenticated device responses and same-account messages |
DELETE /api/devices/{id} |
Explicit device removal and revocation |
GET /ws |
Authenticated device presence and encrypted messages |
auth_connect verifies a device token before WebSocket routing is enabled.
Devices receive requests over WebSocket and submit payloads through the HTTP
messages endpoint, which reserves memory before buffering. Correlation replies
must come from the expected account and device. Small legacy device_message
envelopes remain recognized; attachment-sized WebSocket ingress is rejected.
The versioned client and server must be deployed together for this transport.
RELAY_PORT, RELAY_DB_PATH, RELAY_STATIC_DIR, RELAY_ROOM_WEB_DIR,
RELAY_ASSET_STORE_MAX_BYTES, and RELAY_CORS_ALLOW_ORIGINS are operator
settings. RELAY_PAGE_PUBLIC_BASE_URL and RELAY_PAGE_AUTH_BASE_URL must be set
together and use distinct origins when protected Pages are deployed.
cargo test -p openbitfun-relay-server --bin openbitfun-relay-server
cargo test -p openbitfun-relay-service
cargo check -p openbitfun-relay-server
node scripts/check-core-boundaries.mjsUnit and integration tests are local evidence. Record live remote-control, peer-device, remote-workspace, and detached-dispatch validation separately.