Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 86 additions & 1 deletion Cargo.lock

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

11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ name = "mhrv-rs-ui"
path = "src/bin/ui.rs"
required-features = ["ui"]

[[bin]]
name = "mhrv-drive-node"
path = "src/bin/drive_node.rs"

[features]
default = []
ui = ["dep:eframe"]
Expand All @@ -46,6 +50,13 @@ httparse = "1"
rand = "0.8"
h2 = "0.4"
http = "1"
# hyper drives the Drive REST client over a single multiplexed HTTP/2
# connection. Without this, every Drive API call paid a 300-500 ms TLS
# handshake; HTTP/2 multiplexing folds them all onto one keep-alive
# stream and gets us roughly an order of magnitude on perceived latency.
hyper = { version = "1", features = ["client", "http2"] }
hyper-util = { version = "0.1", features = ["client", "client-legacy", "tokio", "http2"] }
http-body-util = "0.1"
flate2 = "1"
directories = "5"
futures-util = { version = "0.3", default-features = false, features = ["std"] }
Expand Down
51 changes: 51 additions & 0 deletions Dockerfile.drive-node
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Dockerfile for `mhrv-drive-node` — the server side of google_drive mode.
#
# Build:
# docker build -t mhrv-drive-node -f Dockerfile.drive-node .
#
# First run (interactive — completes Google OAuth):
# docker run -it --rm \
# -v /opt/mhrv-drive:/data \
# mhrv-drive-node
#
# Subsequent runs (detached, restart on host reboot):
# docker run -d --name mhrv-drive-node --restart unless-stopped \
# -v /opt/mhrv-drive:/data \
# mhrv-drive-node
#
# /data is expected to contain `credentials.json` and `config.drive.json`
# before first run. The binary writes `credentials.json.token` (the cached
# refresh token, chmod 0600) into the same dir on successful OAuth.

# ---- builder ------------------------------------------------------------
# Need >= 1.85 for the edition2024 stabilization that time-macros (and a
# few other transitive deps in our lockfile) now require. `rust:1` always
# points at the latest 1.x stable — fine for a build image we throw away.
FROM rust:1-slim-bookworm AS builder
WORKDIR /src

# `ring` (TLS backend) needs a C compiler + assembler. Everything else is
# pure Rust thanks to rustls.
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*

COPY Cargo.toml Cargo.lock ./
COPY src ./src

# Skip the desktop / Android binaries — we only need the drive node.
RUN cargo build --release --bin mhrv-drive-node

# ---- runtime ------------------------------------------------------------
FROM debian:bookworm-slim

# CA bundle for HTTPS to www.googleapis.com.
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

COPY --from=builder /src/target/release/mhrv-drive-node /usr/local/bin/

WORKDIR /data
ENTRYPOINT ["/usr/local/bin/mhrv-drive-node"]
CMD ["-c", "/data/config.drive.json"]
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,23 @@ More deployments = more total concurrency = lower per-session latency. Each batc
}
```

## Google Drive queue mode

Experimental `google_drive` mode ports the FlowDriver idea: Google Drive is used as a shared file queue instead of Apps Script. The client listens as a SOCKS5 proxy, writes multiplexed request envelopes into a Drive folder, and `mhrv-drive-node` polls the same folder, opens the real TCP connections, and writes response envelopes back.

Use [`config.drive.example.json`](config.drive.example.json) on both machines. Create a Google OAuth desktop credential JSON with the Drive API enabled, set `drive_credentials_path`, then start the node first:

```bash
mhrv-drive-node -c config.drive.json
mhrv-rs -c config.drive.json
```

On first run each side prints a Google OAuth URL and saves `<credentials>.token` (the client tries to chmod 0600 on Unix — the file holds a long-lived OAuth refresh token, treat it like a credential). If `drive_folder_id` is empty, the program finds or creates `drive_folder_name`; for different machines/accounts, copy the resulting folder ID into both configs. This mode is SOCKS5-only and does not support UDP.

Tune `drive_idle_timeout_secs` (default 300) upward if you tunnel long-poll HTTP, idle WebSockets, or anything else that can go quiet for minutes — sessions silent past this window are force-closed.

> **Security note:** `mhrv-drive-node` is effectively an open TCP relay for whoever has read/write access to the shared Drive folder — anything that can drop a `req-…mux-…bin` file in there can open arbitrary `host:port` connections through the node. Keep the folder narrowly scoped (one OAuth account, no link sharing) and don't run the node on a machine you don't control.

## Running on OpenWRT (or any musl distro)

The `*-linux-musl-*` archives ship a fully static CLI that runs on OpenWRT, Alpine, and any libc-less Linux userland. Put the binary on the router and start it as a service:
Expand Down
16 changes: 14 additions & 2 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,26 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Deep link: tapping mhrv://... in any app opens MainActivity
and auto-imports the encoded config. -->
<!-- Deep link: tapping mhrv-rs://... in any app opens
MainActivity and auto-imports the encoded config. -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="mhrv-rs" />
</intent-filter>
<!-- Drive setup deep link: tapping mhrv-rs-setup://... in
WhatsApp / Telegram / SMS opens the app and offers to
import the bundled credentials + refresh token. Distinct
scheme so the recipient is asked to confirm a different
(credential-bearing) payload than the regular config
share. -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="mhrv-rs-setup" />
</intent-filter>
</activity>

<!-- FileProvider for sharing QR code images via the share sheet. -->
Expand Down
Loading
Loading