Keel — Enterprise / Private Distribution
Public distribution (GitHub Release + CocoaPods Trunk) lives in
architecture.md § iOS distribution channels and roadmap.md.
This doc covers non-public scenarios: customers don’t want their
use of Keel to be discoverable on public registries, or Appunvs needs
an internal private release channel.
When this applies
- Enterprise customers: contractually require the SDK to not appear on public registries / package indexes
- Paid-license closed-source builds: enterprise build with features different from the public Apache 2.0 edition
- Pre-release / RC channels: internal-test versions not yet public
The public and private Keel.xcframework binaries can be byte-identical;
only the distribution channel differs.
Three private channels
1. Private GitHub repo (recommended; covers 90% of cases)
Simplest: fork keel/ into a private GitHub repo (e.g.
github.com/liamxujia/keel-enterprise); change the public Package.swift
Keel.podspecto point at the private release URL.
// keel-enterprise/Package.swift
.binaryTarget(
name: "Keel",
url: "https://github.com/liamxujia/keel-enterprise/releases/download/v0.1.0/Keel.xcframework.zip",
checksum: "..."
),
Customer usage:
// Their Package.swift — needs git+SSH or PAT for private repo
.package(url: "https://github.com/liamxujia/keel-enterprise.git", from: "0.1.0")
Or SSH:
.package(url: "git@github.com:liamxujia/keel-enterprise.git", from: "0.1.0")
Customer access credentials:
- SSH: add a deploy key or SSH key to GitHub
- HTTPS: a personal access token in Git credential / keychain
The GitHub Release zip is itself private-access: unauthorized users’ SPM download returns 401.
Pros:
- Reuses the GitHub Release flow, identical to public release
- No separate CDN / object storage needed
- Standard SPM toolchain, zero extra configuration
Cons:
- Customer needs a GitHub account + repo access
- Not suitable for customers who don’t want to use GitHub
2. Self-hosted CDN + Package Registry
For customers concerned about GitHub access or with compliance reasons for external hosting:
Keel.xcframework.zipon self-hosted CDN (Aliyun OSS / Tencent COS / self-built S3)- Private
Package.swiftrepo separately (could be private GitLab / Gitee / internal git server)
// Private keel manifest repo's Package.swift
.binaryTarget(
name: "Keel",
url: "https://sdk-cdn.appunvs.com/keel/0.1.0/Keel.xcframework.zip",
checksum: "..."
),
CDN URLs can be signed (presigned URLs), but SPM doesn’t support dynamic auth headers, so either:
- URL stays valid long-term (rely on IP allowlist or unenumerable paths)
- Signed URL embedded in the manifest (every rotation needs a fresh Package.swift commit)
Pros:
- Fully GitHub-independent
- Auditable access controls
Cons:
- One extra infrastructure layer (CDN) to operate
- SPM auth model is limited
3. Private Swift Package Registry (officially supported in SPM 5.7+)
SE-0339 (SPM 5.7+) supports self-hosted Swift Package Registry, integrating with internal enterprise package management (similar to npm Verdaccio / Artifactory).
# Customer machine one-time config
swift package-registry login https://registry.appunvs.com --token <PAT>
// Customer's Package.swift
.package(id: "appunvs.keel", from: "0.1.0")
Private registry implementations supported (2026):
- GitHub Packages (GitHub Enterprise plans)
- JFrog Artifactory (commercial)
- Sonatype Nexus (self-hosted, community edition free)
- Roll your own registry server (SE-0339 protocol + REST API)
Pros:
- Most “official” private distribution; aligns with Apple’s long-term direction
- Customer experience closest to public (
from:version declaration)
Cons:
- Highest adaptation cost (buy Artifactory etc., or build your own registry)
- Slightly more complex client SPM config (needs upfront
swift package-registry login) - Suits enterprise market scale; overkill for early single-tenant cases
CocoaPods-compatible channel (private spec repo)
If a customer uses CocoaPods instead of SPM (rare, but pod-only module dependencies may force this path):
# One-time: customer machine adds the private spec repo
pod repo add appunvs-private git@github.com:liamxujia/private-specs.git
# Top of the customer's Podfile
source 'git@github.com:liamxujia/private-specs.git'
source 'https://cdn.cocoapods.org/' # official fallback
target 'MyApp' do
pod 'Keel', '~> 0.1'
end
Appunvs maintains a private git repo liamxujia/private-specs mirroring
CocoaPods’ standard directory structure:
Specs/
K/
e/
e/
Keel/
0.1.0/
Keel.podspec
0.1.1/
Keel.podspec
...
Release command:
pod repo push appunvs-private keel/Keel.podspec
Nearly identical workflow to pod trunk push, just pushing to our own
git instead of the CocoaPods central registry.
Which to pick?
| Customer profile | Recommended channel |
|---|---|
| Single / few enterprise customers | Private GitHub repo (#1) — lowest startup cost |
| Can’t use GitHub but uses SPM | Self-hosted CDN (#2) |
| Large enterprise, already has Artifactory | Private Swift Package Registry (#3) |
| Customer is pod-only | Private CocoaPods spec repo (compatibility channel) |
Short-term dogfooding / internal use: go straight to #1 — minimum overhead.
Binary confidentiality caveats
Private distribution only prevents registry / source-visibility exposure; it does NOT prevent:
- Reverse engineering: xcframework is ARM64 Mach-O, with a low disassembly barrier
- Re-distribution: anyone with the xcframework can physically redistribute it
If you have real anti-reverse needs, do them inside Keel.xcframework
itself (obfuscation / string encryption / runtime checks); that’s
independent of this doc’s distribution layer.