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 (macOS: ~/Library/Application Support/Wish/core.sock, Linux: ~/.wish/core.sock).
Options
| Option | Type | Description |
|---|---|---|
name | string | App name shown in Dashboard |
protocols | string[] | Protocol names this app speaks |
coreUnixSocket | string | Override Unix socket path (auto-detected by default) |
corePort | number | TCP port (legacy, prefer unix socket) |
coreHost | string | TCP host (default: 127.0.0.1) |
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. Retries automatically on buffer-full (up to 15 seconds).
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
app.requestBare('signals', [], (msg) => {
if (msg.data === 'ok') return; // subscription confirmed
const [name, data] = msg.data;
console.log('Signal:', name, data);
});| Signal | Fires when |
|---|---|
identity | Identity list changed — new identity, name update, contact added |
connections | Connection established or lost |
nearby | Nearby peer discovered or lost (admin only) |
Use signals to keep your app reactive. For example, re-fetch app.identity.list() when the identity signal fires, or update your UI when connections changes.
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).