Appearance
App
The App class connects to wish-core and gives you direct access to the p2p network. It's the foundation — RpcApp is built on top of it.
Creating an App
typescript
import { App } from '@wishcore/wish-sdk';
const app = new App({
name: 'MyApp',
protocols: ['myprotocol'],
});Auto-connects to wish-core via Unix socket (~/.wish/core.sock on macOS and Linux).
Options
| Option | Type | Description |
|---|---|---|
name | string | App name shown in Dashboard |
protocols | string[] | Protocol names this app speaks |
coreUnixSocket | string | Override Unix socket path (default ~/.wish/core.sock; WISH_SOCKET env also works) |
coreFd | number | Pre-opened socket fd (embedded-kernel scenarios) |
permissions | object | { admin: true } for admin access |
Events
ready
Fired when connected to wish-core and ready to operate.
typescript
app.on('ready', () => {
console.log('Connected');
});online(peer)
A peer with a matching protocol came online.
typescript
app.on('online', (peer) => {
console.log(peer.toString()); // "André@hp/myprotocol"
app.peer.send(peer, Buffer.from('hello'));
});offline(peer)
A peer went offline.
typescript
app.on('offline', (peer) => {
console.log(peer.toString(), 'disconnected');
});frame(peer, data)
Received a raw frame from a peer.
typescript
app.on('frame', (peer, data) => {
// data is a Buffer
console.log(peer.toString(), ':', data.toString());
});disconnected
Lost connection to wish-core. The SDK reconnects automatically.
Peer
The Peer object identifies who you're talking to.
typescript
peer.luid // Buffer — local identity UID
peer.ruid // Buffer — remote identity UID
peer.rhid // Buffer — remote host ID
peer.rsid // Buffer — remote service/app ID
peer.protocol // string — protocol nameDisplay
peer.toString() returns a human-readable string following peer notation:
typescript
peer.toString() // "André@hp/myprotocol"Names are resolved from wish-core's identity list. Falls back to 6-char hex when names aren't available:
typescript
peer.toString() // "a3f2e1@965907/myprotocol"Identity
peer.toUrl() returns the full peer URL used as a unique key:
<luid-hex>><ruid-hex>@<rhid-hex>/<rsid-hex>/<protocol>Identity API
app.identity.list()
List all identities visible to this app (own + contacts).
typescript
const identities = await app.identity.list();
// [{ uid: Buffer, name: 'André', privkey: true }, ...]Non-admin apps only see identities explicitly granted via the Wish app.
app.identity.get(uid)
Get full identity details including signers and hosts.
typescript
const identity = await app.identity.get(uid);
// { uid, name, privkey, signers: [...], contacts: [...], hosts: [...] }app.identity.sign(uid, document, claim)
Sign a document with an identity's key. The claim is an arbitrary buffer that becomes part of the signed statement.
app.identity.verify(document)
Verify a signed document. Returns the document with verification result.
Sending data
app.peer.send(peer, frame)
Send a frame to a peer. No built-in retry: if the peer's send buffer is full, the call fails immediately (507) — resending is your app's decision (drop, queue, or back off; a kernel-level drain signal is on the roadmap).
typescript
await app.peer.send(peer, Buffer.from('hello'));Frames are raw bytes — you decide the encoding. Most apps use CBOR:
typescript
import { encodeOne, decodeFirstSync } from 'cbor';
// Send
await app.peer.send(peer, encodeOne({ type: 'message', text: 'hello' }));
// Receive
app.on('frame', (peer, data) => {
const msg = decodeFirstSync(data);
console.log(msg.type, msg.text);
});Peer Pins
Declare that your app wants wish-core to maintain a connection to a specific peer.
app.peer.pin(luid, ruid, rhid, policy, priority?)
typescript
const ANY_HOST = Buffer.alloc(32); // all zeros = any host
await app.peer.pin(
myUid, // local identity
bobUid, // remote identity
ANY_HOST, // any host
'eager', // connect when possible
0 // highest priority
);Policies:
'always'— maintain connection persistently'eager'— connect when possible, allow disconnect
app.peer.unpin(luid, ruid, rhid)
Remove a peer pin.
app.peer.pins()
List this app's peer pins.
typescript
const pins = await app.peer.pins();
// [{ luid, ruid, rhid, policy: 'eager', priority: 0 }, ...]Identity name cache
App automatically caches identity names and host names from wish-core, used by peer.toString(). The cache is refreshed on identity changes.
typescript
// Look up names directly
app.identityCache.getName(uid) // "André" or undefined
app.identityCache.getHostName(uid, hid) // "hp" or undefinedSignals
Subscribe to real-time events from wish-core. Everything is event-driven — identities change, connections come and go, contacts are added.
typescript
const subId = app.subscribe('signals', [], (data) => {
const [name, payload] = data;
console.log('Signal:', name, payload);
});
// later
app.cancel(subId);The rule of thumb: frame and peer events arrive on the always-on app.on(...) path; everything else is opt-in RPC streaming via subscribe().
The payload is a CBOR array [name, ...args]. Most signals carry zero or one arg.
| Signal | Arg | Fires when |
|---|---|---|
identity | — | Identity list changed — new identity, name update, contact added |
connection | — | A transport connection was established or lost (admin tools only). |
peer.online | Peer | An app-level peer came online (same as the online event). |
peer.offline | Peer | An app-level peer went offline (same as the offline event). |
nearby | — | Nearby peer discovered or lost (admin only) |
The peer.online/peer.offline args are the same Peer shape the online/offline events deliver — { luid, ruid, rhid, rsid, protocol, online } (the UIDs are byte strings). The connection signal carries no payload; treat it as a nudge to re-list connections.
Use signals to keep your app reactive. For example, re-fetch app.identity.list() when the identity signal fires, or refresh your connection view when connection fires.
Low-level requests
For endpoints not yet wrapped by the SDK, use app.request() directly. See the Identity and Peers API pages for the full reference.
typescript
await app.request('admin.request', ['identity', 'Need identity for sync']);Connection lifecycle
┌──────────┐ ┌───────────┐ ┌──────────┐
│connecting│────→│ connected │────→│ ready │
└──────────┘ └───────────┘ └──────────┘
│ │
│ ┌─────────────┘
▼ ▼
┌──────────────┐
│disconnected │──→ auto-reconnect
└──────────────┘The App auto-reconnects on disconnect (2 second delay). All pending requests are rejected with DISCONNECTED. All peers are marked offline.
App permissions
Apps are sandboxed by wish-core. By default, an app:
- Has no identities (must be granted via the Wish app)
- Sees no contacts
- Cannot make admin API calls
Grant identities and configure contact visibility through the Wish tray app (Dashboard).