Does Keylight Work in Sandboxed Mac Apps?
Adding licensing to a Mac App Store app means working under the App Sandbox, and the first question every developer asks is whether their licensing SDK will survive that. Keylight does — the answer comes down to where it stores state and what the sandbox actually restricts. The SDK uses the system Keychain as its primary storage layer, which the sandbox permits; network calls for activation and lease renewal require one standard entitlement you almost certainly already have.
The sandbox question developers ask before adopting Keylight
The three concerns that come up most often are variations of the same fear: something the developer was relying on will silently stop working after sandboxing.
“Does the sandbox wipe UserDefaults on update?” Sometimes — not always. When you migrate an unsandboxed app into the sandbox, the defaults domain changes and existing UserDefaults entries become invisible. If your license state lived there, the customer looks unlicensed after the update even though they paid. This is a real problem for apps that store license data in UserDefaults, but Keylight does not use UserDefaults for the lease. The SDK writes to the Keychain, which is system-level storage, not bundle-local.
“Is Keychain access blocked inside the sandbox?” No. The macOS App Sandbox allows Keychain reads and writes by default. The sandbox restricts file system access outside the container, certain hardware access, and inter-process communication — not Keychain operations against the generic password store. Your app can read and write Keychain items from inside the sandbox without any additional entitlement as long as those items belong to the app’s own Keychain access group.
“Are network calls denied in a sandboxed app?” By default, yes — outbound connections require the com.apple.security.network.client entitlement. Keylight makes network calls only for activation and for the periodic lease revalidation check, so you need to enable this entitlement. The good news is that com.apple.security.network.client is the standard entitlement every sandboxed app that fetches anything from the network already has; it is not Keylight-specific.
What the SDK actually stores in Keychain
The storage layer is deliberately narrow: one generic-password Keychain item per tenant-and-product combination.
The item is keyed by two attributes:
kSecAttrService— a service prefix scoped todev.keylight.<tenantId>, constructed at init time so that multiple Keylight-powered apps on the same Mac never collide on service names.kSecAttrAccount— the product identifier, which together with the service prefix makes the item unique.
The accessibility is kSecAttrAccessibleAfterFirstUnlock. This is a deliberate choice: it means the Keychain item is readable after the device has been unlocked once since boot, which covers the case where your app launches at login via a Launch Agent before the user has logged in interactively. Items set to kSecAttrAccessibleWhenUnlocked would be unavailable in that scenario. kSecAttrAccessibleAfterFirstUnlock is also the right default under the sandbox because helper tools and XPC services launched by the main app share the same unlock state.
On iOS and other Apple platforms the SDK opts into the Data Protection Keychain (kSecUseDataProtectionKeychain). On macOS the SDK uses the standard Keychain Services API instead — it does not set kSecUseDataProtectionKeychain — so the SDK will not break your build by demanding entitlements you may not have. If you declare keychain-access-groups for cross-app license sharing, that works normally under the standard Keychain Services path on macOS.
Beyond Keychain, the SDK also maintains an obfuscated file fallback for crash recovery. Both the Keychain item and the fallback file are written for each activated license. The file fallback exists so that legitimate users do not lose their entitlement when Keychain is wiped — by app re-signing, user-driven resets, or certain OS migrations. Under the App Sandbox, the file fallback writes to the app’s container Application Support directory rather than the user’s actual home directory, which means both layers work correctly inside a sandboxed app.
The stored payload is the full signed lease — the same Ed25519-signed document the server issues on activation. Every time the SDK reads the cached lease from Keychain, it re-verifies the signature locally before trusting the contents. Even if someone managed to tamper with a Keychain item directly, the signature check catches it immediately. For more on how the signed lease itself works, see where to store license data on macOS.
Required entitlements for direct distribution vs Mac App Store
For direct distribution — downloading a .dmg or .zip from your own site — the sandbox is optional. You can ship without sandboxing and no special entitlements are required. Keylight works out of the box: the SDK makes Keychain reads and writes using the standard Security framework APIs, and the network calls use URLSession.
For Mac App Store distribution, the sandbox is mandatory, and you need at minimum two entitlements:
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<!-- only if sharing licenses across helper apps -->
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)dev.acme.shared</string>
</array>
com.apple.security.app-sandbox enables the sandbox itself — App Review requires it. com.apple.security.network.client allows outbound TCP connections so the SDK can reach the Keylight API for activation and lease refreshes. Without it, the first activation attempt returns a network error.
keychain-access-groups is only needed if your app has a helper tool, an XPC service, or a separate companion app that also needs to read the license state. The access group string must match a prefix in your provisioning profile (the $(AppIdentifierPrefix) macro expands to your Team ID at build time). If your licensing integration lives entirely in the main app bundle — the most common case — you do not need this entitlement at all.
One note on keychain-access-groups: on macOS the SDK uses the standard Keychain Services API, not the Data Protection Keychain. Declaring keychain-access-groups works as expected for sharing license state across bundle IDs — no additional SDK configuration is needed.
For a deeper look at the Keychain as the right place for license data, see store a signed license token in the macOS Keychain and the broader discussion in where to store license data on macOS.
Edge case: Mac App Store apps that also accept direct license keys
Some developers want a single binary that ships on the App Store and accepts a direct license key entered by customers who bought from the developer’s own site. This is technically possible — the Keylight SDK has no hard block on running inside a sandboxed MAS build — but it is an edge case with real consequences.
Apple’s App Store Review Guidelines discourage in-app purchase alternatives for digital goods. If your app accepts a license key to unlock features that could be sold via StoreKit, a reviewer may flag it as circumventing in-app purchase. The outcome depends on how your app is categorized and how the flow is presented, but the risk is non-trivial. App Review has rejected apps for exactly this pattern.
The cleaner pattern — and the one that avoids ambiguity with Apple — is to ship two separate binaries from the same codebase:
- An App Store build that conditionally compiles away the Keylight license-key input flow and routes upgrades through StoreKit.
- A direct build with no StoreKit dependency, distributed via your own site, that uses Keylight license keys as the sole purchase path.
In both cases the guarantee is cryptographic: the signed lease is Ed25519-signed by the server and re-verified locally on every read — not bundle-identity-based. A dual-binary approach works because the Keylight API does not care which binary is making the call; it cares about the SDK key, the license key, and the tenant. You can share a Keylight product and key prefix across both builds and the customer’s license covers both.
The in-app purchase restriction applies to the App Store build; the direct build has no such constraint. Keeping the two binaries separate is cleaner than building a single binary that conditionally hides purchase UI from the App Review team.
For the full treatment of how Keylight issues and verifies license keys, including the signed lease format and activation mechanics, the features page covers it.
Whether you are shipping under the sandbox, via direct download, or both, the advice is the same: pick the distribution channel and build the right binary for it. If you run into anything the entitlement guidance above does not cover, send us your feedback and we will extend this post.
Frequently asked
Does the Keylight Swift SDK work in a sandboxed Mac app?+
Yes. The SDK stores its cached lease in the system Keychain, which is fully supported under the macOS App Sandbox. You need to enable the App Sandbox entitlement and, if you share license state across helper apps, declare keychain-access-groups.
What entitlements does my Mac app need to use Keylight?+
For direct distribution outside the App Store, no special entitlements are required. For Mac App Store distribution under the sandbox, enable com.apple.security.app-sandbox and com.apple.security.network.client. Add keychain-access-groups only if multiple bundle IDs share licenses.
Can a Mac App Store app also accept a direct license key?+
Technically yes, but Apple discourages it and may reject the build. The cleaner pattern is to ship two binaries: an App Store build that uses StoreKit, and a direct build that uses Keylight license keys.
Ready to ship?
Create your account and start licensing your apps in under a minute. Free forever tier included.
Start Free