Crash-report receiver for our own apps
OshaPlay and the VPN client send crashes and logs to servers we run ourselves
The task
Our own apps, OshaPlay and the VPN client, have testers with phones and computers. A message like “it doesn't work for me” gives the developer nothing to act on: you need the log, the version, the network state and the crash reason. At the same time a report must not become a leak: the VPN client's log may contain subscription links and keys, and the player's report contains details about the phone and headphones.
There is one more condition: reports must not go to third-party services. Testers' data lives on the developer's own servers.
The solution
Two report receivers, one per app. The app collects diagnostics itself, strips secrets on the device and sends them with a token. What has been built:
- The OshaPlay receiver: accepts a log, crashes and DAC reports as a compressed file of up to 5 MB, keeps them for 90 days within 800 MB.
- The VPN client's receiver on the stand's entry node: accepts four kinds of reports (
crash,log,probe,user). - An on-disk queue in the VPN client: up to 20 files and 50 MB, with reports resent when the link returns.
- Secret masking when the log is written, not when the server receives it.
- Tester mode: automatic reports on probes, crashes and anomalies, on by default and doubling as consent to send.
- Developer scripts: reading OshaPlay reports from the server, and pulling the VPN client's reports to the workstation.
- A liveness check of the OshaPlay receiver is part of the general check of the sites server.
How it works
Token-protected intake, nothing served outward
A receiver is a small Python program in a Docker container. A token is required on intake: without it a report is not accepted. Reports are never served outward, and only the developer reads them from the server. The OshaPlay receiver sits behind the sites server's shared entry nginx, and the VPN client's receiver sits on the stand's entry node. The same sites server also serves the player's releases for self-updates.
Masking secrets on the device
Masking (replacing values with placeholders) is done by the client when the log is written. Subscription links, vless:// keys and similar, UUIDs, passwords, tokens and Bearer headers are replaced before they are written, so an intercepted or stored report doesn't reveal secrets. The rules are covered by tests on both platforms and were checked on real reports on the server after the first sending.
An on-disk queue and protection from noise
If the receiver is unreachable, the VPN client puts a report into an on-disk queue: up to 20 files and 50 MB. Automatic reports of one kind go out no more often than once every 5 minutes. A 413 response from the receiver discards a report, and a 403 stops sending until the app restarts. The OshaPlay player is simpler: with no network, a report waits for the next launch.
What is inside a report
A VPN client report carries a header with network, power, memory and session state. App and engine logs share one line format, and the engine writes to a rotating file: 5 files of 5 MB each. An OshaPlay report contains a log of up to 5 MB, the phone's model and firmware, audio outputs and inputs, audio decoders, connected headphones, the DAC check report and accumulated listening statistics. Music files are never sent.
When a report goes out
OshaPlay sends a report every 12 hours, after a crash on the next launch, and immediately on a playback error; there is also a “Send report to the developer” button. The VPN client sends an archive with the app and engine logs on the “Send diagnostics” button, and in tester mode it reports problem entry points and anomalies by itself: a reconnect loop, an engine failure, a threefold latency jump, a twofold memory growth.
Results
- Two receivers run in production: for the OshaPlay player and for the VPN client.
- VPN client reports on the server contain no UUIDs, subscription paths or subscription links, checked on real reports.
- A “Send diagnostics” archive from the emulator reached the receiver and carried both logs with no secrets.
- OshaPlay reports are used to analyze real failures: the cause of audio stutter with the screen off was found from phone reports.
Technologies and why
- Python: the receiver's code.
- Docker: packaging and running the receiver as a separate container.
- nginx: the HTTPS entry through which reports reach the receiver.
- An intake token: only our own apps get access, and the key is kept outside the repository.
Status
In production. The OshaPlay receiver was added in sites server release 1.11.0 of September 28, 2026; the current sites server version is 1.16.0 of September 29, 2026. The VPN client's receiver runs on the stand's entry node and accepts reports from the Android, Windows and Windows 7 clients.
A known limitation per the VPN client's README: while the entry node is unreachable, reports wait in the on-disk queue and are not lost until the queue fills up. Tester mode is on by default because all builds are still test builds; for a public release it has to be turned off.
Questions about this project
How do you collect crash reports from an app without third-party cloud services?
Which reports does the VPN client's receiver accept?
Can keys or subscription links end up in a report?
What happens if the receiver is unreachable?
How long are reports kept, and who can see them?
Can such a receiver be built for our team's app?
More in this area
OshaVPN: VPN client for Android, Windows 10/11 and Windows 7
our own app to replace NekoBox, Hiddify and v2rayNG, now in testing with users
OshaPlay: audiophile music player for Android
bit-perfect USB DAC output and audiobooks, our own product at version 0.10.0
Need something similar?
Tell us about the task — we'll show how we solved it and estimate the scope.