@actnet/app-core - v0.1.0
    Preparing search index...

    Class AppCore

    The main client handle. Wraps a SQLCipher-backed local store, a HTTP/WS client for the homeserver, and a background reconnect task.

    Construct via one of the four static factories (AppCore.createAccount, AppCore.finalizeAccount, AppCore.recoverFromBlob, AppCore.login). Each instance spawns a background task that owns the WebSocket lifecycle and pushes decrypted messages + delivery-status updates into the AppCore.nextEvents queue.

    All methods are safe to call concurrently. Async methods run on the napi libuv threadpool so they do not block the JS event loop.

    Index

    Methods - Account

    • Register / refresh this device's push token with the relay and the homeserver. Idempotent; safe (and recommended) to call on every launch.

      Rotates the pseudonym after ~7 days or when (deviceToken, platform) changes.

      Parameters

      • deviceToken: string
      • platform: "apns" | "fcm"

        "apns" (iOS) or "fcm" (Android).

      • relayUrl: string
      • environment: "sandbox" | "production"

        "sandbox" for debug builds, "production" for App Store / TestFlight builds.

      Returns Promise<void>

    • Re-upload the encrypted recovery blob (for instance after joining a new homeserver).

      Parameters

      • recoveryKey: Uint8Array

        32-byte symmetric key.

      • servers: string[]

        Updated list of homeserver URLs.

      Returns Promise<void>

    • Whether this account has a recovery blob configured on the server.

      Returns Promise<boolean>

    • Leave this server (docs/53): leave every group hosted here, then delete the account on the server. Your identity, contacts, and other servers are unaffected. Intended for non-discovery memberships.

      Returns Promise<void>

    • Delete this identity from the network as completely as the protocol allows (docs/53): leave-cascade every server, tombstone the DID in the PLC directory, then wipe all local state. Irreversible. Throws (leaving local state intact) if the PLC tombstone can't be submitted.

      Returns Promise<void>

    Methods - Connection

    • Opportunistically retry/validate connectivity now: wakes the reconnect loop if it's backing off, and probes a live socket for silent death. Cheap and infallible. Call on any signal that connectivity may have changed (foreground, network change, user action).

      Returns void

    • Tell the core whether the app is foreground-active. Gates the WebSocket keepalive (foreground-only, for battery); a transition to active also probes the connection. Bots can leave this untouched — the keepalive defaults on.

      Parameters

      • active: boolean

      Returns void

    • Async iterator over decrypted messages and delivery-status updates from the homeserver. The recommended receive path:

      for await (const e of core.events()) {
      if (e.kind === "message") console.log(e.message.body);
      }

      Internally drains the native batch queue and yields one event at a time. Single-consumer: run from exactly one async loop. The iterator runs forever; break (or return) to stop, or it ends if the channel is closed (i.e. the AppCore is torn down).

      Returns AsyncGenerator<IncomingEvent, void, void>

    • Lower-level variant of events: block until at least one event is available, then drain and return the whole batch.

      Prefer events for normal use; this is here for callers that want explicit batch-processing semantics. Same single-consumer rule applies. Throws when the event channel is closed.

      Returns Promise<IncomingEvent[]>

    • Async iterator over admin-only events from the homeserver. Mirrors events but for the parallel admin queue. Only adminbot sessions ever receive admin events; for any other session this iterator hangs forever waiting on an empty queue.

      for await (const e of core.adminEvents()) {
      if (e.kind === "accountJoined") await invite(e.accountJoined.did);
      }

      Single-consumer: run from exactly one async loop. Drive it concurrently with events via Promise.all.

      Returns AsyncGenerator<AdminEvent, void, void>

    Methods - Constructors

    • Create a brand-new account on the homeserver.

      Generates an identity keypair + rotation key, computes a did:plc:..., submits the PLC genesis op, registers with the homeserver, and (if recoveryKey is non-empty) uploads an encrypted recovery blob.

      Parameters

      • serverUrl: string

        Homeserver URL.

      • dbPath: string

        Where to create the SQLCipher database file.

      • dbKey: string

        Passphrase used to derive the SQLCipher key.

      • recoveryKey: Uint8Array

        32-byte symmetric key (from passkey PRF or recovery phrase). Pass an empty Uint8Array to skip recovery setup.

      • displayName: string

        Profile display name. Encrypted under a fresh profile key and uploaded with registration.

      • OptionalinviteToken: string

      Returns Promise<AppCore>

    • Register a brand-new bot account on the homeserver.

      Bot accounts skip the PLC directory and receive a did:local:... DID assigned by the server. displayName is stored plaintext on the server (so bots can be looked up by name); humans use createAccount which encrypts the display name into a profile blob instead.

      No recovery blob is uploaded — bots are operator-managed and don't use the passkey recovery flow.

      Parameters

      • serverUrl: string
      • dbPath: string
      • dbKey: string
      • displayName: string
      • OptionaldidSuffix: string
      • OptionalinviteToken: string

      Returns Promise<AppCore>

    • Finalize registration using a previously prepared identity.

      Use this when a passkey ceremony needs the DID up front: call PreparedAccount.create to compute the DID locally, register the passkey with that DID, then call this to encrypt the recovery blob, submit the PLC genesis op, and complete server registration.

      The prepared handle is consumed.

      Parameters

      • prepared: PreparedAccount
      • dbPath: string
      • dbKey: string
      • displayName: string
      • OptionalinviteToken: string

      Returns Promise<AppCore>

    • Recover an account from a passkey-protected recovery blob.

      Downloads the encrypted blob, decrypts it with recoveryKey, restores the identity + rotation keys into a freshly opened SQLCipher store, registers a replacement device with the homeserver, and returns an AppCore ready to use.

      Contacts see no safety-number change because the original identity key is preserved.

      Parameters

      • serverUrl: string
      • did: string
      • recoveryKey: Uint8Array
      • dbPath: string
      • dbKey: string
      • displayName: string

      Returns Promise<AppCore>

    • Open an existing account from a local SQLCipher store and authenticate with the homeserver.

      Parameters

      • dbPath: string
      • dbKey: string

      Returns Promise<AppCore>

      if the store has no account; if dbKey is wrong; or if the server rejects authentication.

    • Open a bot account, registering it on first run and re-logging-in thereafter — the idempotent bootstrap an operator-run bot needs.

      If the store at dbPath already holds an account, this logs in and serverUrl / displayName / didSuffix are ignored (they were fixed by the original registration). Otherwise — a fresh deploy, or an empty DB left by a registration that failed after opening the store — it registers a new bot account.

      The "is the store empty?" decision is made inside the core, so callers no longer pattern-match on error strings to tell a first run from a real failure. Layer identity policy on top by checking AppCore.did against the DID you expect.

      Parameters

      • serverUrl: string
      • dbPath: string
      • dbKey: string
      • displayName: string
      • OptionaldidSuffix: string
      • OptionalinviteToken: string

      Returns Promise<AppCore>

    • Build a bootstrap registration token from a shared secret (docs/24).

      Pass the result as inviteToken to createBotAccount / loginOrCreateBot to register against a closed-registration server. Naming project links the new account into that Project — e.g. the superuser Project (slug "adminbot") to bootstrap admin authority.

      Parameters

      • serverUrl: string
      • secret: string
      • Optionalproject: string

      Returns string

    Methods - Contacts

    • Every known contact, newest-interaction-first. Joins the curation flag from contacts with the cached display name from contact_profiles.

      Returns Promise<ContactRow[]>

    • Touch a contact row, creating it if missing.

      Parameters

      • did: string
      • curated: boolean

        true marks this as a deliberate gesture (sticky). Pass false to record an interaction without curating.

      Returns Promise<void>

    • Set or clear the local pending-message-request flag on a contact. app-core no longer sets this automatically on receive; a bot that surfaces a message-request inbox calls this when it receives a DecryptedMessage with isRequest === true. Most bots don't track requests and can ignore this entirely.

      Parameters

      • did: string
      • pending: boolean

      Returns Promise<void>

    Methods - Device linking

    • Show a pairing code to link a new device to this identity. Creates an ephemeral mailbox session and returns the pairing string to render as a QR and/or copyable code. mailboxServer defaults to this device's own server. Follow with AppCore.linkSendBundle.

      Parameters

      • OptionalmailboxServer: string

      Returns Promise<string>

    • Ingest the new device's pairing code (scanned or pasted). Follow with AppCore.linkSendBundle.

      Parameters

      • code: string

      Returns Promise<void>

    Methods - Direct Messages

    • Send an encrypted DM. The body is wrapped in a content envelope (with sentAt) before encryption and fanned out to every active device of the recipient.

      Parameters

      • recipientDid: string
      • body: string

        Text body. Sent verbatim — UTF-8 on the wire.

      • OptionalsentAt: Instant

        Send-time stamped into the envelope. Defaults to Temporal.Now.instant().

      Returns Promise<void>

    • Send a text message to a SendTarget — a DM or a group — without branching on the transport yourself. A "dm" target behaves exactly like sendDm (envelope + per-device fan-out + marks the contact curated); a "group" target behaves like sendGroupMessage (Sender Key encryption + per-member fan-out).

      Handy for replying back through whichever channel a message arrived on:

      const target: SendTarget = msg.groupId
      ? { kind: "group", groupId: msg.groupId }
      : { kind: "dm", recipientDid: msg.senderDid };
      await core.send(target, "got it");

      Parameters

      • target: SendTarget
      • body: string

        Text body. Sent verbatim — UTF-8 on the wire.

      • OptionalsentAt: Instant

        Send-time stamped into the envelope. Defaults to Temporal.Now.instant().

      Returns Promise<void>

    • Encrypt and upload an attachment blob (docs/35-attachments.md), returning the Attachment pointer to pass to sendWithAttachments. The blob is padded, CBC+HMAC-encrypted under a fresh one-off key, and uploaded; the server only ever sees ciphertext.

      Parameters

      • plaintext: Uint8Array
      • contentType: string
      • opts: {
            fileName?: string;
            width?: number;
            height?: number;
            durationMs?: number;
            thumbnail?: Uint8Array<ArrayBufferLike>;
            flags?: number;
        } = {}

      Returns Promise<Attachment>

    • Send a text message with attachments and/or link previews to a SendTarget. Attachments (and each preview's image) must already be uploaded via uploadAttachment. body may be empty for an attachment-only message.

      Parameters

      Returns Promise<void>

    • Download, verify, and decrypt an attachment blob (docs/35). Aborts before decrypting if the downloaded bytes don't match the pointer's digest.

      Parameters

      Returns Promise<Uint8Array<ArrayBufferLike>>

    • Record that an attachment's blob was downloaded to localPath (docs/35), so later loads render the local file instead of re-fetching.

      Parameters

      • attachmentId: string
      • localPath: string

      Returns Promise<void>

    • React to a message in a DM or group (docs/33-reactions.md). One reaction per account per message: a fresh emoji replaces any prior one; remove clears it. Applies locally and sends a reaction control message to the conversation.

      Parameters

      • target: SendTarget

        Where the reacted-to message lives (DM peer or group).

      • targetAuthor: string

        DID of the reacted-to message's author.

      • targetSentAt: Instant

        sentAt of the reacted-to message — its wire identity.

      • emoji: string

        The reaction emoji (ignored when remove is true).

      • remove: boolean

        true to clear this account's reaction on the target.

      • OptionalsentAt: Instant

        This reaction op's timestamp. Defaults to now.

      Returns Promise<void>

    • Fetch and decrypt all pending messages from the homeserver.

      Most callers should use AppCore.nextEvents (the push-driven stream) instead. This is the explicit-pull variant, mainly useful for tests and one-shot tools.

      Returns Promise<DecryptedMessage[]>

    • Send a read receipt for a batch of messages to recipientDid. Fans out across all of the recipient's active devices.

      Parameters

      • recipientDid: string
      • sentAts: Instant[]

        sentAt instants of the messages being acknowledged.

      Returns Promise<void>

    Methods - Group Admin

    • Invite recipientDid into the group with the given role. Also sends the substrate GroupContext DM + Sender Key distribution message so the invitee can decrypt the group on accept.

      Parameters

      • groupId: string
      • recipientDid: string
      • role: GroupRole

      Returns Promise<void>

    • Admit a requester from pendingApprovals into the group.

      Parameters

      • groupId: string
      • encryptedMemberId: string

      Returns Promise<void>

    • Reject a requester from pendingApprovals.

      Parameters

      • groupId: string
      • encryptedMemberId: string

      Returns Promise<void>

    • Remove a member from the group.

      Parameters

      Returns Promise<void>

    • Change a member's role (member ↔ admin).

      Parameters

      • groupId: string
      • encryptedMemberId: string
      • newRole: GroupRole

      Returns Promise<void>

    • Set the group's disappearing-messages timer in seconds (0 = off). Admin-gated by the group's modify_expiry policy role (default admin), so the caller must be an admin of the group.

      Parameters

      • groupId: string
      • expirySeconds: number

      Returns Promise<void>

    Methods - Groups

    • Create a new action-bound group on this homeserver.

      Parameters

      • title: string
      • description: string
      • expirySeconds: number

        Disappearing-messages timer. 0 disables it.

      Returns Promise<CreatedGroup>

      The new groupId (URL-safe-no-pad base64) and the 32-byte master key. Stash the master key — it's the secret an invite link carries, and there is no way to recover it from the server.

    • Pull the latest decrypted group state from the homeserver.

      Parameters

      • groupId: string

      Returns Promise<GroupSummary>

    • Last-known group info from the local cache, with no server round-trip (docs/53). Returns null if nothing is cached. Use this to show group info for a group you've left, where AppCore.fetchGroupState would fail the membership-gated server fetch.

      Parameters

      • groupId: string

      Returns Promise<GroupSummary | null>

    • Group ids of every group held locally — every group we have the master key for, including ones we were invited to (invites are auto-accepted). Reads the local group store directly, so it surfaces groups with no messages yet (unlike AppCore.loadConversations). Pair with AppCore.fetchGroupState to inspect membership and roles.

      Returns Promise<string[]>

    • Accept a pending invite. Generates our own Sender Key and broadcasts the distribution message to every other member.

      Parameters

      • groupId: string

      Returns Promise<void>

    • Decline a pending invite. Removes the local pending row.

      Parameters

      • groupId: string

      Returns Promise<void>

    • Join via an invite link.

      Parameters

      • masterKey: Uint8Array

        32-byte master key from the link.

      • hostingServerUrl: string

        Homeserver hosting the group.

      • password: Uint8Array

        Link password, or an empty Uint8Array if none.

      Returns Promise<JoinResult>

      "member" (open link, admitted) or "pending" (RequestToJoin link, awaiting admin approval).

    • Cancel a pending join request we issued via AppCore.joinViaLink.

      Parameters

      • groupId: string

      Returns Promise<void>

    • Leave a group yourself. Unlike AppCore.removeMember (an admin action), any member can leave. Removes your server-side membership and drops the group locally.

      Parameters

      • groupId: string

      Returns Promise<void>

    • Whether the current identity is still a member of the group, per the locally-cached state (docs/53). Returns false after leaving — the UI uses this to hide the composer while keeping the conversation readable.

      Parameters

      • groupId: string

      Returns Promise<boolean>

    • Apply any pending changes from /changes since the last applied revision.

      Parameters

      • groupId: string

      Returns Promise<number>

      The new local revision (equal to the previous one if nothing was pending).

    • Encrypt and send a message to the group. Uses our Sender Key for symmetric encryption, then fans out per-recipient via the existing DM transport.

      Parameters

      • groupId: string
      • body: string

        Text body. Sent verbatim — UTF-8 on the wire.

      Returns Promise<void>

    • Generate a fresh push-routing pseudonym for this group on the server. Caller should re-register with the relay using the returned bytes.

      Parameters

      • groupId: string

      Returns Promise<Uint8Array<ArrayBufferLike>>

      The new pseudonym bytes.

    Methods - Identity

    • This account's DID (did:plc:... or did:local:...).

      Returns string

    • This account's per-device id. Stable for the lifetime of the local store.

      Returns number

    Methods - Local History

    • Load all persisted messages for a conversation, oldest first.

      Parameters

      • conversationId: string

      Returns Promise<StoredMessage[]>

    • Most recent message for a conversation, or null if it has none.

      Parameters

      • conversationId: string

      Returns Promise<StoredMessage | null>

    • Mark every message in the conversation with sentAt ≤ upTo as read.

      Parameters

      • conversationId: string
      • upTo: Instant

      Returns Promise<number>

      Number of rows newly marked.

    • Count unread messages in a conversation.

      Parameters

      • conversationId: string

      Returns Promise<number>

    Methods - Profile

    • This user's own display name from the local profile cache. Empty string until a profile has been set.

      Returns Promise<string>

    • Update the user's display name. Re-encrypts and uploads the profile blob, then updates the local cache.

      Parameters

      • displayName: string

      Returns Promise<void>

    • Cached display name for a contact. Empty string if no profile has been fetched yet for this DID (caller should fall back to the DID).

      Parameters

      • did: string

      Returns Promise<string>

    • Re-fetch a contact's encrypted profile and update the local cache.

      Parameters

      • did: string

      Returns Promise<boolean>

      true if the cached display name changed.

    • Prime the contact-profile cache with metadata extracted from an invite token (so the auto-DM to the inviter shows their name from the first frame). Call right after AppCore.createAccount when an invite was accepted.

      Parameters

      • did: string
      • displayName: string
      • profileKey: Uint8Array

      Returns Promise<void>

    • Fetch the sender's encrypted profile blob using the profileKey from an inbound DecryptedMessage, decrypt it, and cache the display name locally. app-core does NOT do this automatically on receive (it would block on the network and persist a name most bots never need). Best-effort and idempotent. Blocks on the network.

      Parameters

      • did: string
      • profileKey: Uint8Array

      Returns Promise<void>

    Methods - Projects

    • Request a short-lived token for opening a Project's webview / API.

      Parameters

      • projectUrl: string

      Returns Promise<string>

    • Project login ("Sign in with Avalanche"), same-device front-end (docs/25). After the user consents in-app, mint an OAuth authorization code bound to this account and the PKCE challenge. Redirect the browser to redirectUri?code=<returned>&state=.... codeChallengeMethod is normally "S256".

      Parameters

      • clientId: string
      • redirectUri: string
      • codeChallenge: string
      • codeChallengeMethod: string
      • Optionalscope: string

      Returns Promise<string>

    • Project login, cross-device front-end (docs/25). After the user consents in-app to a login started on another device, approve the pending userCode. Returns the Project URL just signed in to.

      Parameters

      • userCode: string
      • clientId: string

      Returns Promise<string>