Repository files navigation

Moblink (Rust🦀 edition)

Use spare devices as extra SRTLA bonding connections. The same functionality is part of Moblin on iOS.

Originally inspired by the Moblink Kotlin/Android code.

Features

  • WebSocket Connection: Connects to Moblin via WebSocket (e.g., wss://...)
  • Auth Handling: Implements the same challenge–response authentication logic as the Android client
  • UDP Relay: Forwards UDP packets between the remote streamer and a local destination.
  • mDNS: Automatically connect to nearby Moblink devices.

Requirements

  • Rust (stable, e.g., 1.85+)
  • Cargo (for building)

Usage

Use on Belabox

ssh user@belabox.local

password: See belabox under advanced / developer

ssh to your belabox and run this:

wget -q -O - https://raw.githubusercontent.com/datagutt/moblink-rust/refs/heads/main/install/belabox/install.sh | sudo sh

Then start a Moblink relay and open http://belabox.local/ in your browser.

If not, make sure your relay has a short name. At most ~10 characters --> Make sure the password is "1234" and put it on AUTO on the android phone.

Build

# 1. Clone this repository (or copy the code)
git clone https://github.com/datagutt/moblink-rust.git
cd moblink-rust
# Set nightly (optional)
rustup override set nightly
# 2. Build the project
cargo build --release

Run Relay

./target/release/moblink-relay \
--name "RelayName" \
--id "UUID" \
--streamer-url ws://192.168.1.2:7777 \
--password "secret123" \
--bind-address 192.168.1.10
--log-level debug

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the relayHostname--name CameraRelay1
--idUUID to identify the RelayGenerated--id UUID
--streamer-urlWebSocket URL to connect to the streamerNone (multicast DNS)--streamer-url wss://example.com/ws
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--bind-addressLocal modem IP address to bind for UDP socket0.0.0.0--bind-address 192.168.1.10
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json

Relay status (today only battery percentage) is sent to the streamer if --status-executable or --status-file is given and outputting a valid JSON object as seen above.

Run Relay Service

moblink-relay connects a single network interface. moblink-relay-service is a supervisor that watches every eligible network interface and starts one relay per interface automatically. It is useful on machines with several uplinks (for example a router with Ethernet, Wi-Fi, and cellular), where each uplink becomes an independent bonding path.

./target/release/moblink-relay-service \
--password "secret123" \
--interface-name-override eth0=WAN \
--interface-name-override wwan0=LTE \
--runtime-status-file /tmp/moblink-relay-status.json

By default the service discovers streamers over multicast DNS. Pass one or more --streamer-url to connect to fixed streamers instead, which is helpful where multicast DNS is unreliable (for example across router uplinks behind NAT).

Command-Line Arguments

ArgumentDescriptionDefaultExample
--passwordPassword used in the challenge response authentication1234--password mySecret
--network-interfaces-to-allowRegex of interface names to allow (^/$ anchors added automatically). Repeatable. Localhost is never allowed.All--network-interfaces-to-allow 'eth.*'
--network-interfaces-to-ignoreRegex of interface names to ignore (^/$ anchors added automatically). Repeatable.None--network-interfaces-to-ignore 'docker.*'
--streamer-urlConnect to this streamer URL directly instead of discovering over multicast DNS. Repeatable.None (multicast DNS)--streamer-url ws://192.168.1.2:7777
--interface-name-overrideRename the relay shown in the Moblin app for a given interface, as interface=label. Repeatable.None--interface-name-override eth0=WAN
--runtime-status-fileWrite relay and streamer state as JSON to this file, for external UIs to display connection status and streamer IPs.None--runtime-status-file status.json
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json
--databaseFile storing the per-interface relay identitiesmoblink-relay-service.json--database /etc/moblink/relays.json
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug

The runtime status file is rewritten whenever relays or streamers change. It contains a JSON object with connected, manual_streamer, a streamers array, and a relays array (each relay reporting its interface name, interface address, and the streamer name, URL, and host it serves).

Run Streamer

./target/release/moblink-streamer \
--websocket-server-address 192.168.1.2 \
--destination-address 172.120.50.214 \
--destination-port 5000

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the streamerHostname--name CameraRelay1
--idId to identify the streamer using multicast DNSHostname--id UUID
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--websocket-server-addressLocal IP address to bind websocket server to--websocket-server-address 192.168.1.10
--websocket-server-portLocal port to bind the websocket server to7777--websocket-server-port 7778
--tun-ip-networkTUN IP network (CIDR notation). TUN network interfaces will be assigned IP addresses from this network.10.3.3.0/24--tun-ip-network 10.1.1.0/24
--destination-addressStreaming destination address--status-file status.json
--destination-portStreaming destination port--status-file status.json

Relay Architecture

  1. WebSocket Connection

    • Establishes a WebSocket to streamer_url, or if not provided, tries to find nearby Moblink streamers through multicast DNS.
    • Handles “Hello” messages, calculates authentication, and sends an “Identify” message.
  2. Handling Requests

    • When a startTunnel request is received, the relay spawns two async tasks:
      • (relay_to_destination): Forwards traffic from streamer → destination
      • (relay_to_streamer): Forwards traffic from destination → streamer
  3. UDP Binding

    • By default, it binds a UDP socket to whatever we deem to be the main network interface.

FAQ

Q: How do I integrate this into my own application?
A: Use the moblink-rust crate


License: This project is distributed under the terms of the MIT license.

Enjoy using Moblink!

About

Use spare devices as extra SRTLA bonding connections. Rust reimplementation of the Moblin iOS feature

Resources

Stars

32 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Moblink (Rust🦀 edition)

Use spare devices as extra SRTLA bonding connections. The same functionality is part of Moblin on iOS.

Originally inspired by the Moblink Kotlin/Android code.

Features

  • WebSocket Connection: Connects to Moblin via WebSocket (e.g., wss://...)
  • Auth Handling: Implements the same challenge–response authentication logic as the Android client
  • UDP Relay: Forwards UDP packets between the remote streamer and a local destination.
  • mDNS: Automatically connect to nearby Moblink devices.

Requirements

  • Rust (stable, e.g., 1.85+)
  • Cargo (for building)

Usage

Use on Belabox

ssh user@belabox.local

password: See belabox under advanced / developer

ssh to your belabox and run this:

wget -q -O - https://raw.githubusercontent.com/datagutt/moblink-rust/refs/heads/main/install/belabox/install.sh | sudo sh

Then start a Moblink relay and open http://belabox.local/ in your browser.

If not, make sure your relay has a short name. At most ~10 characters --> Make sure the password is "1234" and put it on AUTO on the android phone.

Build

# 1. Clone this repository (or copy the code)
git clone https://github.com/datagutt/moblink-rust.git
cd moblink-rust
# Set nightly (optional)
rustup override set nightly
# 2. Build the project
cargo build --release

Run Relay

./target/release/moblink-relay \
--name "RelayName" \
--id "UUID" \
--streamer-url ws://192.168.1.2:7777 \
--password "secret123" \
--bind-address 192.168.1.10
--log-level debug

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the relayHostname--name CameraRelay1
--idUUID to identify the RelayGenerated--id UUID
--streamer-urlWebSocket URL to connect to the streamerNone (multicast DNS)--streamer-url wss://example.com/ws
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--bind-addressLocal modem IP address to bind for UDP socket0.0.0.0--bind-address 192.168.1.10
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json

Relay status (today only battery percentage) is sent to the streamer if --status-executable or --status-file is given and outputting a valid JSON object as seen above.

Run Relay Service

moblink-relay connects a single network interface. moblink-relay-service is a supervisor that watches every eligible network interface and starts one relay per interface automatically. It is useful on machines with several uplinks (for example a router with Ethernet, Wi-Fi, and cellular), where each uplink becomes an independent bonding path.

./target/release/moblink-relay-service \
--password "secret123" \
--interface-name-override eth0=WAN \
--interface-name-override wwan0=LTE \
--runtime-status-file /tmp/moblink-relay-status.json

By default the service discovers streamers over multicast DNS. Pass one or more --streamer-url to connect to fixed streamers instead, which is helpful where multicast DNS is unreliable (for example across router uplinks behind NAT).

Command-Line Arguments

ArgumentDescriptionDefaultExample
--passwordPassword used in the challenge response authentication1234--password mySecret
--network-interfaces-to-allowRegex of interface names to allow (^/$ anchors added automatically). Repeatable. Localhost is never allowed.All--network-interfaces-to-allow 'eth.*'
--network-interfaces-to-ignoreRegex of interface names to ignore (^/$ anchors added automatically). Repeatable.None--network-interfaces-to-ignore 'docker.*'
--streamer-urlConnect to this streamer URL directly instead of discovering over multicast DNS. Repeatable.None (multicast DNS)--streamer-url ws://192.168.1.2:7777
--interface-name-overrideRename the relay shown in the Moblin app for a given interface, as interface=label. Repeatable.None--interface-name-override eth0=WAN
--runtime-status-fileWrite relay and streamer state as JSON to this file, for external UIs to display connection status and streamer IPs.None--runtime-status-file status.json
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json
--databaseFile storing the per-interface relay identitiesmoblink-relay-service.json--database /etc/moblink/relays.json
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug

The runtime status file is rewritten whenever relays or streamers change. It contains a JSON object with connected, manual_streamer, a streamers array, and a relays array (each relay reporting its interface name, interface address, and the streamer name, URL, and host it serves).

Run Streamer

./target/release/moblink-streamer \
--websocket-server-address 192.168.1.2 \
--destination-address 172.120.50.214 \
--destination-port 5000

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the streamerHostname--name CameraRelay1
--idId to identify the streamer using multicast DNSHostname--id UUID
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--websocket-server-addressLocal IP address to bind websocket server to--websocket-server-address 192.168.1.10
--websocket-server-portLocal port to bind the websocket server to7777--websocket-server-port 7778
--tun-ip-networkTUN IP network (CIDR notation). TUN network interfaces will be assigned IP addresses from this network.10.3.3.0/24--tun-ip-network 10.1.1.0/24
--destination-addressStreaming destination address--status-file status.json
--destination-portStreaming destination port--status-file status.json

Relay Architecture

  1. WebSocket Connection

    • Establishes a WebSocket to streamer_url, or if not provided, tries to find nearby Moblink streamers through multicast DNS.
    • Handles “Hello” messages, calculates authentication, and sends an “Identify” message.
  2. Handling Requests

    • When a startTunnel request is received, the relay spawns two async tasks:
      • (relay_to_destination): Forwards traffic from streamer → destination
      • (relay_to_streamer): Forwards traffic from destination → streamer
  3. UDP Binding

    • By default, it binds a UDP socket to whatever we deem to be the main network interface.

FAQ

Q: How do I integrate this into my own application?
A: Use the moblink-rust crate


License: This project is distributed under the terms of the MIT license.

Enjoy using Moblink!

About

Use spare devices as extra SRTLA bonding connections. Rust reimplementation of the Moblin iOS feature

Resources

Stars

32 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Moblink (Rust🦀 edition)

Use spare devices as extra SRTLA bonding connections. The same functionality is part of Moblin on iOS.

Originally inspired by the Moblink Kotlin/Android code.

Features

  • WebSocket Connection: Connects to Moblin via WebSocket (e.g., wss://...)
  • Auth Handling: Implements the same challenge–response authentication logic as the Android client
  • UDP Relay: Forwards UDP packets between the remote streamer and a local destination.
  • mDNS: Automatically connect to nearby Moblink devices.

Requirements

  • Rust (stable, e.g., 1.85+)
  • Cargo (for building)

Usage

Use on Belabox

ssh user@belabox.local

password: See belabox under advanced / developer

ssh to your belabox and run this:

wget -q -O - https://raw.githubusercontent.com/datagutt/moblink-rust/refs/heads/main/install/belabox/install.sh | sudo sh

Then start a Moblink relay and open http://belabox.local/ in your browser.

If not, make sure your relay has a short name. At most ~10 characters --> Make sure the password is "1234" and put it on AUTO on the android phone.

Build

# 1. Clone this repository (or copy the code)
git clone https://github.com/datagutt/moblink-rust.git
cd moblink-rust
# Set nightly (optional)
rustup override set nightly
# 2. Build the project
cargo build --release

Run Relay

./target/release/moblink-relay \
--name "RelayName" \
--id "UUID" \
--streamer-url ws://192.168.1.2:7777 \
--password "secret123" \
--bind-address 192.168.1.10
--log-level debug

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the relayHostname--name CameraRelay1
--idUUID to identify the RelayGenerated--id UUID
--streamer-urlWebSocket URL to connect to the streamerNone (multicast DNS)--streamer-url wss://example.com/ws
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--bind-addressLocal modem IP address to bind for UDP socket0.0.0.0--bind-address 192.168.1.10
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json

Relay status (today only battery percentage) is sent to the streamer if --status-executable or --status-file is given and outputting a valid JSON object as seen above.

Run Relay Service

moblink-relay connects a single network interface. moblink-relay-service is a supervisor that watches every eligible network interface and starts one relay per interface automatically. It is useful on machines with several uplinks (for example a router with Ethernet, Wi-Fi, and cellular), where each uplink becomes an independent bonding path.

./target/release/moblink-relay-service \
--password "secret123" \
--interface-name-override eth0=WAN \
--interface-name-override wwan0=LTE \
--runtime-status-file /tmp/moblink-relay-status.json

By default the service discovers streamers over multicast DNS. Pass one or more --streamer-url to connect to fixed streamers instead, which is helpful where multicast DNS is unreliable (for example across router uplinks behind NAT).

Command-Line Arguments

ArgumentDescriptionDefaultExample
--passwordPassword used in the challenge response authentication1234--password mySecret
--network-interfaces-to-allowRegex of interface names to allow (^/$ anchors added automatically). Repeatable. Localhost is never allowed.All--network-interfaces-to-allow 'eth.*'
--network-interfaces-to-ignoreRegex of interface names to ignore (^/$ anchors added automatically). Repeatable.None--network-interfaces-to-ignore 'docker.*'
--streamer-urlConnect to this streamer URL directly instead of discovering over multicast DNS. Repeatable.None (multicast DNS)--streamer-url ws://192.168.1.2:7777
--interface-name-overrideRename the relay shown in the Moblin app for a given interface, as interface=label. Repeatable.None--interface-name-override eth0=WAN
--runtime-status-fileWrite relay and streamer state as JSON to this file, for external UIs to display connection status and streamer IPs.None--runtime-status-file status.json
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json
--databaseFile storing the per-interface relay identitiesmoblink-relay-service.json--database /etc/moblink/relays.json
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug

The runtime status file is rewritten whenever relays or streamers change. It contains a JSON object with connected, manual_streamer, a streamers array, and a relays array (each relay reporting its interface name, interface address, and the streamer name, URL, and host it serves).

Run Streamer

./target/release/moblink-streamer \
--websocket-server-address 192.168.1.2 \
--destination-address 172.120.50.214 \
--destination-port 5000

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the streamerHostname--name CameraRelay1
--idId to identify the streamer using multicast DNSHostname--id UUID
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--websocket-server-addressLocal IP address to bind websocket server to--websocket-server-address 192.168.1.10
--websocket-server-portLocal port to bind the websocket server to7777--websocket-server-port 7778
--tun-ip-networkTUN IP network (CIDR notation). TUN network interfaces will be assigned IP addresses from this network.10.3.3.0/24--tun-ip-network 10.1.1.0/24
--destination-addressStreaming destination address--status-file status.json
--destination-portStreaming destination port--status-file status.json

Relay Architecture

  1. WebSocket Connection

    • Establishes a WebSocket to streamer_url, or if not provided, tries to find nearby Moblink streamers through multicast DNS.
    • Handles “Hello” messages, calculates authentication, and sends an “Identify” message.
  2. Handling Requests

    • When a startTunnel request is received, the relay spawns two async tasks:
      • (relay_to_destination): Forwards traffic from streamer → destination
      • (relay_to_streamer): Forwards traffic from destination → streamer
  3. UDP Binding

    • By default, it binds a UDP socket to whatever we deem to be the main network interface.

FAQ

Q: How do I integrate this into my own application?
A: Use the moblink-rust crate


License: This project is distributed under the terms of the MIT license.

Enjoy using Moblink!

About

Use spare devices as extra SRTLA bonding connections. Rust reimplementation of the Moblin iOS feature

Resources

Stars

32 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Moblink (Rust🦀 edition)

Use spare devices as extra SRTLA bonding connections. The same functionality is part of Moblin on iOS.

Originally inspired by the Moblink Kotlin/Android code.

Features

  • WebSocket Connection: Connects to Moblin via WebSocket (e.g., wss://...)
  • Auth Handling: Implements the same challenge–response authentication logic as the Android client
  • UDP Relay: Forwards UDP packets between the remote streamer and a local destination.
  • mDNS: Automatically connect to nearby Moblink devices.

Requirements

  • Rust (stable, e.g., 1.85+)
  • Cargo (for building)

Usage

Use on Belabox

ssh user@belabox.local

password: See belabox under advanced / developer

ssh to your belabox and run this:

wget -q -O - https://raw.githubusercontent.com/datagutt/moblink-rust/refs/heads/main/install/belabox/install.sh | sudo sh

Then start a Moblink relay and open http://belabox.local/ in your browser.

If not, make sure your relay has a short name. At most ~10 characters --> Make sure the password is "1234" and put it on AUTO on the android phone.

Build

# 1. Clone this repository (or copy the code)
git clone https://github.com/datagutt/moblink-rust.git
cd moblink-rust
# Set nightly (optional)
rustup override set nightly
# 2. Build the project
cargo build --release

Run Relay

./target/release/moblink-relay \
--name "RelayName" \
--id "UUID" \
--streamer-url ws://192.168.1.2:7777 \
--password "secret123" \
--bind-address 192.168.1.10
--log-level debug

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the relayHostname--name CameraRelay1
--idUUID to identify the RelayGenerated--id UUID
--streamer-urlWebSocket URL to connect to the streamerNone (multicast DNS)--streamer-url wss://example.com/ws
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--bind-addressLocal modem IP address to bind for UDP socket0.0.0.0--bind-address 192.168.1.10
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json

Relay status (today only battery percentage) is sent to the streamer if --status-executable or --status-file is given and outputting a valid JSON object as seen above.

Run Relay Service

moblink-relay connects a single network interface. moblink-relay-service is a supervisor that watches every eligible network interface and starts one relay per interface automatically. It is useful on machines with several uplinks (for example a router with Ethernet, Wi-Fi, and cellular), where each uplink becomes an independent bonding path.

./target/release/moblink-relay-service \
--password "secret123" \
--interface-name-override eth0=WAN \
--interface-name-override wwan0=LTE \
--runtime-status-file /tmp/moblink-relay-status.json

By default the service discovers streamers over multicast DNS. Pass one or more --streamer-url to connect to fixed streamers instead, which is helpful where multicast DNS is unreliable (for example across router uplinks behind NAT).

Command-Line Arguments

ArgumentDescriptionDefaultExample
--passwordPassword used in the challenge response authentication1234--password mySecret
--network-interfaces-to-allowRegex of interface names to allow (^/$ anchors added automatically). Repeatable. Localhost is never allowed.All--network-interfaces-to-allow 'eth.*'
--network-interfaces-to-ignoreRegex of interface names to ignore (^/$ anchors added automatically). Repeatable.None--network-interfaces-to-ignore 'docker.*'
--streamer-urlConnect to this streamer URL directly instead of discovering over multicast DNS. Repeatable.None (multicast DNS)--streamer-url ws://192.168.1.2:7777
--interface-name-overrideRename the relay shown in the Moblin app for a given interface, as interface=label. Repeatable.None--interface-name-override eth0=WAN
--runtime-status-fileWrite relay and streamer state as JSON to this file, for external UIs to display connection status and streamer IPs.None--runtime-status-file status.json
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json
--databaseFile storing the per-interface relay identitiesmoblink-relay-service.json--database /etc/moblink/relays.json
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug

The runtime status file is rewritten whenever relays or streamers change. It contains a JSON object with connected, manual_streamer, a streamers array, and a relays array (each relay reporting its interface name, interface address, and the streamer name, URL, and host it serves).

Run Streamer

./target/release/moblink-streamer \
--websocket-server-address 192.168.1.2 \
--destination-address 172.120.50.214 \
--destination-port 5000

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the streamerHostname--name CameraRelay1
--idId to identify the streamer using multicast DNSHostname--id UUID
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--websocket-server-addressLocal IP address to bind websocket server to--websocket-server-address 192.168.1.10
--websocket-server-portLocal port to bind the websocket server to7777--websocket-server-port 7778
--tun-ip-networkTUN IP network (CIDR notation). TUN network interfaces will be assigned IP addresses from this network.10.3.3.0/24--tun-ip-network 10.1.1.0/24
--destination-addressStreaming destination address--status-file status.json
--destination-portStreaming destination port--status-file status.json

Relay Architecture

  1. WebSocket Connection

    • Establishes a WebSocket to streamer_url, or if not provided, tries to find nearby Moblink streamers through multicast DNS.
    • Handles “Hello” messages, calculates authentication, and sends an “Identify” message.
  2. Handling Requests

    • When a startTunnel request is received, the relay spawns two async tasks:
      • (relay_to_destination): Forwards traffic from streamer → destination
      • (relay_to_streamer): Forwards traffic from destination → streamer
  3. UDP Binding

    • By default, it binds a UDP socket to whatever we deem to be the main network interface.

FAQ

Q: How do I integrate this into my own application?
A: Use the moblink-rust crate


License: This project is distributed under the terms of the MIT license.

Enjoy using Moblink!

About

Use spare devices as extra SRTLA bonding connections. Rust reimplementation of the Moblin iOS feature

Resources

Stars

32 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Moblink (Rust🦀 edition)

Use spare devices as extra SRTLA bonding connections. The same functionality is part of Moblin on iOS.

Originally inspired by the Moblink Kotlin/Android code.

Features

  • WebSocket Connection: Connects to Moblin via WebSocket (e.g., wss://...)
  • Auth Handling: Implements the same challenge–response authentication logic as the Android client
  • UDP Relay: Forwards UDP packets between the remote streamer and a local destination.
  • mDNS: Automatically connect to nearby Moblink devices.

Requirements

  • Rust (stable, e.g., 1.85+)
  • Cargo (for building)

Usage

Use on Belabox

ssh user@belabox.local

password: See belabox under advanced / developer

ssh to your belabox and run this:

wget -q -O - https://raw.githubusercontent.com/datagutt/moblink-rust/refs/heads/main/install/belabox/install.sh | sudo sh

Then start a Moblink relay and open http://belabox.local/ in your browser.

If not, make sure your relay has a short name. At most ~10 characters --> Make sure the password is "1234" and put it on AUTO on the android phone.

Build

# 1. Clone this repository (or copy the code)
git clone https://github.com/datagutt/moblink-rust.git
cd moblink-rust
# Set nightly (optional)
rustup override set nightly
# 2. Build the project
cargo build --release

Run Relay

./target/release/moblink-relay \
--name "RelayName" \
--id "UUID" \
--streamer-url ws://192.168.1.2:7777 \
--password "secret123" \
--bind-address 192.168.1.10
--log-level debug

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the relayHostname--name CameraRelay1
--idUUID to identify the RelayGenerated--id UUID
--streamer-urlWebSocket URL to connect to the streamerNone (multicast DNS)--streamer-url wss://example.com/ws
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--bind-addressLocal modem IP address to bind for UDP socket0.0.0.0--bind-address 192.168.1.10
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json

Relay status (today only battery percentage) is sent to the streamer if --status-executable or --status-file is given and outputting a valid JSON object as seen above.

Run Relay Service

moblink-relay connects a single network interface. moblink-relay-service is a supervisor that watches every eligible network interface and starts one relay per interface automatically. It is useful on machines with several uplinks (for example a router with Ethernet, Wi-Fi, and cellular), where each uplink becomes an independent bonding path.

./target/release/moblink-relay-service \
--password "secret123" \
--interface-name-override eth0=WAN \
--interface-name-override wwan0=LTE \
--runtime-status-file /tmp/moblink-relay-status.json

By default the service discovers streamers over multicast DNS. Pass one or more --streamer-url to connect to fixed streamers instead, which is helpful where multicast DNS is unreliable (for example across router uplinks behind NAT).

Command-Line Arguments

ArgumentDescriptionDefaultExample
--passwordPassword used in the challenge response authentication1234--password mySecret
--network-interfaces-to-allowRegex of interface names to allow (^/$ anchors added automatically). Repeatable. Localhost is never allowed.All--network-interfaces-to-allow 'eth.*'
--network-interfaces-to-ignoreRegex of interface names to ignore (^/$ anchors added automatically). Repeatable.None--network-interfaces-to-ignore 'docker.*'
--streamer-urlConnect to this streamer URL directly instead of discovering over multicast DNS. Repeatable.None (multicast DNS)--streamer-url ws://192.168.1.2:7777
--interface-name-overrideRename the relay shown in the Moblin app for a given interface, as interface=label. Repeatable.None--interface-name-override eth0=WAN
--runtime-status-fileWrite relay and streamer state as JSON to this file, for external UIs to display connection status and streamer IPs.None--runtime-status-file status.json
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json
--databaseFile storing the per-interface relay identitiesmoblink-relay-service.json--database /etc/moblink/relays.json
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug

The runtime status file is rewritten whenever relays or streamers change. It contains a JSON object with connected, manual_streamer, a streamers array, and a relays array (each relay reporting its interface name, interface address, and the streamer name, URL, and host it serves).

Run Streamer

./target/release/moblink-streamer \
--websocket-server-address 192.168.1.2 \
--destination-address 172.120.50.214 \
--destination-port 5000

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the streamerHostname--name CameraRelay1
--idId to identify the streamer using multicast DNSHostname--id UUID
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--websocket-server-addressLocal IP address to bind websocket server to--websocket-server-address 192.168.1.10
--websocket-server-portLocal port to bind the websocket server to7777--websocket-server-port 7778
--tun-ip-networkTUN IP network (CIDR notation). TUN network interfaces will be assigned IP addresses from this network.10.3.3.0/24--tun-ip-network 10.1.1.0/24
--destination-addressStreaming destination address--status-file status.json
--destination-portStreaming destination port--status-file status.json

Relay Architecture

  1. WebSocket Connection

    • Establishes a WebSocket to streamer_url, or if not provided, tries to find nearby Moblink streamers through multicast DNS.
    • Handles “Hello” messages, calculates authentication, and sends an “Identify” message.
  2. Handling Requests

    • When a startTunnel request is received, the relay spawns two async tasks:
      • (relay_to_destination): Forwards traffic from streamer → destination
      • (relay_to_streamer): Forwards traffic from destination → streamer
  3. UDP Binding

    • By default, it binds a UDP socket to whatever we deem to be the main network interface.

FAQ

Q: How do I integrate this into my own application?
A: Use the moblink-rust crate


License: This project is distributed under the terms of the MIT license.

Enjoy using Moblink!

About

Use spare devices as extra SRTLA bonding connections. Rust reimplementation of the Moblin iOS feature

Resources

Stars

32 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Moblink (Rust🦀 edition)

Use spare devices as extra SRTLA bonding connections. The same functionality is part of Moblin on iOS.

Originally inspired by the Moblink Kotlin/Android code.

Features

  • WebSocket Connection: Connects to Moblin via WebSocket (e.g., wss://...)
  • Auth Handling: Implements the same challenge–response authentication logic as the Android client
  • UDP Relay: Forwards UDP packets between the remote streamer and a local destination.
  • mDNS: Automatically connect to nearby Moblink devices.

Requirements

  • Rust (stable, e.g., 1.85+)
  • Cargo (for building)

Usage

Use on Belabox

ssh user@belabox.local

password: See belabox under advanced / developer

ssh to your belabox and run this:

wget -q -O - https://raw.githubusercontent.com/datagutt/moblink-rust/refs/heads/main/install/belabox/install.sh | sudo sh

Then start a Moblink relay and open http://belabox.local/ in your browser.

If not, make sure your relay has a short name. At most ~10 characters --> Make sure the password is "1234" and put it on AUTO on the android phone.

Build

# 1. Clone this repository (or copy the code)
git clone https://github.com/datagutt/moblink-rust.git
cd moblink-rust
# Set nightly (optional)
rustup override set nightly
# 2. Build the project
cargo build --release

Run Relay

./target/release/moblink-relay \
--name "RelayName" \
--id "UUID" \
--streamer-url ws://192.168.1.2:7777 \
--password "secret123" \
--bind-address 192.168.1.10
--log-level debug

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the relayHostname--name CameraRelay1
--idUUID to identify the RelayGenerated--id UUID
--streamer-urlWebSocket URL to connect to the streamerNone (multicast DNS)--streamer-url wss://example.com/ws
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--bind-addressLocal modem IP address to bind for UDP socket0.0.0.0--bind-address 192.168.1.10
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json

Relay status (today only battery percentage) is sent to the streamer if --status-executable or --status-file is given and outputting a valid JSON object as seen above.

Run Relay Service

moblink-relay connects a single network interface. moblink-relay-service is a supervisor that watches every eligible network interface and starts one relay per interface automatically. It is useful on machines with several uplinks (for example a router with Ethernet, Wi-Fi, and cellular), where each uplink becomes an independent bonding path.

./target/release/moblink-relay-service \
--password "secret123" \
--interface-name-override eth0=WAN \
--interface-name-override wwan0=LTE \
--runtime-status-file /tmp/moblink-relay-status.json

By default the service discovers streamers over multicast DNS. Pass one or more --streamer-url to connect to fixed streamers instead, which is helpful where multicast DNS is unreliable (for example across router uplinks behind NAT).

Command-Line Arguments

ArgumentDescriptionDefaultExample
--passwordPassword used in the challenge response authentication1234--password mySecret
--network-interfaces-to-allowRegex of interface names to allow (^/$ anchors added automatically). Repeatable. Localhost is never allowed.All--network-interfaces-to-allow 'eth.*'
--network-interfaces-to-ignoreRegex of interface names to ignore (^/$ anchors added automatically). Repeatable.None--network-interfaces-to-ignore 'docker.*'
--streamer-urlConnect to this streamer URL directly instead of discovering over multicast DNS. Repeatable.None (multicast DNS)--streamer-url ws://192.168.1.2:7777
--interface-name-overrideRename the relay shown in the Moblin app for a given interface, as interface=label. Repeatable.None--interface-name-override eth0=WAN
--runtime-status-fileWrite relay and streamer state as JSON to this file, for external UIs to display connection status and streamer IPs.None--runtime-status-file status.json
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json
--databaseFile storing the per-interface relay identitiesmoblink-relay-service.json--database /etc/moblink/relays.json
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug

The runtime status file is rewritten whenever relays or streamers change. It contains a JSON object with connected, manual_streamer, a streamers array, and a relays array (each relay reporting its interface name, interface address, and the streamer name, URL, and host it serves).

Run Streamer

./target/release/moblink-streamer \
--websocket-server-address 192.168.1.2 \
--destination-address 172.120.50.214 \
--destination-port 5000

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the streamerHostname--name CameraRelay1
--idId to identify the streamer using multicast DNSHostname--id UUID
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--websocket-server-addressLocal IP address to bind websocket server to--websocket-server-address 192.168.1.10
--websocket-server-portLocal port to bind the websocket server to7777--websocket-server-port 7778
--tun-ip-networkTUN IP network (CIDR notation). TUN network interfaces will be assigned IP addresses from this network.10.3.3.0/24--tun-ip-network 10.1.1.0/24
--destination-addressStreaming destination address--status-file status.json
--destination-portStreaming destination port--status-file status.json

Relay Architecture

  1. WebSocket Connection

    • Establishes a WebSocket to streamer_url, or if not provided, tries to find nearby Moblink streamers through multicast DNS.
    • Handles “Hello” messages, calculates authentication, and sends an “Identify” message.
  2. Handling Requests

    • When a startTunnel request is received, the relay spawns two async tasks:
      • (relay_to_destination): Forwards traffic from streamer → destination
      • (relay_to_streamer): Forwards traffic from destination → streamer
  3. UDP Binding

    • By default, it binds a UDP socket to whatever we deem to be the main network interface.

FAQ

Q: How do I integrate this into my own application?
A: Use the moblink-rust crate


License: This project is distributed under the terms of the MIT license.

Enjoy using Moblink!

About

Use spare devices as extra SRTLA bonding connections. Rust reimplementation of the Moblin iOS feature

Resources

Stars

32 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Moblink (Rust🦀 edition)

Use spare devices as extra SRTLA bonding connections. The same functionality is part of Moblin on iOS.

Originally inspired by the Moblink Kotlin/Android code.

Features

  • WebSocket Connection: Connects to Moblin via WebSocket (e.g., wss://...)
  • Auth Handling: Implements the same challenge–response authentication logic as the Android client
  • UDP Relay: Forwards UDP packets between the remote streamer and a local destination.
  • mDNS: Automatically connect to nearby Moblink devices.

Requirements

  • Rust (stable, e.g., 1.85+)
  • Cargo (for building)

Usage

Use on Belabox

ssh user@belabox.local

password: See belabox under advanced / developer

ssh to your belabox and run this:

wget -q -O - https://raw.githubusercontent.com/datagutt/moblink-rust/refs/heads/main/install/belabox/install.sh | sudo sh

Then start a Moblink relay and open http://belabox.local/ in your browser.

If not, make sure your relay has a short name. At most ~10 characters --> Make sure the password is "1234" and put it on AUTO on the android phone.

Build

# 1. Clone this repository (or copy the code)
git clone https://github.com/datagutt/moblink-rust.git
cd moblink-rust
# Set nightly (optional)
rustup override set nightly
# 2. Build the project
cargo build --release

Run Relay

./target/release/moblink-relay \
--name "RelayName" \
--id "UUID" \
--streamer-url ws://192.168.1.2:7777 \
--password "secret123" \
--bind-address 192.168.1.10
--log-level debug

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the relayHostname--name CameraRelay1
--idUUID to identify the RelayGenerated--id UUID
--streamer-urlWebSocket URL to connect to the streamerNone (multicast DNS)--streamer-url wss://example.com/ws
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--bind-addressLocal modem IP address to bind for UDP socket0.0.0.0--bind-address 192.168.1.10
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json

Relay status (today only battery percentage) is sent to the streamer if --status-executable or --status-file is given and outputting a valid JSON object as seen above.

Run Relay Service

moblink-relay connects a single network interface. moblink-relay-service is a supervisor that watches every eligible network interface and starts one relay per interface automatically. It is useful on machines with several uplinks (for example a router with Ethernet, Wi-Fi, and cellular), where each uplink becomes an independent bonding path.

./target/release/moblink-relay-service \
--password "secret123" \
--interface-name-override eth0=WAN \
--interface-name-override wwan0=LTE \
--runtime-status-file /tmp/moblink-relay-status.json

By default the service discovers streamers over multicast DNS. Pass one or more --streamer-url to connect to fixed streamers instead, which is helpful where multicast DNS is unreliable (for example across router uplinks behind NAT).

Command-Line Arguments

ArgumentDescriptionDefaultExample
--passwordPassword used in the challenge response authentication1234--password mySecret
--network-interfaces-to-allowRegex of interface names to allow (^/$ anchors added automatically). Repeatable. Localhost is never allowed.All--network-interfaces-to-allow 'eth.*'
--network-interfaces-to-ignoreRegex of interface names to ignore (^/$ anchors added automatically). Repeatable.None--network-interfaces-to-ignore 'docker.*'
--streamer-urlConnect to this streamer URL directly instead of discovering over multicast DNS. Repeatable.None (multicast DNS)--streamer-url ws://192.168.1.2:7777
--interface-name-overrideRename the relay shown in the Moblin app for a given interface, as interface=label. Repeatable.None--interface-name-override eth0=WAN
--runtime-status-fileWrite relay and streamer state as JSON to this file, for external UIs to display connection status and streamer IPs.None--runtime-status-file status.json
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json
--databaseFile storing the per-interface relay identitiesmoblink-relay-service.json--database /etc/moblink/relays.json
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug

The runtime status file is rewritten whenever relays or streamers change. It contains a JSON object with connected, manual_streamer, a streamers array, and a relays array (each relay reporting its interface name, interface address, and the streamer name, URL, and host it serves).

Run Streamer

./target/release/moblink-streamer \
--websocket-server-address 192.168.1.2 \
--destination-address 172.120.50.214 \
--destination-port 5000

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the streamerHostname--name CameraRelay1
--idId to identify the streamer using multicast DNSHostname--id UUID
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--websocket-server-addressLocal IP address to bind websocket server to--websocket-server-address 192.168.1.10
--websocket-server-portLocal port to bind the websocket server to7777--websocket-server-port 7778
--tun-ip-networkTUN IP network (CIDR notation). TUN network interfaces will be assigned IP addresses from this network.10.3.3.0/24--tun-ip-network 10.1.1.0/24
--destination-addressStreaming destination address--status-file status.json
--destination-portStreaming destination port--status-file status.json

Relay Architecture

  1. WebSocket Connection

    • Establishes a WebSocket to streamer_url, or if not provided, tries to find nearby Moblink streamers through multicast DNS.
    • Handles “Hello” messages, calculates authentication, and sends an “Identify” message.
  2. Handling Requests

    • When a startTunnel request is received, the relay spawns two async tasks:
      • (relay_to_destination): Forwards traffic from streamer → destination
      • (relay_to_streamer): Forwards traffic from destination → streamer
  3. UDP Binding

    • By default, it binds a UDP socket to whatever we deem to be the main network interface.

FAQ

Q: How do I integrate this into my own application?
A: Use the moblink-rust crate


License: This project is distributed under the terms of the MIT license.

Enjoy using Moblink!

About

Use spare devices as extra SRTLA bonding connections. Rust reimplementation of the Moblin iOS feature

Resources

Stars

32 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Moblink (Rust🦀 edition)

Use spare devices as extra SRTLA bonding connections. The same functionality is part of Moblin on iOS.

Originally inspired by the Moblink Kotlin/Android code.

Features

  • WebSocket Connection: Connects to Moblin via WebSocket (e.g., wss://...)
  • Auth Handling: Implements the same challenge–response authentication logic as the Android client
  • UDP Relay: Forwards UDP packets between the remote streamer and a local destination.
  • mDNS: Automatically connect to nearby Moblink devices.

Requirements

  • Rust (stable, e.g., 1.85+)
  • Cargo (for building)

Usage

Use on Belabox

ssh user@belabox.local

password: See belabox under advanced / developer

ssh to your belabox and run this:

wget -q -O - https://raw.githubusercontent.com/datagutt/moblink-rust/refs/heads/main/install/belabox/install.sh | sudo sh

Then start a Moblink relay and open http://belabox.local/ in your browser.

If not, make sure your relay has a short name. At most ~10 characters --> Make sure the password is "1234" and put it on AUTO on the android phone.

Build

# 1. Clone this repository (or copy the code)
git clone https://github.com/datagutt/moblink-rust.git
cd moblink-rust
# Set nightly (optional)
rustup override set nightly
# 2. Build the project
cargo build --release

Run Relay

./target/release/moblink-relay \
--name "RelayName" \
--id "UUID" \
--streamer-url ws://192.168.1.2:7777 \
--password "secret123" \
--bind-address 192.168.1.10
--log-level debug

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the relayHostname--name CameraRelay1
--idUUID to identify the RelayGenerated--id UUID
--streamer-urlWebSocket URL to connect to the streamerNone (multicast DNS)--streamer-url wss://example.com/ws
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--bind-addressLocal modem IP address to bind for UDP socket0.0.0.0--bind-address 192.168.1.10
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json

Relay status (today only battery percentage) is sent to the streamer if --status-executable or --status-file is given and outputting a valid JSON object as seen above.

Run Relay Service

moblink-relay connects a single network interface. moblink-relay-service is a supervisor that watches every eligible network interface and starts one relay per interface automatically. It is useful on machines with several uplinks (for example a router with Ethernet, Wi-Fi, and cellular), where each uplink becomes an independent bonding path.

./target/release/moblink-relay-service \
--password "secret123" \
--interface-name-override eth0=WAN \
--interface-name-override wwan0=LTE \
--runtime-status-file /tmp/moblink-relay-status.json

By default the service discovers streamers over multicast DNS. Pass one or more --streamer-url to connect to fixed streamers instead, which is helpful where multicast DNS is unreliable (for example across router uplinks behind NAT).

Command-Line Arguments

ArgumentDescriptionDefaultExample
--passwordPassword used in the challenge response authentication1234--password mySecret
--network-interfaces-to-allowRegex of interface names to allow (^/$ anchors added automatically). Repeatable. Localhost is never allowed.All--network-interfaces-to-allow 'eth.*'
--network-interfaces-to-ignoreRegex of interface names to ignore (^/$ anchors added automatically). Repeatable.None--network-interfaces-to-ignore 'docker.*'
--streamer-urlConnect to this streamer URL directly instead of discovering over multicast DNS. Repeatable.None (multicast DNS)--streamer-url ws://192.168.1.2:7777
--interface-name-overrideRename the relay shown in the Moblin app for a given interface, as interface=label. Repeatable.None--interface-name-override eth0=WAN
--runtime-status-fileWrite relay and streamer state as JSON to this file, for external UIs to display connection status and streamer IPs.None--runtime-status-file status.json
--status-executableStatus executable. Print status to standard output on format {"batteryPercentage": 93}None--status-executable ./status.sh
--status-fileStatus file. Contains status on format {"batteryPercentage": 93}None--status-file status.json
--databaseFile storing the per-interface relay identitiesmoblink-relay-service.json--database /etc/moblink/relays.json
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug

The runtime status file is rewritten whenever relays or streamers change. It contains a JSON object with connected, manual_streamer, a streamers array, and a relays array (each relay reporting its interface name, interface address, and the streamer name, URL, and host it serves).

Run Streamer

./target/release/moblink-streamer \
--websocket-server-address 192.168.1.2 \
--destination-address 172.120.50.214 \
--destination-port 5000

Command-Line Arguments

ArgumentDescriptionDefaultExample
--nameName to identify the streamerHostname--name CameraRelay1
--idId to identify the streamer using multicast DNSHostname--id UUID
--passwordPassword used in the challenge–response authentication1234--password mySecret
--log-levelLogging verbosity (e.g., error, warn, info, debug, trace)info--log-level debug
--websocket-server-addressLocal IP address to bind websocket server to--websocket-server-address 192.168.1.10
--websocket-server-portLocal port to bind the websocket server to7777--websocket-server-port 7778
--tun-ip-networkTUN IP network (CIDR notation). TUN network interfaces will be assigned IP addresses from this network.10.3.3.0/24--tun-ip-network 10.1.1.0/24
--destination-addressStreaming destination address--status-file status.json
--destination-portStreaming destination port--status-file status.json

Relay Architecture

  1. WebSocket Connection

    • Establishes a WebSocket to streamer_url, or if not provided, tries to find nearby Moblink streamers through multicast DNS.
    • Handles “Hello” messages, calculates authentication, and sends an “Identify” message.
  2. Handling Requests

    • When a startTunnel request is received, the relay spawns two async tasks:
      • (relay_to_destination): Forwards traffic from streamer → destination
      • (relay_to_streamer): Forwards traffic from destination → streamer
  3. UDP Binding

    • By default, it binds a UDP socket to whatever we deem to be the main network interface.

FAQ

Q: How do I integrate this into my own application?
A: Use the moblink-rust crate


License: This project is distributed under the terms of the MIT license.

Enjoy using Moblink!

About

Use spare devices as extra SRTLA bonding connections. Rust reimplementation of the Moblin iOS feature

Resources

Stars

32 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages