Skip to content

Identity ​

Your app works with cryptographic identities — real people and devices, not usernames and passwords.

Listing identities ​

identity.list (void): Identity[] ​

List the identities available to your app. Returns own identities and contacts (remote identities you've been introduced to). Own identities have privkey: true — the key itself stays in wish-core.

typescript
const identities = await app.identity.list();

for (const id of identities) {
    console.log(id.name, id.privkey ? '(own)' : '(contact)');
}

Each identity has:

FieldTypeDescription
uidBufferIdentity UID (32 bytes) — stable, never changes
namestringDisplay name
privkeybooleantrue for own identities, false for contacts

Getting identity details ​

identity.get (uid: Uid): Identity ​

Get full details for an identity — signers, contacts, and hosts.

typescript
const identity = await app.identity.get(uid);

for (const host of identity.hosts || []) {
    console.log(`  ${host.name || host.role || 'unnamed'}`);
}
FieldTypeDescription
signersSigner[]Cryptographic keys that can act as this identity
contactsBuffer[]Contact UIDs (own identities only)
hostsHostInfo[]Known devices — { hostId, name?, role? }

Signing and verification ​

Your app can sign data with any identity it has access to, and verify signatures from anyone.

identity.sign (uid: Uid, document: Document, claim: Buffer): Document ​

Sign a document with an identity's key. The claim is an arbitrary buffer that becomes part of the signed statement.

typescript
const signed = await app.identity.sign(uid, document, claim);

identity.verify (document: Document): Document ​

Verify a signed document. Returns the document with verification result.

typescript
const result = await app.identity.verify(signedDocument);

Asking the admin app for help ​

An app never creates identities, adds contacts or joins devices itself — the user does that in Wish, the admin app — the one app that holds the identities' private keys and decides which apps may use them. What an app can do is ask the admin app to help the user with one of those tasks. That is admin.request.

admin.request (kind: string, args?: object): int ​

Send a request to every admin app. Returns the request id. The kernel keeps the request until an admin app answers it (see admin.response), and forwards it as an admin.request signal to admin apps — including ones that connect after it was sent (Wish lists unanswered requests on start-up), so it's safe to send before Wish is running.

kind names the admin API family the admin app should drive, and args scopes it. The kernel treats both as opaque: it stores kind and forwards args verbatim to the admin app. The kinds Wish understands:

kindargsWhat Wish does
identity{ contacts?: 'all' | 'none' } — access hintAsks the user which identity the app should use (existing, new, or joined from another device) and grants it. The hint proposes the contact visibility to grant by default; the user confirms and can tighten it. Absent hint → identity only, no contacts.
identity.contact{ uid }Opens the People view for uid with contact discovery ready (nearby peers, directory search). For "find people" onboarding steps.
identity.join{ uid }Opens the People view on uid with the join another device flow (join mode) ready.

Every kind except identity is scoped to an identity the app already holds — args.uid is required (byte string), and the admin app rejects the request if it's missing. The naming rule: a kind mirrors the admin endpoint family it lands in (identity, identity.contact.*, identity.join.*), so a reader who knows the admin API can guess what each opens.

typescript
// First run: no identity yet — ask for one, proposing access to all its contacts.
if (!(await app.identity.list()).some((id) => id.privkey)) {
    await app.request('admin.request', ['identity', { contacts: 'all' }]);
}

// Later, from an onboarding hint in the app's own UI:
await app.request('admin.request', ['identity.contact', { uid: me.uid }]);

Reacting. Don't wait on the request id — the admin app answers when the user is done, which may be never. Instead:

  • re-fetch identity.list() on the identity signal: a grant, a new contact or a new host all fire it;
  • optionally listen for the admin.response signal ({ id, requestType, success, message? }) to know the request was closed — e.g. to bring your window back to the front.

Unknown kinds are shown to the user as such and declined. Apps must not depend on Wish's UI shape; only the request/response contract above is stable.

Reacting to changes ​

The identity signal fires whenever identities change — new identity created, contact added, name updated. Re-fetch app.identity.list() to stay current.

Identity name cache ​

The SDK automatically caches identity and host names from wish-core. This powers peer.toString() and is also available directly:

typescript
app.identityCache.getName(uid)            // "André"
app.identityCache.getHostName(uid, hid)   // "hp"

The cache refreshes automatically when identities change.