A SwiftUI iOS app that drives the full qubic-cli command surface remotely via a tiny Go API running on your VPS.
Not for the App Store. This exposes every CLI command — wallet, node admin, smart contracts, debug — which Apple would reject. Sideload only, free Apple ID, re-sign every 7 days.
iPhone ───HTTPS+bearer──▶ VPS:Caddy:443 ─▶ qubic-api ─▶ qubic-cli
│
└─ subprocess exec
# on your Mac
scp -r deploy backend root@YOUR_VPS_IP:/root/qubic-api-src
ssh root@YOUR_VPS_IP
# on the VPS
cd /root/qubic-api-src
# with a domain (recommended: point DNS A-record at the VPS first)
DOMAIN=api.yourdomain.com bash deploy/install.sh
# or without a domain (localhost:8787, use SSH tunnel or Tailscale)
bash deploy/install.shThe installer:
- builds
qubic-clifrom source →/usr/local/bin/qubic-cli - builds the Go API →
/usr/local/bin/qubic-api - creates
qubicsystem user +/var/lib/qubic-api - generates a random bearer token → printed once, also in
/etc/qubic-api.env - installs + starts
qubic-api.service(systemd, hardened sandbox) - if
DOMAINset: installs Caddy with Let's Encrypt auto-TLS
Save the printed API_TOKEN — you'll paste it into the iOS app.
curl -fsSL https://tailscale.com/install.sh | sh
tailscale up
tailscale ip -4 # note the 100.x.x.x addressInstall Tailscale on your iPhone, sign in to the same account. In the app's Settings, use http://100.x.x.x:8787 as the base URL. No domain, no TLS certs, private.
curl -s http://127.0.0.1:8787/healthz
# {"status":"ok"}
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"args":["-help"]}' \
http://127.0.0.1:8787/exec | jq .The Swift files live in ios/QubicApp/. The repo doesn't ship an .xcodeproj — create one yourself:
- Open Xcode → File → New → Project → iOS → App
- Settings:
- Product name:
QubicApp - Interface: SwiftUI
- Language: Swift
- Bundle identifier:
com.yourname.qubicapp(any unique string)
- Product name:
- Close the template files Xcode generated (
ContentView.swift, etc.). Delete them. - In Finder, drag all contents of
ios/QubicApp/into the Xcode project navigator. Check "Copy items if needed" and "Create groups". - Make sure
Resources/commands.jsonandResources/categories.jsonare in the Copy Bundle Resources build phase. (Xcode usually does this automatically for JSON dragged into the project.) - Targets → QubicApp → Signing & Capabilities → Team: pick your free Apple ID. Xcode will auto-generate a provisioning profile.
- Plug in your iPhone, select it as the run destination, press ⌘R.
First launch the app will look empty-ish — go to Settings tab:
- Base URL:
https://api.yourdomain.comorhttp://100.x.x.x:8787(Tailscale) - API token: paste the one from the installer
- Node IP / port: your preferred Qubic node (optional; auto-injected into every command)
- Seed: 55-char seed, stored in iOS Keychain. Only used for commands marked 🔑.
- Tap Test connection — should say ✓ Reachable.
Now Browse → pick a category → pick a command → fill args → Run.
- App expires every 7 days; re-plug and re-run from Xcode to extend.
- Max 3 sideloaded apps at once.
- No push notifications, no background modes (we don't need them).
If that gets old, $99/yr Apple Developer gets you 1-year signing.
- Token is random 48-char base64, stored in
/etc/qubic-api.env(root:qubic 0640) and in iOS Keychain on the phone. - Seed never leaves the phone except inside the
-seedarg of each request over HTTPS to your own backend. The backend does not persist args; it shells out and returns. - Backend runs as a non-root
qubicuser, systemd sandbox (no new privs, read-only root fs, private tmp, etc.). - Concurrency: a mutex serializes
/execcalls so two tx signings don't clash. - No shell: args are passed directly to
execve, no shell expansion. Null bytes and over-long args are rejected.
backend/ Go API (single file, stdlib only)
main.go
go.mod
deploy/
install.sh one-shot VPS installer
qubic-api.service
Caddyfile
schema/
commands.json 199 commands, parsed from argparser.h
categories.json 16 categories w/ SF Symbol icons
ios/QubicApp/
QubicApp.swift @main
Models.swift
Catalog.swift loads bundled JSON
Settings.swift UserDefaults + Keychain
API.swift URLSession client
History.swift recent + favorites
Info.plist
Views/
RootView.swift
CategoriesView.swift
CommandListView.swift
CommandDetailView.swift
ResultView.swift
HistoryView.swift
SettingsView.swift
Resources/
commands.json ← bundled copy of schema/
categories.json
upstream/ qubic-cli source (reference only)
When qubic-cli adds new flags upstream:
cd upstream && git pull
# re-run the schema extractor (see schema/ — or just hand-edit commands.json)
cp schema/commands.json ios/QubicApp/Resources/commands.json
cp schema/categories.json ios/QubicApp/Resources/categories.json
# rebuild iOS appOn the VPS, rebuild qubic-cli:
cd /root/qubic-api-src && DOMAIN=api.yourdomain.com bash deploy/install.sh(The installer is idempotent and will rebuild qubic-cli + restart the service.)
- The schema-driven form can't know every flag's nuance — some commands may need tweaks to their arg definitions in
commands.json. The form is generic; fix the JSON and rebuild. - Boolean flags are modeled as key-only (present/absent). Double-check a few tx commands before trusting them with real qubic.
- No biometric auth on the app yet (Face ID gate before running signing commands is an easy add — ask if you want it).
- No streaming output — long-running commands block the API until done (60s default, configurable per request).