Case study · Freelance

OAPKH

Ship test builds to your team without handing your APKs to a third party

Internal build distribution normally means uploading every unreleased binary to someone else's cloud. OAPKH is the same workflow on infrastructure the organization already controls: a single Rust binary and a Postgres database, with the web dashboard compiled inside it and an Android client for the people who actually install the builds. One app joins as many organizations as you work with, and the home screen answers the only question a tester opens it to ask — is there something new for me?

Role
Sole author
Type
Freelance
Since
September 2026
Platforms
Android · Web

Architecture

The constraint that shaped everything: whoever runs this is a development team, not an ops team. So the server is one binary that runs its own migrations on boot, serves its own dashboard, and writes APKs to a directory. Postgres is the only thing to install alongside it.

What you run
oapkh (binary)
Rust · axum
API, auth, storage and the dashboard in one process
PostgreSQL
sqlx, migrations on boot
the only dependency to operate
Disk
APKs in a local directory
SHA-256 recorded per build; no object storage, no third party
How people reach it
Web dashboard
Svelte 5 · Tailwind 4
embedded in the binary — nothing to deploy separately
Android app
Kotlin · Views · Material 3
testers install builds; developers upload them
CLI
oapkh admin / serve / migrate
create users, run migrations, dump OpenAPI

What it does

Build distribution

  • › Upload an APK with an environment and tags
  • › Package, version and min SDK parsed from the file itself
  • › A project locks to its first APK's package name
  • › Download and install straight from the phone

What's new, first

  • › Home screen answers: is there anything new for me?
  • › New builds across followed projects, newest first
  • › Per-project notification subscriptions
  • › Push the moment a build lands

Many organizations, one app

  • › Join as many servers as you work with
  • › Separate sign-in, projects and settings per instance
  • › Switch instances in a tap
  • › Nothing shared between them

Joining in seconds

  • › Scan the invite QR from the dashboard
  • › Open an invite link
  • › Or sign in with a server URL
  • › Invites are single-use and expire

Access control

  • › Admin, developer and tester roles
  • › Groups grant project access
  • › Every access change lands in the audit feed
  • › Activity feed of who shipped what

APK verification

  • › Check an APK before trusting it
  • › Hash and manifest read on the phone
  • › Only the hash, package and version leave the device
  • › Server answers: match, suspicious or unknown

Organizing builds

  • › Environments with a colour ramp
  • › Free-form tags with admin-chosen colours
  • › Filter build lists by environment
  • › Changelogs rendered from sanitized Markdown

Self-hosted by design

  • › Your builds, users, database and storage
  • › Single binary plus Postgres, or Docker Compose
  • › Firebase service-account key encrypted at rest
  • › No vendor holds your APKs

Designed to be read

  • › Carbon & Signal palette — colour carries meaning only
  • › Contrast, not hue, marks what is tappable
  • › Every pair clears WCAG AA, pinned by the dashboard's tests
  • › The same tokens on Android and web

Worth knowing

  • Argon2 password hashing, JWT access and refresh tokens, AES-GCM at rest for the Firebase service-account key
  • No google-services.json: each organization brings its own Firebase project, and the app builds one Firebase instance per server at runtime
  • A hand-written parser for the binary AndroidManifest, in Rust on the server and again in Kotlin on the phone
  • Download links are HMAC-signed and expire after five minutes
  • The dashboard is compiled into the server binary, so an install is one file and a database
  • OpenAPI document generated from the router with utoipa, and dumped by the CLI
  • Contrast test suite parses the real CSS and asserts every shipped colour pair against WCAG
  • Rust integration tests against a real Postgres, a Playwright smoke test for the dashboard, JVM and instrumented tests for the app

Stack

RustaxumtokiosqlxutoipaPostgreSQLArgon2JWTAES-GCMKotlinAndroid ViewsMaterial 3RoomRetrofitOkHttpCoroutinesSvelte 5TypeScriptTailwind 4VitePlaywrightFirebase Cloud MessagingML KitDocker