Bundles, Signing, and the CDN
What a bundle is, what happens when you publish, why every bundle is signed, and how key rotation works.
A bundle is the unit of delivery in AirStrings: a single JSON document containing all of one environment's strings for one locale. Bundles are:
- Immutable: once published, a bundle never changes. An edit produces a new bundle.
- Versioned: each bundle carries a revision, an integer that increases with every publish of that locale. Revisions let SDKs tell newer from older and refuse to go backwards.
- Signed: every bundle carries an Ed25519 signature. There is no unsigned delivery path.
What happens when you publish
Compile. The platform gathers the environment's strings for each locale into one document per locale and assigns the next revision.
Sign. Each bundle is signed with the environment's Ed25519 signing key. The signature covers the bundle's identity (which project, which locale, which revision) as well as every string, not just the text.
Upload. The signed bundle is stored as the immutable artifact for that revision.
Refresh. The CDN is refreshed so edges pick up the new revision. Updates reach users within about 5 minutes of a publish.
Why bundles are signed
Copy is code: an attacker who can swap a string can change what your app tells its users. Because the signature binds the strings and the bundle's identity, verification defeats three whole classes of attack:
- Substitution: passing off strings from a different project or locale as yours.
- Downgrade: replaying an old revision as if it were current.
- Format confusion: reinterpreting a bundle's content under a different format version than it was published for.
Verification is non-negotiable
SDKs verify every bundle before using it, whether it arrived over the network, from the local cache, or from a bundled seed. Verification failure is a hard error: the bundle is rejected, never cached, and never rendered. AirStrings SDKs never serve unverified content.
Rejection is loud but not fatal. The SDK reports the failure through its error channel and keeps running with the last verified content it has, or falls back to key names if it has none. Your app never crashes over a bad bundle.
Key rotation
SDK configuration accepts a list of public keys, and every bundle carries the ID of the key that signed it. The SDK uses that ID to select which of its configured keys verifies the bundle, so two keys can be trusted at once during a rotation:
let strings = AirStrings(configuration: .init(
organizationId: "org_a1b2c3d4e5f6",
projectId: "proj_a1b2c3d4e5f6",
environmentId: "env_a1b2c3d4e5f6",
publicKeys: ["CURRENT_PUBLIC_KEY", "NEXT_PUBLIC_KEY"]
))A rotation, in order: ship app releases that trust both the current and the next key, switch publishing to the next key once those releases have reached enough of your users, then drop the old key from configuration in a later release. At no point does any client fail verification.