QMAX
QMAX is a private Android messenger client backed by a personal bridge server. The Android app talks to your QMAX server, and the server keeps a browser session for https://web.max.ru.
Current shape:
server/QMax.Api- ASP.NET Core API, SQLite cache, JWT pairing auth, SignalR hub, attachment storage, APK update catalog.worker- Node/Playwright worker with a persistent MAX Web browser profile.android- Kotlin + Jetpack Compose Android client pointed athttps://qmax.kusoft.xyz.deploy- Docker Compose + Caddy for Raspberry Pi 5.
Local Checks
dotnet test QMax.slnx
cd android
.\gradlew.bat :app:assembleDebug :app:assembleRelease --console=plain --no-daemon
cd ..\worker
npm.cmd install
node --check src/server.js
Raspberry Pi Deployment
- Point DNS
qmax.kusoft.xyzto the Raspberry Pi public IP. - Install Docker Engine and the Docker Compose plugin on the Pi.
- Copy the repository or the archive produced by
scripts/deploy-rpi.ps1. - On the Pi:
cd ~/qmax/deploy
cp .env.example .env
nano .env
docker compose up -d --build
This compose publishes QMAX API on 127.0.0.1:18080. The existing system Caddy should contain:
qmax.kusoft.xyz {
encode zstd gzip
reverse_proxy 127.0.0.1:18080
}
Set strong values in .env:
QMAX_JWT_SECRET- at least 32 random characters.QMAX_PAIRING_CODE- one-time-ish pairing password for your Android client and/admin/max.QMAX_MAX_PHONE_NUMBER- the phone number used for MAX Web login.
Secrets must stay in .env, not in git.
MAX Login
Open:
https://qmax.kusoft.xyz/admin/max?pairingCode=YOUR_PAIRING_CODE
Use Start phone login. If MAX shows a robot check, solve it manually on this page using the screenshot plus click/type forms. When MAX sends the confirmation code, submit it on the same page or from the Android app MAX panel.
The worker stores browser state in the qmax-max-profile Docker volume, so the MAX session should survive restarts.
Android Pairing
Build or install:
cd android
.\gradlew.bat :app:assembleRelease --console=plain --no-daemon
On first launch:
- Server:
https://qmax.kusoft.xyz - Code:
QMAX_PAIRING_CODEfrom.env
The first screen after pairing is the chat list.
APK Update Catalog
Create a release package:
.\scripts\package-android-release.ps1 -Version 0.1.4 -VersionCode 5
The compose file mounts deploy/releases as /data/releases, so files generated by the packaging script are served automatically after docker compose up -d. The server exposes:
GET /api/app-updates/android/latest
GET /api/app-updates/android/download/{fileName}
Use one stable release key for production APKs. Do not install a new APK signed by a different key over an already installed QMAX app.
Push Notifications
The server has an FCM HTTP v1 dispatcher. To enable real push delivery on the Raspberry Pi, put a Firebase service account JSON under deploy/secrets/ and set:
QMAX_FIREBASE_PROJECT_ID=qmax-29df6
QMAX_FIREBASE_SERVICE_ACCOUNT_PATH=/run/secrets/qmax/firebase-service-account.json
The Android app registers its FCM token after pairing. It uses the Google Services Gradle plugin with android/app/google-services.json for the package xyz.kusoft.qmax. FirebaseBootstrap still supports asset-based firebase.android.json or google-services.json as a fallback for custom local builds.
Argus Publication
QMAX is published in Argus with:
- base URL:
https://argus.kusoft.xyz - slug:
qmax - platform:
android - channel:
stable
Manifest:
https://argus.kusoft.xyz/api/apps/qmax/manifest?platform=android&channel=stable
Latest APK:
https://argus.kusoft.xyz/api/apps/qmax/download/latest?platform=android&channel=stable
The Android app checks this manifest, compares semantic versions, downloads the APK to a temporary file, verifies SHA-256, and only then opens Android's package installer.
Current MAX Mapping Status
The deployed worker is authorized in MAX Web and currently maps:
- chat list extraction from the left MAX Web dialog list;
- visible chat history extraction when Android opens a dialog;
- text sending through the MAX Web composer;
- attachment upload through the MAX Web file chooser;
- image, video, file and voice attachment projection from MAX Web;
- Android image attachment caching with
.partdownloads before a file is shown from local storage; - manual browser inspection endpoints for future selector changes.
The bridge uses DOM selectors in worker/src/server.js, so MAX Web UI changes may require a worker redeploy without changing the Android app contract.