Trey T 491f3a22ba Builder v2: local project browser + multi-team ASC keys
Rewrites the builder console to browse local Xcode projects instead of
accepting source uploads or git URLs. Replaces the devices page with a
profiles page that manages ad-hoc provisioning profiles and lists
registered bundle IDs per team.

Adds multi-account support: ASC API keys are now stored in an asc_keys
table keyed by team_id (team_name, key_id, issuer_id, p8_filename). At
build time, the worker reads DEVELOPMENT_TEAM from the Xcode project and
auto-picks the matching key for fastlane sigh + JWT signing. Legacy
single-key settings auto-migrate on first boot.

Fixes storefront IPA parser to handle binary plists produced by Xcode.
Drops the enrollment bridge, device management routes, and direct
ASC API client -- fastlane sigh handles profile lifecycle now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 14:43:16 -05:00
2026-04-11 11:40:44 -05:00
2026-04-11 11:40:44 -05:00
2026-04-11 11:40:44 -05:00
2026-04-11 11:41:37 -05:00

iOS App Store

Self-hosted iOS OTA app distribution server. Build with your dev/distribution certs, host the IPAs, install on registered devices over the air — no Xcode tether, no TestFlight.

Features

  • Web UI for browsing and installing apps (mobile-friendly, dark theme)
  • Drag-and-drop IPA upload with automatic metadata + icon extraction
  • CLI/API uploads with token auth
  • Automatic manifest plist generation for itms-services:// installs
  • Multiple builds per app, version history, release notes
  • Password-protected web UI, token-protected API

Requirements

  • iOS device UDID registered in the provisioning profile embedded in the IPA
  • IPA exported with method: ad-hoc (development-signed IPAs cannot be installed OTA)
  • Server reachable over HTTPS with a trusted certificate

Stack

Node.js + Express + SQLite + Docker. Sharp for icon resizing. No client framework — vanilla HTML/CSS/JS.

Endpoints

Method Path Auth Description
GET / session App listing
GET /upload session Upload page
POST /api/upload token or session Upload IPA
GET /api/apps token or session List apps + latest build
GET /api/apps/:id token or session App detail with all builds
DELETE /api/apps/:id token or session Delete app + all builds
DELETE /api/builds/:id token or session Delete a single build
GET /api/manifest/:buildId public Plist manifest for OTA install
GET /api/download/:buildId public Download IPA file
GET /api/health public Health check

The manifest and download endpoints are intentionally public — iOS fetches them unauthenticated during install.

CLI Upload

curl -X POST https://appstore.example.com/api/upload \
  -H "X-Api-Token: $API_TOKEN" \
  -F "ipa=@path/to/YourApp.ipa" \
  -F "notes=Build notes"

Response includes the install URL (itms-services://...) ready to open in Safari on the device.

Configuration

Copy .env.example to .env and set:

Variable Description
ADMIN_PASSWORD Web UI login password
API_TOKEN Token for CLI/automation uploads
SESSION_SECRET Express session secret
BASE_URL Public HTTPS URL of the server
PORT Listen port (default 3000)

Deployment

Runs as a single Docker container. The /data volume holds the SQLite DB, uploaded IPAs, and extracted icons.

docker compose up -d --build

Put a reverse proxy with HTTPS in front of it. iOS requires a trusted SSL certificate for OTA installs to work.

Project Layout

src/
  server.js        Express app, routes, middleware
  db.js            SQLite schema
  ipa-parser.js    Extracts Info.plist + icon from IPA
  manifest.js      Generates OTA manifest plist
  auth.js          Session + token auth middleware
public/            Static assets (CSS, JS)
views/             HTML pages
Description
No description provided
Readme 161 KiB
Languages
JavaScript 68.5%
CSS 16.7%
HTML 13.2%
Shell 0.8%
Ruby 0.6%
Other 0.2%