Appearance
Apps
Manage app permissions, identity grants, and contact visibility. Used primarily by the Wish Dashboard.
App lifecycle
app.list (void): App[]
List all registered apps with their permissions.
app.remove (wsid: Buffer): bool
Remove an app and all its permissions.
Identity grants
Apps are sandboxed — they only see identities explicitly granted to them.
app.grantIdentity (wsid: Buffer, uid: Buffer): bool
Grant an identity to an app. The app will see this identity in identity.list and can receive peers for it.
app.revokeIdentity (wsid: Buffer, uid: Buffer): bool
Revoke an identity from an app.
Contact visibility
By default, apps cannot see any contacts. Contact visibility must be configured per app, per identity.
app.setContacts (wsid: Buffer, uid: Buffer, settings: { mode, contacts? }): bool
Set contact visibility for an app.
Modes:
'none'— no contacts visible (default)'all'— all contacts visible'allow'— only listed contacts visible'deny'— all contacts except listed ones
typescript
await app.request('app.setContacts', [wsid, uid, { mode: 'all' }]);
// Only specific contacts
await app.request('app.setContacts', [wsid, uid, {
mode: 'allow',
contacts: [bobUid, carolUid]
}]);WARNING
Contact visibility changes only take effect on new connections. Existing connections won't see updated permissions until reconnected.
app.getContacts (wsid: Buffer, uid: Buffer): { mode, contacts }
Get contact visibility settings for an app.
Admin privileges
app.setAdmin (wsid: Buffer, admin: bool): bool
Set admin privileges for an app.
Peer inspection
app.peers (wsid: Buffer): RemotePeer[]
List the remote hosts currently running the app's protocol — i.e. the app-level peers that are online right now. wish-core walks its active connections and reports one entry per connection whose remote side speaks the target app's protocol, so this is genuinely peer-level (an app is present), not merely a list of connected devices.
This is the way to seed presence state on startup; pair it with the live online/offline events (or the peer.online/peer.offline signals) to keep it current.
Each entry (RemotePeer):
typescript
{
ruid: Buffer; // remote identity
rhid: Buffer; // remote host (device)
bytesIn: number;
bytesOut: number;
transport: string; // "tcp" | "utp"
}Note this is narrower than the full Peer tuple from the online/offline events (no luid/rsid/protocol) — the protocol is implied by the wsid you queried, and luid by the app's grants.
Admin request routing
Regular apps ask the admin app for help via admin.request. The kernel forwards each request to admin apps as an admin.request signal — { id, appId, appName, requestType, access? }, where requestType is the app's kind and access its args object, verbatim — and keeps it pending until answered.
admin.requests (void): AdminRequest[]
List the requests not yet answered, in the same shape as the signal. Call it after subscribing to signals: a request may predate your admin app (on mobile the admin UI is often started by the request), so signal-only delivery misses it.
admin.response (id: int, success: bool, message?: string): bool
Answer a request. The kernel drops it from the pending list and emits an admin.response signal { id, requestType, success, message? } to the requesting app. Always answer — also when the user dismisses or a newer request supersedes an older one — or the request lingers in admin.requests.
Handling by kind (Wish's convention; the kernel enforces none of it): identity → consent flow, then app.grantIdentity + app.setContacts per the access hint, then admin.response(id, true); identity.contact / identity.join → steer the People view to access.uid, answer when the user is done. Unknown kinds → show, decline with a message.