SDK Reference

@encra/client

Framework-agnostic Encra client for Node.js, Svelte, Vue, vanilla JS — anywhere React hooks don't fit. Same features as @encra/react with an event-emitter API instead of hooks. Multi-device, persistent ratchet state, automatic reconnection.

💡 Using React?

Use @encra/react instead — it wraps EncraClient in idiomatic hooks. This package is for non-React environments.

Installation

bash
npm install @encra/client

Constructor

client.tstypescript
import { EncraClient } from '@encra/client'

const client = new EncraClient({
  apiKey:    process.env.ENCRA_API_KEY!,
  userId:    'alice',
  // serverUrl is optional — defaults to Encra managed server
})
PropTypeDefaultDescription
apiKey*stringYour Encra API key from the dashboard.
userId*stringUnique identifier for the current user. Used as the key registration ID.
serverUrlstringhttps://api.encra.devKey server base URL. Override for self-hosting.

connect / disconnect

connect() generates or restores the key pair from IndexedDB, registers the public key with the server, and opens the WebSocket connection. It resolves once the socket is open and the ready event fires.

typescript
await client.connect()

// Later, clean up
client.disconnect()
PropTypeDefaultDescription
connect()Promise<void>Initialise crypto, register key, and open WebSocket. Safe to call once.
disconnect()voidClose the WebSocket, cancel reconnects, and clear in-memory state. Does not delete IndexedDB data.
isReadybooleanTrue when the WebSocket is connected and ready to send.
isConnectingbooleanTrue while connecting or reconnecting.
deviceIdstring | nullStable device ID (UUID) generated once per browser/device. Available after connect() resolves.
messagesMessage[]All messages received in this session (max 200), newest last.
errorError | nullSet if key registration fails during connect().

Events

EncraClient is a typed event emitter. Subscribe with on() and unsubscribe with off(). Both return this for chaining.

typescript
client
  .on('ready',        ()    => console.log('connected, deviceId:', client.deviceId))
  .on('connecting',   ()    => console.log('reconnecting…'))
  .on('disconnected', ()    => console.log('lost connection'))
  .on('message',      (msg) => console.log(msg.from, ':', msg.text))
  .on('error',        (err) => console.error('error:', err.message))
  .on('wire',         (ev)  => console.log('wire', ev.direction, ev.ciphertext.slice(0, 12)))
PropTypeDefaultDescription
ready() => voidFired when the WebSocket connects and registration succeeds.
connecting() => voidFired when a reconnection attempt starts (after a disconnect).
disconnected() => voidFired when the WebSocket closes. Reconnection begins automatically.
message(msg: Message) => voidFired when an incoming message is successfully decrypted.
error(err: Error) => voidFired on decryption failures or WebSocket errors. Connection is not closed.
wire(event: WireEvent) => voidLow-level event fired for every sent/received ciphertext. Useful for debugging.

sendMessage

Encrypts text with a Double Ratchet step and sends it to all registered devices of to. Each device receives an independently encrypted copy.

typescript
// Wait for 'ready' before sending
client.on('ready', async () => {
  await client.sendMessage('bob', 'Hello Bob!')
})
PropTypeDefaultDescription
sendMessage(to, text)Promise<void>Encrypt and deliver text to all devices of the recipient. Throws if the WebSocket is not open.

encryptFile / decryptFile

Encrypt a File or Blob for a recipient. The result contains one encrypted copy per registered device. The recipient calls decryptFile — the correct device entry is picked automatically. Maximum file size is 50 MB.

typescript
// Sender
const encrypted = await client.encryptFile(file, 'bob')
// encrypted.name      — original filename
// encrypted.mimeType  — original MIME type
// encrypted.size      — original size in bytes
// encrypted.devices   — one entry per device of 'bob'

// Store / transmit encrypted (JSON-safe after converting Uint8Arrays to base64)

// Recipient
const file = await client.decryptFile(encrypted, 'alice')
// Returns a File with original name and type restored
PropTypeDefaultDescription
encryptFile(file, to)Promise<EncryptedFile>Encrypt a File or Blob for all registered devices of 'to'. Throws RangeError if file exceeds 50 MB.
decryptFile(encrypted, from)Promise<File>Decrypt an EncryptedFile from 'from'. Automatically finds this device's entry. Throws DecryptionFailedError if not found.

encryptFields / decryptFields

Encrypt a flat object of string values — form submissions, medical records, PII — for a recipient. Field names are plaintext; only values are encrypted. Each device of the recipient gets an independently encrypted copy with per-field unique nonces.

typescript
// Sender
const encrypted = await client.encryptFields(
  { name: 'Alice', ssn: '123-45-6789', notes: 'Confidential' },
  'doctor'
)
// encrypted.devices — one entry per device of 'doctor', each with per-field ciphertext

// Recipient (after receiving encrypted via your transport)
const fields = await client.decryptFields(encrypted, 'alice')
console.log(fields.ssn)    // '123-45-6789'
console.log(fields.notes)  // 'Confidential'
PropTypeDefaultDescription
encryptFields(fields, to)Promise<EncryptedFields>Encrypt a Record<string, string> for all devices of 'to'. Each field gets a unique random nonce.
decryptFields(encrypted, from)Promise<Record<string, string>>Decrypt an EncryptedFields object from 'from'. Returns the original key→plaintext mapping.

TypeScript types

typescript
import type {
  EncraClientOptions,
  Message,
  WireEvent,
  EncryptedFile,
  EncryptedFields,
  DeviceKey,
} from '@encra/client'

// Message shape
interface Message {
  from:      string  // sender's userId
  text:      string  // decrypted plaintext
  timestamp: number  // Date.now() at receipt
}

// WireEvent — low-level sent/received ciphertext
interface WireEvent {
  direction:  'sent' | 'received'
  ciphertext: string   // URL-safe base64
  nonce:      string   // URL-safe base64
  timestamp:  number
}

// Maximum file size
import { MAX_FILE_BYTES } from '@encra/client'
// MAX_FILE_BYTES === 50 * 1024 * 1024

Examples

Svelte

Chat.sveltesvelte
<script lang="ts">
  import { onMount, onDestroy } from 'svelte'
  import { EncraClient } from '@encra/client'
  import type { Message } from '@encra/client'

  export let userId: string
  export let recipientId: string

  let client: EncraClient
  let messages: Message[] = []
  let isReady = false
  let text = ''

  onMount(async () => {
    client = new EncraClient({
      apiKey: import.meta.env.VITE_ENCRA_API_KEY,
      userId,
    })
    client
      .on('ready',   ()    => { isReady = true })
      .on('message', (msg) => { messages = [...messages, msg] })

    await client.connect()
  })

  onDestroy(() => client?.disconnect())

  async function send() {
    if (!text.trim() || !isReady) return
    await client.sendMessage(recipientId, text)
    text = ''
  }
</script>

{#each messages as msg}
  <p><strong>{msg.from}</strong>: {msg.text}</p>
{/each}

<input bind:value={text} on:keydown={(e) => e.key === 'Enter' && send()} />
<button on:click={send} disabled={!isReady}>Send</button>

Node.js (backend-to-backend)

notify.tstypescript
import { EncraClient } from '@encra/client'

// Useful for server-side notifications: send encrypted alerts to a user
const bot = new EncraClient({
  apiKey:  process.env.ENCRA_API_KEY!,
  userId:  'system-bot',
})

bot.on('ready', async () => {
  await bot.sendMessage('alice', 'Your export is ready.')
  bot.disconnect()
})

await bot.connect()

Vue 3 (Composition API)

useEncra.tstypescript
import { ref, onMounted, onUnmounted } from 'vue'
import { EncraClient } from '@encra/client'
import type { Message } from '@encra/client'

export function useEncra(userId: string) {
  const client   = new EncraClient({ apiKey: import.meta.env.VITE_ENCRA_API_KEY, userId })
  const messages = ref<Message[]>([])
  const isReady  = ref(false)

  client
    .on('ready',   ()    => { isReady.value = true })
    .on('message', (msg) => { messages.value = [...messages.value, msg] })

  onMounted(() => client.connect())
  onUnmounted(() => client.disconnect())

  return { messages, isReady, sendMessage: client.sendMessage.bind(client) }
}

Encra AI

Ask me anything · docs, code, troubleshooting

Hi, I'm Encra AI

I can explain concepts, generate starter code, troubleshoot errors, and guide your setup.

May make mistakes · verify critical crypto details