Skip to content

Mobile Repos, CI/CD & Security (all free-tier)

Decided by Hari 2026-06-11: OSS-first three-tier chain, everything on GitHub Free.

Repo chain & flow

BelledonneCommunications/linphone-iphone        (upstream — Belledonne OSS)
            │  fetch + merge their updates
astradial/astradial-ios-oss          PUBLIC    ← PRIMARY DEV REPO (branch: astradial)
            │  merge oss → private overlay              all app code lands here first
astradial/astradial-ios              PRIVATE   ← mirror + private-only overlay
                                                  (branch: private-main)

Every change goes OSS → private. The public repo is where features are developed and where ALL enforcement lives (free for public repos). The private repo is a downstream overlay for anything that must never be public (internal tooling; never credentials — see below) plus a backup mirror of astradial.

Local remotes (single clone at ~/AstradialDevelopment/Mobile App/astradial-ios): upstream = Belledonne · oss = public · origin = private.

Routines

Action Command
Daily feature work branch off astradial → PR into astradial on oss (CI + gitleaks gate)
Sync from Belledonne git fetch upstream && git merge upstream/master into astradial, fix, push oss
Update private mirror git push origin astradial && git branch -f private-main astradial && git push origin private-main (plus private-only commits cherry-picked on top)
Release tag on oss astradial; TestFlight built from that tag

CI/CD (GitHub Actions — free, public repo only)

.github/workflows/ci.yml, gated with if: github.repository == 'astradial/astradial-ios-oss' so the private mirror consumes zero Actions minutes (macOS runners bill 10× on private; public repos are unlimited and free):

  • Secret scan (gitleaks) — every push/PR, full-history fetch, custom .gitleaks.toml (allowlists only Linphone's public placeholder plist).
  • Build (iOS Simulator)xcodebuild on macos-15 with the newest runner Xcode, signing disabled. ⚠️ If GitHub's hosted Xcode is ever too old for the iOS-26 SDK features we use, the fallback is a free self-hosted runner on Hari's Mac (Settings → Actions → Runners → New self-hosted runner).

Security controls (all free)

Control Where Status
GitHub secret scanning + push protection oss (public = free) ✅ enabled via API
gitleaks CI gate oss ✅ workflow
Branch protection on astradial (require both CI checks, no force-push/delete) oss apply after first push (API 404s on a branch that doesn't exist yet) — command below
Real GoogleService-Info.plist uncommittable local clone git update-index --skip-worktree — file swaps never appear in git status
.gitignore for .env*, *-sa-key.json, *credentials* repo
CODEOWNERS (@harisuryaa), PR template with no-secrets checklist, dependabot (SPM + Actions weekly), SECURITY.md repo
Branch protection on private repo ❌ requires paid plan → not needed; rules live on oss, private is a mirror

Credential model recap: the app has NO API keys (Firebase login → platform JWT). The only sensitive local file is the Astradial GoogleService-Info.plist, which is triple-guarded (skip-worktree + gitleaks + GitHub push protection). Linphone's placeholder remains in git history intentionally — it has been public in Belledonne's repo for years.

One-time commands still to run (Hari)

# 1. First push of our branches to the public OSS repo
cd "~/AstradialDevelopment/Mobile App/astradial-ios"
git push oss astradial master
gh repo edit astradial/astradial-ios-oss --default-branch astradial

# 2. Then protect the branch (requires it to exist):
gh api -X PUT repos/astradial/astradial-ios-oss/branches/astradial/protection \
  -F 'required_status_checks[strict]=true' \
  -f 'required_status_checks[contexts][]=Build (iOS Simulator)' \
  -f 'required_status_checks[contexts][]=Secret scan (gitleaks)' \
  -F enforce_admins=false -F 'allow_force_pushes=false' -F 'allow_deletions=false' \
  --input /dev/null 2>/dev/null || true   # or ask Claude to run it

Android (astradial-android) — clean split

Created June 2026. Same OSS-first idea as iOS, but with a clean split: the public repo is a standalone native-Samsung dialer with no Firebase/auth/ analytics; the private repo overlays them. (iOS instead keeps all code on OSS and hides only the secret plist.)

BelledonneCommunications/linphone-android        (upstream — Belledonne OSS)
            │  git fetch upstream && merge upstream/master
astradial/astradial-android-oss      PUBLIC   ← dialer dev repo (branch: astradial)
            │  git merge oss/astradial → private-main          NO Firebase / analytics
astradial/astradial-android          PRIVATE  ← OSS dialer + overlay (branch: private-main)
                                                  Firebase sign-in + MD Analytics + Tickets

Local remotes (single clone ~/AstradialDevelopment/Mobile App/astradial-android): upstream = Belledonne · oss = public · origin = private. Flow is strictly one-way OSS → private (never git push oss private-main).

How the split works (no product flavors needed)

Linphone's app/build.gradle.kts already gates Firebase on file presence: firebaseCloudMessagingAvailable = File(".../google-services.json").exists(). So:

  • OSS: delete app/google-services.json → google-services plugin not applied, Firebase/Crashlytics off. firebase-messaging stays on the classpath, so nothing fails to compile; FCM is simply inert.
  • Private: add the real Astradial app/google-services.json (gitignored + skip-worktree) → Firebase on. Overlay code (Analytics/Tickets/sign-in) lives in private-only paths so Belledonne merges don't conflict.

Routines

Action Command
Daily dialer work branch off astradial → PR into astradial on oss (CI + TruffleHog gate)
Sync from Belledonne git fetch upstream && git merge upstream/master into astradial, fix, push oss
Update private overlay git checkout private-main && git merge oss/astradial (+ overlay commits on top) → git push origin private-main
Release tag on oss astradial; Play build from that tag

CI/CD (GitHub Actions — free, public repo only)

.github/workflows/ci.yml, gated if: github.repository == 'astradial/astradial-android-oss' so the private mirror burns zero minutes:

  • Secret scan (TruffleHog) — every push/PR, full-history.
  • Build./gradlew :app:assembleDebug (→ assembleOssDebug once a flavor is added) on ubuntu-latest with JDK 21 (Linux Android builds are free/unlimited on public repos; far cheaper than iOS macOS runners). Needs SDK platform 37 (sdkmanager "platforms;android-37.0" "build-tools;37.0.0") and resolves the Linphone SDK AAR from Maven.

Security controls (all free)

Control Where Status
GitHub secret scanning + push protection oss (public = free) ✅ enabled via API at creation (org default is OFF — must set explicitly)
TruffleHog CI gate oss ✅ workflow
Dependabot alerts + security updates (gradle + actions) oss ✅ enabled via API
Branch protection on astradial (require both checks, no force-push/delete) oss apply after first push — command below
Real google-services.json uncommittable private clone git update-index --skip-worktree app/google-services.json + .gitignore
Signing keystore (*.jks, keystore.properties passwords) private/local only .gitignore *.jks/*.keystore; skip-worktree keystore.properties; CI debug unsigned
READ_CONTACTS dropped (org-directory contacts) manifest (M2) privacy + smaller Play data-safety surface

Audit (June 2026): upstream commits its own google-services.json (Belledonne project linphone-android-8a563) and an empty placeholder keystore.properties — both already public for years, so the fork inherits nothing sensitive. The Android Firebase API key is a client id shipped in every APK, not a secret — protect it with Firebase Security Rules + API-key restriction (package + signing SHA-256) + App Check, not by hiding the file. SIP transport is UDP only (accepted cleartext-on-wire risk; see decisions). The old astradial-softphone-android repo was secret-scanned (gitleaks) — clean (only false positives in the public sqlite3.c amalgamation).

One-time commands still to run (Hari)

# 1. First push of our branches to the public OSS repo (publishing new Astradial work)
cd "~/AstradialDevelopment/Mobile App/astradial-android"
git push oss astradial

# 2. Then protect the branch (requires it to exist):
gh api -X PUT repos/astradial/astradial-android-oss/branches/astradial/protection \
  -F 'required_status_checks[strict]=true' \
  -f 'required_status_checks[contexts][]=Build (assembleDebug)' \
  -f 'required_status_checks[contexts][]=Secret scan (TruffleHog)' \
  -F enforce_admins=false -F 'allow_force_pushes=false' -F 'allow_deletions=false' \
  --input /dev/null 2>/dev/null || true   # or ask Claude to run it