Apsule Host API
Implementation status: the executable Host implements the API families reported by runtime discovery, including networking, project data/files, timers/crypto, image/document processing, Local Network, Location/BLE, Camera/Audio/Photos, Apple Music, AuthenticationServices, WeatherKit, Translation, Nearby Interaction, Core Spotlight, Wallet/PassKit, Notifications, Background Tasks, system utilities, Live Activities, Biometrics, Vision, Contacts, Calendar/Reminders, Motion, Speech, geocoding/directions, NFC, HealthKit, HomeKit, Widget state and WebView control. Runtime discovery is authoritative for an installed Host, and Apple entitlement-dependent families may still report runtime/platform errors when the signed Host lacks that entitlement/capability.
Naming
Host API means native functionality exposed by the iPhone Apsule Host to project JavaScript. It is deliberately not called a Capability; that term is reserved for Conductor.
All APIs live under host.<family>.<operation>:
await host.http.request(...);
await host.location.current(...);
await host.bluetooth.scan(...);Use Promise-based calls for finite operations and cancellable subscriptions/events for streams. New Host API families must follow this common grammar rather than inventing family-specific callback/synchronous conventions.
Common behavior
Every Host API should:
- validate project permission before native access;
- return JSON-serializable values unless explicitly documented otherwise;
- use a common structured error shape;
- support cancellation/timeouts where meaningful;
- emit useful diagnostics to the runtime console without leaking secrets;
- be discoverable through runtime feature/schema discovery.
Minimize Host rebuilds: high-level API + constrained raw API
Apsule should minimize the number of Host rebuilds required to add Project features. For native/service integrations whose remote API can evolve independently of the installed Host, prefer a two-layer Host API design:
- High-level wrappers for common operations. These provide stable, ergonomic Project APIs, normalize errors/results, enforce permissions and hide provider credentials/tokens.
- A constrained raw/provider request API for supported operations that do not require a new iOS framework, entitlement, Info.plist declaration, extension or other compile-time Host capability.
The raw layer is not unrestricted networking. It must remain provider-scoped and permission-gated. Provider credentials and user tokens stay inside the Host, allowed destinations/methods are restricted, and responses are converted to the normal JSON-serializable Host API contract. Runtime discovery must advertise the raw API separately so Projects can test compatibility before using it.
For example, an Apple Music integration should expose convenient APIs for playback, queue, library, history, recommendations and other common MusicKit operations, while also exposing a constrained Apple Music API request primitive where MusicKit/Apple Music authentication can be safely applied by the Host. If Apple later adds a compatible Apple Music REST endpoint, a Project should ideally be able to use it without rebuilding Apsule.
A Host rebuild is still required when a feature needs a newly compiled native framework/API surface, a new entitlement/signing capability, a new Info.plist usage description or background mode, an extension, or another platform-level declaration that Project JavaScript cannot add dynamically.
Proposed error shape:
{
"code": "bluetooth.service_not_found",
"message": "Requested service was not found.",
"details": {}
}Network
host.http.request(options)
Generic HTTP client supporting:
- GET/POST/PUT/PATCH/DELETE and arbitrary standard methods;
- headers;
- query parameters;
- JSON/text/binary request body;
- JSON/text/binary response;
- timeout;
- multipart upload.
API-specific integrations (for example a game stats API) should normally be written in project JavaScript on top of this primitive, not added to the Host.
The executable client supports JSON/text bodies, Base64 binary bodies, Project-sandbox file bodies, multipart fields/files, Base64 binary responses and downloadTo for writing a response directly into the Project sandbox. File-backed request/response modes additionally require storage: true.
HTTP cookies are isolated to the running Project runtime rather than using a process-global cookie store:
host.cookies.list(options)host.cookies.set(options)host.cookies.delete(options)host.cookies.clear()
Requests use that cookie jar by default; pass cookies: false on a request to opt out.
host.webAuthentication.authorize(options)
Presents an ASWebAuthenticationSession for Passkey/OAuth-style browser authentication and returns the callback URL to project JavaScript. It requires the project's network permission. The current Host accepts an HTTPS url, the registered callbackScheme: "apsule", and optional prefersEphemeralSession; the resolved value contains url, scheme, host, and a string query object. Restricting the callback scheme to the Host-owned apsule scheme prevents projects from impersonating callbacks owned by other installed apps.
WebSocket
host.websocket.connect(options)- connection send/close;
- message/open/close/error event subscriptions.
connect(options, onEvent) returns a Promise for a connection handle. The handle exposes connectionId, send(message) and close(code, reason). Text and Base64 binary payloads are supported. Connections are closed automatically when the Project runtime stops.
SSE and progress transfers
host.sse.connect(options, onEvent)/host.sse.close(id)host.transfer.download(options, onEvent)host.transfer.upload(options, onEvent)host.transfer.cancel(id)
SSE parses standard event, data, id, and retry fields and returns a cancellable connection. File transfers require both Network and Storage permission; download/upload callbacks receive start, byte progress, complete, error, or cancelled events while the call itself returns a cancellable transfer handle immediately.
Storage
host.storage.get(key)host.storage.set(key, value)host.storage.delete(key)host.storage.list(options)
Storage is namespaced per Apsule Project.
The current implementation persists JSON-serializable values inside the project's Sandbox and rejects access when the manifest has not declared storage: true.
Database
host.database.execute(statement, parameters)host.database.query(statement, parameters)
Implementation may use SQLite. Database files must be project-scoped by default.
The executable Host uses one SQLite database per project Sandbox. Statements accept positional ? parameters supplied as a JSON array. execute returns affected-row count and last insert row id; query returns JSON-serializable row objects. Database access currently uses the project's storage permission.
Files
host.files.read(path, options)host.files.write(path, data, options)host.files.list(path)host.files.delete(path)host.files.pick(options)host.files.export(paths)host.files.createDirectory(path)host.files.move(source, destination)host.files.copy(source, destination)
Default reads/writes are constrained to the project's sandbox. Explicit user-picked files may be represented by scoped handles rather than arbitrary global filesystem paths.
The executable Host implements sandbox read/write/list/delete/createDirectory/move/copy plus native import/export pickers. read returns UTF-8 text when decodable plus Base64 and byte size; write accepts { text } or { base64 }. Imports are copied into the Project sandbox and exports expose only explicitly named sandbox files. Files access uses the Project's storage permission.
Keychain
host.keychain.get(key)host.keychain.set(key, value)host.keychain.delete(key)
Use for tokens/secrets instead of plain project storage.
The executable Host namespaces Keychain items by project ID and stores JSON-serializable values using device-only Keychain accessibility. Keychain access currently uses the project's storage permission.
Location
host.location.authorization()host.location.current(options)host.location.watch(options, callback)host.location.heading(options, callback)host.location.monitorRegion(options, callback)host.location.stopMonitoringRegion(id)host.location.monitoredRegions()host.location.monitorVisits(callback)
The executable Host implements current-position, continuous location/heading streams, circular-region entry/exit monitoring and visit monitoring. Watch-style operations return cancellable handles. Region and visit monitoring require Project location: "always"; foreground current/watch/heading remain usable with whenInUse.
Background location is not implicitly granted merely because foreground location exists; it requires explicit Host/runtime support and appropriate iOS configuration.
Bluetooth Low Energy
Backed by CoreBluetooth. The executable Host implements:
host.bluetooth.authorization()host.bluetooth.scan(options)host.bluetooth.stopScan()host.bluetooth.connect(deviceId, options)host.bluetooth.disconnect(deviceId)host.bluetooth.services(deviceId)host.bluetooth.characteristics(deviceId, serviceId)host.bluetooth.read(request)host.bluetooth.write(request)host.bluetooth.subscribe(request, callback)
Subscriptions return a cancellable handle.
Device identifiers exposed to projects should be stable only to the extent iOS/CoreBluetooth permits; project logic must not assume hardware MAC-address access.
Camera and Photos
host.camera.capture(options)host.camera.captureDirect(options)host.camera.startRecording(options)host.camera.pauseRecording()host.camera.resumeRecording()host.camera.stopRecording()host.camera.recordingStatus()host.camera.list()host.camera.capabilities(position)host.camera.setZoom(options)host.camera.setTorch(options)host.camera.focus(options)host.camera.setExposure(options)host.audio.startRecording(options)host.audio.stopRecording()host.audio.recordingStatus()host.photos.authorization()host.photos.pick(options)host.photos.save(asset, options)host.photos.list(options)host.photos.get(id)host.photos.albums(options)host.photos.createAlbum(options)host.photos.update(id, options)host.photos.delete(options)host.photos.addToAlbum(options)host.photos.removeFromAlbum(options)
Captured/selected media is copied into the Project sandbox and returned as safe metadata. capture() presents the system still-camera UI; captureDirect() uses AVCapturePhotoOutput without presenting that picker. Direct capture supports position/uniqueId and flash. Video recording writes QuickTime movies and may include microphone audio when both camera and microphone access are approved. Recording options support position or a camera uniqueId, resolution (4k, 1080p, 720p, 480p), fps, quality, and audio. Pause/resume is implemented with internal recording segments; stopRecording() merges those segments into the single returned movie path. CameraPreview, VideoPlayer, AudioPlayer and MediaImage connect sandbox paths back to native UI.
PhotoKit access now covers both picker/save flows and library management. Projects with photos: true can query assets/albums, create albums, change favorite/hidden/creation-date metadata, delete assets, and add/remove assets from user albums. iOS read/write Photos authorization still applies, and limited-library authorization naturally limits which assets a Project can see.
Recorded/imported audio can be reduced to waveform peak buckets with host.media.waveform({ path, points }). Instantiated VideoPlayer/AudioPlayer nodes are controllable by node id through host.mediaPlayer.play, pause, seek, setVolume, setRate, and status.
Notifications
host.notification.authorization()host.notification.schedule(options)host.notification.cancel(id)host.notification.cancelAll()host.notification.list()
Project Notification scope remains local notifications. Apsule Host-level background synchronization uses a separate Relay/APNs channel and does not consume a Project's notification permission.
The executable Host supports one-shot or repeating time triggers, ISO date triggers, calendar-component triggers, title/subtitle/body, sound, badge, custom action buttons, cancellation and pending-list inspection. Identifiers/categories are Project-namespaced. Notification responses are delivered to actions.notification(payload) while that Project is active.
Scheduled local notifications are OS-managed persistent work. Once successfully registered with iOS, stopping or switching away from the project does not implicitly cancel them. They remain owned by the project for listing/cancellation and are removed only by an explicit cancellation/removal policy.
Apsule itself may also use iOS local notifications for Host-level events such as a project update being applied or a sync failure. These Apsule System Notifications are separate from Project Notifications and do not consume a project's notification permission.
System utilities
host.clipboard.read()host.clipboard.write(value)host.haptics.trigger(style)host.share.present(items, options)host.url.open(url)host.device.info()
These utilities are implemented. Clipboard access follows iOS pasteboard privacy behavior; share and URL operations use native system presentation/opening. Haptics supports selection, success/warning/error and standard impact styles.
host.share.present may also share Project-sandbox paths. Apsule includes a Share Extension/Open-In path for bringing external text/URL/image/file content toward a Project; external URLs are delivered through actions.openURL(payload) and imported files are copied into the target Project sandbox.
Apple Music
Apple Music access requires music: true in the Project permissions and the normal MusicKit user authorization. The Host contains NSAppleMusicUsageDescription and links MusicKit. The Apsule App ID used to sign/install the Host must also have the Apple MusicKit App Service enabled.
Authorization/subscription/storefront:
host.music.authorization.status()host.music.authorization.request()host.music.subscription.current()host.music.subscription.showOffer(options)host.music.storefront.current()
subscription.showOffer() presents Apple's native subscription sheet. Optional values include message: "join" | "addMusic" | "playMusic", itemId, affiliateToken, and campaignToken. The Project never receives Apple Music authentication tokens.
Playback and queue control:
host.music.player.play(options)host.music.player.prepare(options)host.music.player.pause(options)host.music.player.stop(options)host.music.player.next(options)host.music.player.previous(options)host.music.player.seek(seconds, options)host.music.player.status(options)host.music.player.playbackState(options)host.music.player.nowPlaying(options)host.music.player.setShuffle(mode, options)host.music.player.setRepeat(mode, options)host.music.player.setCrossfade(options)host.music.queue.get(options)host.music.queue.replace({ items, player })host.music.queue.playNext(options)host.music.queue.playLater(options)host.music.queue.setAffectsListeningHistory(enabled, options)
Playback options default to { player: "system" }, which controls the Music app through SystemMusicPlayer. Use { player: "application" } for Apsule-owned playback through ApplicationMusicPlayer. A playable catalog song or music video can be selected with { id, kind: "song" | "musicVideo" }. prepare() asks MusicKit to prepare the current queue before playback. Crossfade is application-player-only and requires iOS 26 or later. queue.setAffectsListeningHistory requires iOS 26.4 or later; on earlier systems queue.get() reports that field as unavailable.
Catalog/library/service conveniences are implemented on top of the same provider-scoped data bridge:
host.music.catalog.search/get/song/album/artist/playlist/musicVideo/station/radioShow/curator/recordLabel/genre/relationship/viewhost.music.library.list/get/search/songs/albums/artists/playlists/musicVideos/recentlyAdded/relationship/addhost.music.playlist.create/get/getTracks/addTracks/rootFolder/folder/folderChildren/createFolderhost.music.favorites.add/statushost.music.ratings.get/set/removehost.music.history.recentlyPlayed/recentlyPlayedTracks/recentlyPlayedStations/heavyRotationhost.music.recommendations.get/getAll/getById/contentshost.music.replay.summary/topSongs/topArtists/topAlbumshost.music.charts.get
The generic catalog.relationship, catalog.view, library.get, and library.relationship helpers reduce the need to add a new Host method for every Apple Music relationship or resource view. They still run through the same provider-scoped Apple Music bridge and Project music permission.
Replay currently sends Apple's supported filter[year]=latest value. The dedicated Replay helpers intentionally do not advertise arbitrary historical years through this endpoint.
The low-level escape hatch is:
const result = await host.music.api.request({
method: "GET",
path: "/v1/me/recent/played/tracks",
query: { limit: 10, types: ["songs", "music-videos"] }
});host.music.api.request accepts only relative /v1/... Apple Music API paths and always fixes the destination to api.music.apple.com. Project code cannot supply Authorization headers or read MusicKit tokens; MusicKit injects the required provider/user authentication inside the Host. The raw bridge currently permits GET, POST, PUT, PATCH and DELETE and only forwards safe representation headers (Accept, Content-Type, Accept-Language).
This split is intentional: common operations get stable high-level wrappers, while newly introduced compatible Apple Music REST endpoints can be used through music.api.request without waiting for a new Apsule Host build. A Host rebuild remains necessary for a future Apple Music feature that itself requires new compiled native support, Info.plist declarations, extensions, signing/App Services, or other platform configuration.
AuthenticationServices
host.authentication.apple.signIn(options)host.authentication.passkey.register(options)host.authentication.passkey.assert(options)
These APIs require authentication: true. Sign in with Apple returns the Apple user identifier plus the authorization code/identity token as Base64 data when Apple supplies them. Passkey registration/assertion uses the platform authenticator; Project JavaScript supplies the relying-party identifier and WebAuthn challenge material and receives credential/authenticator data for verification by its server.
The required Associated Domains, Sign in with Apple capability or related signing configuration cannot be created by a Project at runtime. If a chosen flow requires Apple capabilities missing from the installed Host signature, the call must fail rather than weakening the platform boundary.
WeatherKit
host.weather.current(options)host.weather.hourly(options)host.weather.daily(options)host.weather.alerts(options)host.weather.attribution()
Weather calls require weather: true and accept latitude/longitude; hourly/daily calls optionally accept a bounded limit. Results expose JSON-friendly conditions, temperatures, precipitation and alert metadata. WeatherKit attribution is a required part of presenting Apple Weather data, so the Host exposes the provider name, legal page/text and Apple Weather mark URLs through attribution().
WeatherKit also depends on the signed App ID/build having the necessary Apple Weather capability/service configuration. Runtime discovery only reports shipped code and does not promise that a particular sideload signature can use the service.
Translation
host.translation.translate(options)host.translation.supportedLanguages()host.translation.availability(options)
translation: true enables system Translation framework sessions. options.text is required for translation; source and target are optional language identifiers. Omitting the source allows the framework to identify it where possible. Projects can enumerate supported BCP-47 languages and check whether a language pair is installed/supported before starting translation. The Host owns the SwiftUI translation task/session and Project JavaScript receives only the translated text and language metadata.
Nearby Interaction
host.nearbyInteraction.availability()host.nearbyInteraction.create(callback)host.nearbyInteraction.run(sessionId, options)host.nearbyInteraction.pause(sessionId)host.nearbyInteraction.close(sessionId)
Projects with nearbyInteraction: true can create an NISession, exchange the returned discovery token with a peer through their own transport, then run peer ranging and receive distance/direction events where the device supports them. Optional camera assistance is enabled only when supported.
Nearby Interaction support varies by hardware and some configurations require Apple signing capabilities/entitlements. availability() reports the current device's precise-distance, direction and camera-assistance support before a Project starts a session.
Core Spotlight
host.spotlight.index(options)host.spotlight.delete(options)host.spotlight.clear()
spotlight: true lets a Project publish Project-scoped searchable items to the on-device Spotlight index. Item identifiers are namespaced by Project ID, and clear() removes only that Project's indexed domain rather than deleting another Project's data.
Wallet / PassKit
host.wallet.availability()host.wallet.add(options)
wallet: true gates Wallet access. add({ path }) additionally requires storage: true, reads a .pkpass from the Project sandbox, validates it as a PKPass, and presents Apple's native add-pass sheet. The Project does not receive access to unrelated Wallet data.
Timers and utility crypto
The JavaScript runtime provides browser-style setTimeout, clearTimeout, setInterval and clearInterval. All active timers are cancelled when the Project stops. host.timer.sleep(ms) is the Promise-based equivalent for action code.
host.crypto provides randomUUID, secure random bytes, SHA-256/SHA-512 hashing, HMAC, Base64 encode/decode, URL encode/decode and non-verifying JWT header/payload decoding. Hash/HMAC input may be text or Base64 without permission; file-backed input additionally requires Storage and is constrained to the Project sandbox.
Image and document processing
host.image.info(path)host.image.process(options)host.image.thumbnail(options)host.document.pdfInfo(path)host.document.createPDF(options)host.document.renderPage(options)host.document.preview(path)host.document.scan(options)
Image operations are sandbox-only and support crop, rotation, resize/max-dimension, JPEG/PNG/HEIC output and quality control. PDF operations can inspect, create, render and QuickLook-preview sandbox documents. Document scanning uses VisionKit and returns page images plus a generated PDF. All document/image operations require Storage; scanning additionally requires Camera.
Local Network
host.localNetwork.browse(options, onEvent)host.localNetwork.stopBrowse(id)host.localNetwork.connect(options, onEvent)host.localNetwork.send(id, options)host.localNetwork.close(id)
This family is separate from Internet HTTP access and requires localNetwork: true. It supports Bonjour discovery plus direct TCP/UDP connections. The default Host declares common Bonjour service types (_apsule._tcp, _http._tcp, _https._tcp, _ssh._tcp); a different service type may require a Host rebuild because iOS requires Bonjour types in Info.plist.
Permission introspection
host.permissions.summary() returns the Project's effective Host approvals. status(name) combines that approval with iOS authorization where a direct status API exists. request(name) explicitly requests supported iOS permissions such as Camera, Microphone, Photos, Notifications, Contacts, Calendar, Reminders and Speech. Other families continue to prompt when their own Host API is first used. openSettings() opens the iOS Settings page for Apsule.
Live Activities / Dynamic Island
host.liveActivity.authorization()host.liveActivity.start(options)host.liveActivity.update(id, state)host.liveActivity.end(id, state)host.liveActivity.list()
Live Activities use a generic Apsule Widget Extension backed by ActivityKit/WidgetKit. Project state supports common fields such as title, subtitle, status, detail, progress and SF Symbol icon; this stable generic state allows the Host to gain more presentation templates later without changing the Project API. Starting/updating/ending requires liveActivities: true in the Project permission set. Local Live Activities work independently of Relay. Remote ActivityKit push updates require a signing setup that includes the Apple push entitlement and Relay APNs credentials.
Map
Map is primarily a UI component. It supports marker/polyline data and emits map-tap/region-change events. Location data comes from host.location. Address lookup and route calculation use:
host.geocoding.geocode(address)host.geocoding.reverse(latitude, longitude)host.directions.calculate(options)
Background Project actions and Project push
host.background.schedule(options)host.background.list()host.background.cancel(id)host.background.cancelAll()
Background jobs use BGTaskScheduler. earliestSeconds and repeated intervals are hints to iOS, not exact timers; the system decides when the app receives execution time. When a job runs, Apsule loads that Project headlessly, invokes the configured action, waits for its Promise and then stops the runtime.
The Host can also receive Relay/APNs Project events. These require push: true, device APNs registration and Relay APNs credentials. They are delivered to actions.push({ name, data, receivedAt }). This Project event channel is separate from Host update commands and from local-notification permission.
Biometrics and Vision
host.biometrics.status()host.biometrics.evaluate(options)host.vision.recognizeText(options)host.vision.barcodes(options)
Biometrics uses LocalAuthentication and requires biometrics: true. Vision reads a Project-sandbox image and therefore requires storage access; OCR returns recognized text/confidence/bounds and barcode detection returns payload/symbology/confidence/bounds.
Contacts, Calendar and Reminders
host.contacts.authorization()/list(options)/create(options)/update(id, options)/delete(id)host.calendar.listCalendars()/listEvents(options)/createEvent(options)/updateEvent(id, options)/deleteEvent(id)host.reminders.list(options)/create(options)/update(id, options)/delete(id)
These are permission-gated CRUD wrappers around Contacts/EventKit. Event/reminder updates support common metadata such as dates, completion state, notes/location and relative alarms. The Host never grants access merely because the Project declared a permission; the normal iOS authorization prompt still applies.
Motion and Speech
host.motion.availability()host.motion.watch(type, options, callback)host.motion.pedometer(options)host.motion.pedometerWatch(options, callback)host.motion.activityWatch(callback)host.speech.authorization()host.speech.transcribe(options)host.speech.speak(options)host.speech.stop()
Motion streams accelerometer, gyro, device-motion or magnetometer updates through cancellable subscriptions. Pedometer APIs expose step count, distance, floors and pace/cadence where supported; activity monitoring reports stationary/walking/running/automotive/cycling classification. Speech-to-text operates on a Project-sandbox audio file and requires Speech, Microphone and Storage approval; text-to-speech uses the Speech permission without needing a file.
NFC, Health and Home
host.nfc.availability()/scan(options)/write(options)host.health.availability()/authorize(options)/queryQuantity(options)/saveQuantity(options)host.home.authorization()/listHomes()/accessories(homeId)host.home.readCharacteristic(homeId, characteristicId)/writeCharacteristic(homeId, characteristicId, value)host.home.actionSets(homeId)/executeActionSet(homeId, actionSetId)
NFC supports reading NDEF records and writing a text, URL or explicitly encoded NDEF record set to a writable tag. HomeKit exposes the home/accessory/service/characteristic graph plus characteristic reads/writes and action-set execution, rather than only listing homes. These APIs are implemented as generic wrappers, but Apple also gates them with signing entitlements/capabilities. The default Apsule signing configuration does not forcibly add restricted entitlements because doing so would break development/sideload profiles that do not contain them. A Host build with the corresponding entitlement is required before those APIs become usable on-device.
WebView control
WebView remains an explicit opt-in component. Each instance uses a non-persistent WebKit data store so one Project/WebView does not inherit another instance's cookies. Project JavaScript can control an instantiated WebView by node ID with host.webView.back, forward, reload and evaluate; host.webView.cookies.list/set/delete/clear controls that exact instance's WebKit cookie store. A page can send a JSON-compatible message back through window.webkit.messageHandlers.apsule.postMessage(...), which becomes the node's message event. Popup/new-window navigations are redirected into the same WebView rather than spawning unmanaged browser state.
Home-screen Project widget
The WidgetKit extension contains a configurable Apsule Project launcher widget. The user configures Project ID/title/subtitle/icon, and tapping it opens apsule://run so the Host can launch that installed Project. host.widget.update({ title, subtitle, value, progress, icon }) and host.widget.clear() provide generic data-driven display state. Shared dynamic state requires the Host and Widget extension to be signed with the configured Apsule App Group; when that entitlement is absent the API fails explicitly rather than silently pretending the widget updated.
App Intents / Shortcuts
The Host ships a generic Run Apsule Action App Intent. Shortcuts can provide an installed Project ID, action name and JSON payload. Apsule opens the requested Project, applies its normal permission approval flow, then invokes that Project action with the supplied payload. Project-specific intents are intentionally not compiled into the Host.
Platform constraints
Runtime discovery describes code shipped by the Host, not capabilities granted by Apple to the particular signature/profile. Entitlement-restricted APIs must fail explicitly when unavailable rather than pretending the device supports them. Background execution is opportunistic, system privacy indicators remain controlled by iOS, and extensions cannot bypass iOS sandbox/privacy rules.