OptionalauthorOptionalbadgeReactive unread count for the launcher badge — unreadMailCount and friends.
OptionalbundleRemote bundle URL if dynamically loaded
Utility class for the launcher icon (from app-utilities.css) — bg-indigo-600.
A class, not a color value. AppIcon interpolates this straight into a class
attribute, so a hex string becomes a class name matching no rule and the icon renders
with no background at all. The docstring here used to offer "or hex string", which is
why that is worth stating outright.
Does this app ship with the phone, and is it therefore not uninstallable?
Required, and deliberately not inferred. It was once isSystem, defaulted from
author — a display string — so an app whose author was anything other than
'Community' silently became unremovable, and naming yourself 'gPhone' was enough to
do it. Two things followed from deriving a protection boundary instead of stating it:
the derivation was circular (isSystem read author, then author read isSystem),
and a second, subtly different copy of it grew in the Store — so the registry and the
uninstall button could disagree about the same app, and the button threw.
A remote app is never core. defineApp forces false when isRemote is set and
throws on an explicit core: true beside it: a downloaded bundle asking for protection
is broken or hostile. That closes a footgun rather than preventing an attack — an
add-on runs in the shell's own JS context regardless (§7).
OptionaldefaultDefault props passed when launching app component
OptionaldescriptionBrief description of app functionality
A Svelte component, a snippet, or an image URL. Null renders no glyph, which is what a remote app that shipped without one gets.
Unique id — contacts, crypto_tracker. lower_snake_case.
The stable one. It is a directory name, the gphone:<id>: storage namespace, the
<app> segment of every net event, a keybind claim and the ?app= deep link. Renaming
it is a data migration, not a rename, which is exactly why it is not derived from
name: a display string should be free to change without orphaning stored data or
altering event names that server code handles.
OptionalinstalledISO date string when app was installed
OptionalisFlag indicating whether app was dynamically loaded from a remote bundle
OptionalkeybindsDefault hotkeys this app wants, shown grouped under it in Settings > Shortcuts. Only apps for which this is set contribute app-scoped keybinds; most apps omit it.
Display name on the home screen. Cosmetic — the launcher label, the Store listing and the error-boundary message, nothing that is keyed on.
Optional on input. defineApp title-cases the id when it is omitted, so crypto_tracker
becomes "Crypto Tracker" and all twelve apps in this repo needed only the id. Give it
explicitly when the display name is not simply the id — "GPS", "My Bank".
OptionalnetworkGPHONE-24: the exact origins a core: false add-on's sandboxed frame may fetch().
The frame has no Content-Security-Policy otherwise, so an add-on's own JS — not just
the code an author wrote, but anything a supply-chain compromise slipped into its
bundle — could silently call out to any host on the internet. srcdoc.ts turns this
list into the frame's connect-src; an empty or absent list means connect-src 'none', blocking outbound fetch() entirely.
Declaring this without also declaring requiresNetwork: true is refused by
defineApp — asking for real network egress while saying the app does not need
network at all is a contradiction, not an oversight to let through quietly. The
reverse is fine and common: an app can need requiresNetwork (in-game cell signal,
for its ordinary server-proxied calls) without ever calling fetch() itself, and
should simply leave this empty rather than declare a host it does not use.
Full origins only (https://api.example.com, no path) — defineApp rejects
anything else, since a bare hostname or a wildcard is not a valid CSP source and
srcdoc.ts does no correction of its own.
OptionalpermissionsWhat this app reaches for, as shown to a player in the Store.
A disclosure, not only a sandbox. A core: true app runs in the shell's own JS
context, so nothing stops it importing a hook it did not declare; a core: false
add-on runs in a sandboxed iframe and has the shell re-check every call. Either way
§2.9 is the real boundary underneath: the server gates privileged actions and does not
take a NUI request as proof of intent.
What is enforced is that the disclosure is true. sdk/permissions.test.ts reads
each app's SDK imports and fails the build if it reaches for contacts, photos, the
camera, notifications or local storage without saying so. Declaring more than the scan
finds is allowed — network and location have no hook to infer them from.
OptionalpreloadLoad this app's data while the phone opens, before the launcher draws.
Declared by the app rather than listed by the shell. bootstrap.ts used to name each
store by hand, and nothing connected that list to the apps it was loading for — so an
app that shipped a badgeStore and was forgotten there showed a stale badge until
somebody opened it, which is precisely when a badge no longer matters.
Only for what has to be right before first paint. Everything else belongs in
onAppForeground, which reloads per visit; this runs once per phone-open.
OptionalrequiresHide the app entirely unless the player holds an admin ace.
A visibility hint, not a permission. The server gates every privileged action it exposes; this only stops the icon appearing for everyone else.
OptionalrequiresDoes this app need the NUI bridge to work at all?
Used to be inferred from permissions.includes('network') — but network disclosed
nothing (every app talks to its own service, so marking it on everyone would mean
marking it on no one) and conflated two different questions: what a player should be
told an app reaches for, and whether the phone should block the app while signal is
out. This is the second question, stated on its own rather than smuggled through the
permission list. Shell.svelte's "Not Network" gate reads this, not permissions.
OptionalupdatedISO date string when app was last updated
OptionalversionSemantic version string (e.g. "1.0.0")
App author or developer team