Machine-translated draft — terminology + flow still being reviewed.

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

Simplest: fork keel/ into a private GitHub repo (e.g. github.com/liamxujia/keel-enterprise); change the public Package.swift

  • Keel.podspec to 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.zip on self-hosted CDN (Aliyun OSS / Tencent COS / self-built S3)
  • Private Package.swift repo 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 profileRecommended channel
Single / few enterprise customersPrivate GitHub repo (#1) — lowest startup cost
Can’t use GitHub but uses SPMSelf-hosted CDN (#2)
Large enterprise, already has ArtifactoryPrivate Swift Package Registry (#3)
Customer is pod-onlyPrivate 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.