Projects
How Extentos identifies a project across Android and iOS — the Extentos app id (config.appId) decoupled from the store bundle id, and how the same app id shares one project across platforms.
A project in Extentos is one app — your Android version and your iOS version live under the same project. The dashboard at extentos.com/projects groups them automatically, and the agent's createSimulatorSession calls flow into the right project without you doing anything special — if you follow one convention. This page explains exactly how project identity works, what makes two platforms join under one project, what to do when they don't, and what each piece of stored data means.
TL;DR
- Each project has a Project ID (a reverse-DNS identifier, e.g.
com.example.myapp). - The Project ID is the Extentos app id you scaffold with (
config.appId) — it is decoupled from your store bundle id. It defaults to your bundle id but does not have to equal it. - Give both platforms the same Extentos app id and they share one project — two platform chips, one card, zero manual setup. This works even when the store bundle ids differ (Android
applicationId≠ iOS bundle identifier), which is common on apps already shipped to both stores. - The id each shipped app actually broadcasts is preserved per row, so the Settings page always shows the truth.
How project identity works
Project identity is the appPackage you pass to generateConnectionModule — recorded in extentos.manifest.json and baked into the app as config.appId (iOS: the EXTENTOS_APP_ID Info.plist key; Android: the appId field of ExtentosConfig). The SDK reports this id for agent config, telemetry, and sounds. It is independent of the store bundle id — you can set it equal to your bundle id (the default expectation) or to any shared value you choose.
This value is sent to the backend as projectInstallId. The backend's get-or-create logic looks for an existing saved sim under (your account, this projectInstallId, this platform); if one exists, it's returned; otherwise a new one is minted. The dashboard groups all of an account's sessions by projectInstallId — one card per unique value.
If the project doesn't have a manifest yet (you've called createSimulatorSession before generateConnectionModule), the MCP server falls back to:
- Android:
rootProject.namefromsettings.gradle.kts, prefixed asandroid:<name>(e.g.android:my-app) - iOS: no fallback — anonymous mint, no project grouping
Once generateConnectionModule writes the manifest, future mints use the manifest's appPackage, so the project identity becomes stable.
Two different things are easy to confuse here. Your project identity
(projectInstallId, from the appPackage you scaffold with) is a grouping
label — it is what puts Android and iOS under one dashboard card, and it is
not secret. Your project key (EXTENTOSProjectKey / EXTENTOS_PROJECT_KEY,
a pk_… value) is a credential — it authenticates your app to the managed AI
gateway and bills usage to your account. This page is about the first one.
Sharing one project across Android + iOS
Give both platforms the same Extentos app id (the appPackage you scaffold with):
- Scaffold Android with
appPackage = com.example.myapp. - Scaffold iOS with
appPackage = com.example.myapp.
Both apps then broadcast that id to Extentos, so createSimulatorSession from each platform produces the same projectInstallId:
- First mint (e.g. Android): a new project row is created with
projectInstallId = com.example.myapp. - Second mint (e.g. iOS): the get-or-create lookup finds the existing project, and the iOS row joins under the same
projectInstallId. - Dashboard: ONE project card with two platform chips (Android · iOS).
This holds even when your store bundle ids differ. Because the Extentos id is config.appId and not the store identifier, an app whose Android applicationId is com.co.app and iOS bundle id is com.co.App (different, and locked by the stores) still shares one project — you just give both the same Extentos appPackage. Integrating Extentos is itself a store release, so you pick the shared id at integration time at no extra cost. Matching the appPackage is the mechanism.
Linking a project (recovery path)
The same-id approach above is what you want in almost every case. But if you've already shipped Extentos on both platforms under different, store-locked ids — and can't re-release to unify them — you'd otherwise be stuck with two cards. For that case there's Link, on a project's Settings tab:
- On the project you want to be canonical, open Settings → Project identifier → Link project…
- Pick the other project. Its install-id is registered as an alias of this project, and its simulators move here.
Unlike a cosmetic relabel, the alias is resolved server-side: the linked platform's shipped app keeps broadcasting its original id, and Extentos maps that id to the canonical project for agent config, sounds, and analytics. The real per-platform id is preserved and shown in Settings. This is the recovery path — matching the appPackage up front is simpler and needs no linking.
What's stored on each session row
There are three identifier fields on every session row, each with a distinct role:
projectInstallId— the dashboard grouping key = theappPackage(config.appId) at mint time. Controls which card a session appears under.platformInstallId— the id the shipped app actually broadcasts, set on insert and never modified. EqualsprojectInstallIdin the normal case; differs only when a separate install-id has been linked to this project. Surfaces in Settings → Project info → per-platform identifiers when it diverges.appLabel— the friendly display name shown on the dashboard card and the project header. Cosmetic, editable from Settings → Project info → Display name.
Per-platform truth in Settings
Because platformInstallId is preserved per row, a project stays honest about the id each platform actually broadcasts. On the project's Settings page:
- Project ID displays the canonical
projectInstallId(the grouping value). - Per-platform identifiers appears as a separate section only when at least one platform's
platformInstallIddiffers from the Project ID. It lists each diverging platform with its actual identifier, e.g.:Android: com.example.android-originaliOS: com.example.ios-original
If your identifiers match (the happy path), this section is hidden — the project ID alone is the complete picture.
Editing the project name
The Project ID is read-only on the dashboard — your app's source is the source of truth. Editing the dashboard's stored projectInstallId would just create a mismatch with the next mint (which derives the value from the manifest again).
The display name (appLabel) is editable on the Settings tab. Saving it propagates atomically across every platform row of the project, so Android and iOS rows display the same label.
What the agent does
createSimulatorSession is get-or-create under the hood. The first call from a project mints; subsequent calls return the existing saved sim with status: "resumed". Rotating the session ID (URL hijack recovery, a deliberate fresh identity) is a two-step act: deleteSimulatorSession({ sessionId }), then mint again. There is no one-call force-fresh — a silent replace left device registrations pointing at apps that were already gone.
When the agent mints from a second platform with a matching identifier, the second-platform row simply joins the existing project — no special tool call, no flag, no agent prompt required. The agent just calls createSimulatorSession for the iOS or Android project as normal.
Reference
- Settings UI lives at extentos.com/projects/[id]/settings.
- Backend endpoints:
PATCH /api/projects/[projectId]updates the display name across all rows.POST /api/sessions/[id]/deletesoft-deletes a session — the only per-sim lifecycle action. Rotating an ID is delete, then mint.
- Schema columns on
simulator_sessions:project_install_id,platform_install_id,app_label(plus the rest of the row).
Related
Sessions
How an Extentos session works — the glasses connection-state machine, what persists across backgrounding and reconnects, and the three browser-simulator roles.
Quickstart with an AI agent
Install the Extentos MCP server and let your AI agent scaffold Meta Ray-Ban smart-glasses capabilities into a native iOS or Android app. Free to start.
Architecture
How Extentos fits together — AI agent, MCP server, native Kotlin/Swift SDK, four transports (system audio, Meta DAT, browser sim, local in-memory sim), and the backend.
Permissions
Extentos derives Android permissions and iOS Info.plist keys from the capabilities your handler uses, plus the always-required Bluetooth and Meta DAT keys.
Protocol overview
The Extentos simulator wire protocol — WebSocket roles, hardware-ready gating, the inject envelope, event log, and voice proxy. The SDK speaks it for you.