Skip to content

v3.1.0

Choose a tag to compare

@github-actions github-actions released this 01 Aug 16:50
1d0043b

xprem 3.1.0 — Expo Open OTA has a new name

Expo Open OTA is now xprem. Same project, same maintainers, same licenses renamed to respect Expo's trademark. xprem is not affiliated with, sponsored or endorsed by Expo.

The release behind the rename is a big one: the server grows from an update pipeline into a full control plane. Device observability lands with expo-observe support, every device gets an identity you can attach your own attributes to, an MCP server lets agents operate the whole thing over OAuth, and iOS and Android updates published together now move together as publish groups.

The rename: what changes for you

Everything is republished under the new name, and every old name keeps working so nothing breaks on upgrade day:

New (canonical) Old (still published)
Repository mercuretechnologies/xprem redirects automatically
Docker image ghcr.io/mercuretechnologies/xprem ghcr.io/mercuretechnologies/expo-open-ota, ghcr.io/axelmarciano/expo-open-ota
Helm chart oci://ghcr.io/mercuretechnologies/charts/xprem oci://ghcr.io/mercuretechnologies/charts/expo-open-ota (byte-identical copy)
Documentation mercure-technologies.gitbook.io/xprem
CLI npx eoasunchanged

No environment variable, API route or bucket layout changes. Existing deployments upgrade by bumping the tag; switch to the new image and chart names at your own pace.

Highlights

Observability with expo-observe (#119)

xprem is now a self-hosted backend for Expo's expo-observe SDK — point extra.eas.observe.endpointUrl at your server and keep the stock SDK, no fork. Native and JS crashes, cold launch, bundle load, time to first render and time to interactive, sessions, plus your own structured events and logs — all tied to the exact update, branch and channel that produced them, so a regression shows up while the rollout is still on a slice of the branch.

The data lands in your ClickHouse. The dashboard grows an Observe section: overview with a live device-activity globe, per-metric percentiles with breakdowns by device model, OS, region, app version and screen (straight from expo-router or react-navigation), an event explorer and a log stream. Point Grafana, PostHog or Datadog at the same tables, or query them directly.

Device identity (#116)

Every device that checks in is now registered per app: first/last seen, model, OS, current update, branch, runtime version and location, browsable and searchable from the dashboard's Devices view — including a live "online now" count.

On top of the registry, custom device attributes (Enterprise): declare an allowlist of typed keys, then ship $set / $set_once / $unset operations from the app through the expo-observe pipeline. Your plan tier, your tenant, your feature flag — whatever your app knows about the device becomes a first-class filter and breakdown dimension across metrics, events, logs and update health.

MCP server (#141, #142, #143)

xprem is now an MCP server at /mcp. Agents sign in with OAuth 2.1 as dashboard users — dynamic client registration, PKCE, refresh tokens with revocation, no API keys — and every call runs with that user's per-app permissions, nothing more.

16 tools ship under MIT and cover the same operations as the dashboard: list apps, branches, channels and updates, inspect rollouts, create and delete branches and channels, roll back, republish. 10 more come with the Enterprise edition and open up the observability dataset: update health, metric breakdowns, events, logs, audit logs and device search.

"Which screens had the worst time to interactive since 3.4.2 rolled out?"

"Something looks wrong since the last release. Roll production back to the previous update."

Publish groups (#118)

One eoas publish run produces an iOS update and an Android update; they are now linked by a publish group. The dashboard collapses them into a single row with aggregated health across both platforms, and republish and rollback act on the whole group, so putting an old version back can never leave the branch serving one platform's build without the other. Available from the dashboard, the REST API and the MCP tools; older CLIs keep publishing ungrouped rows.

Republish and roll back from the dashboard (#127)

Rollback and republish are no longer CLI-only: revert a branch to its previous update, or put any past update (or publish group) back live, in one click from the Updates feed.

GeoIP (#147)

Two ways to locate your fleet: trust the visitor-location headers your CDN already adds (Cloudflare, CloudFront, Vercel, or custom X-Geo-* headers), or give the server free MaxMind GeoLite2 credentials and it downloads and refreshes the database itself — nothing to mount. Location feeds the live device map and makes region a breakdown dimension everywhere. Configure neither and everything else keeps working.

Server configuration wizard (#146)

npx eoas server:init walks through the whole server setup — storage backend, delivery, database, cache, key store, GeoIP, replicas — and generates a ready-to-run .env file for Docker, or values.yaml plus a properly gitignored secrets file for Helm. It reuses your existing master key on re-run, warns you to back it up when minting one, and npx eoas server:validate checks a config before you deploy it.

Faster on the hot path, and measured (#145, #148)

Manifest signatures are now cached (keyed by content hash, so a stale signature can never be served), alongside short-TTL caches for app config and channel mappings. A new S3 presigned URL delivery mode serves assets straight from a private bucket with no CDN in front and no traffic through the server.

The release also ships a reproducible k6 load test with published results: one 1-vCPU instance serving real expo-updates polls from 100,000 devices, code signing and telemetry on — 938 req/s at the peak of a full-fleet rollout, zero errors. Method and raw data in test/load/results.

Security hardening

  • Sensitive endpoints (sign-in, token issuance, uploads) are rate limited (#131).
  • Dashboard sessions can be revoked server-side, and refresh tokens rotate with session-version invalidation (#133).
  • API tokens carry specific roles instead of blanket access (#141).
  • Upload requests are validated harder before touching storage (#135).
  • GitHub workflows run with read-only permissions by default (#128).

Also in this release

  • DISABLE_DEVICE_TELEMETRY=true switches off the device registry and telemetry ingest entirely, for deployments that want none of it (#134).
  • Overwrite confirmation in eoas before replacing existing config, and safer write modes (#129).
  • Dashboard polish: debounced filters, better loading skeletons, richer update details (5d517aa).
  • pgx bumped to 5.9.2 (#110).

Install or upgrade

  • Docker: ghcr.io/mercuretechnologies/xprem:v3.1.0
  • Helm: helm install xprem oci://ghcr.io/mercuretechnologies/charts/xprem --version 3.1.0
  • CLI: npx eoas@3

Observability, identity and the control plane need PostgreSQL (control plane mode); stateless mode keeps working with nothing but a bucket.

Full changelog: v3.0.5...v3.1.0