---
url: https://corewire.dev/reference/core.md
---
# @corewire/core

`@corewire/core` has a main entry point and five subpaths. It knows no protocol.

## @corewire/core

```ts
import { AppleProtocolError, Logger, reporter } from '@corewire/core';
```

### Errors

Every class takes a `message: string`. The connection errors have a default message.

| Export | Extends |
| --- | --- |
| `AppleProtocolError` | `Error` |
| `ConnectionError` | `AppleProtocolError` |
| `ConnectionTimeoutError` | `ConnectionError` |
| `ConnectionClosedError` | `ConnectionError` |
| `PairingError` | `AppleProtocolError` |
| `AuthenticationError` | `PairingError` |
| `CredentialsError` | `PairingError` |
| `CommandError` | `AppleProtocolError` |
| `SetupError` | `AppleProtocolError` |
| `DiscoveryError` | `AppleProtocolError` |
| `EncryptionError` | `AppleProtocolError` |
| `InvalidResponseError` | `AppleProtocolError` |
| `TimeoutError` | `AppleProtocolError` |
| `PlaybackError` | `AppleProtocolError` |
| `DecryptionError` | `EncryptionError`; the authentication tag of a ChaCha20-Poly1305 frame did not match |
| `TLV8PairingError` | `PairingError`; `constructor(message: string, code?: number)`, `code: number \| undefined` holds the TLV8 error code of the accessory |

### Logging

| Export | Description |
| --- | --- |
| `class Logger` | `constructor(id: string, sink?: LogSink)`; `debug()`, `error()`, `info()`, `net()`, `raw()`, `warn()`; `traffic(protocol, direction, summary, decoded?, bytes?)`; `id`, `label` |
| `reporter: Reporter` | The global reporter |
| type `Reporter` | `all()`, `none()`, `enable(group)`, `disable(group)`, `isEnabled(group)`, `accepts(group)`, `printsToConsole(group)`, `setSink(sink, options?)`, `clearSink()`, `setTrafficSink(sink)`, `clearTrafficSink()`, `reportTraffic(entry)`, `report(group, deviceId, args)`, `tapsTraffic` |
| type `DebugGroup` | `'debug' \| 'error' \| 'info' \| 'net' \| 'raw' \| 'warn'` |
| type `LogSink` | `(group: DebugGroup, args: readonly unknown[]) => void` |
| type `ReporterEntry` | `{group: DebugGroup; deviceId: string \| null; args: readonly unknown[]; timestamp: number}` |
| type `ReporterSink` | `(entry: ReporterEntry) => void` |
| type `ReporterSinkOptions` | `{groups?: readonly DebugGroup[]; silenceConsole?: boolean}` |
| type `TrafficEntry` | `{deviceId: string \| null; protocol: TrafficProtocol; direction: TrafficDirection; summary: string; decoded?: unknown; bytes?: Uint8Array; timestamp: number}` |
| type `TrafficSink` | `(entry: TrafficEntry) => void` |
| type `TrafficDirection` | `'in' \| 'out'` |
| type `TrafficProtocol` | `string` |

### Helpers

| Export | Description |
| --- | --- |
| `waitFor(ms: number): Promise<void>` | |
| type `EventMap` | `Record<string, any>` |

## @corewire/core/crypto

The subpath exports namespaces and two types. Acronyms stay in capitals: `AES`, `ChaCha20`, `HKDF`.

| Export | Description |
| --- | --- |
| `ChaCha20` | `encrypt(key, nonce, aad, plaintext): EncryptedData`, `decrypt(key, nonce, aad, ciphertext, authTag): Buffer` (throws `DecryptionError` from the main entry), `padNonce(nonce)`, `CHACHA20_AUTH_TAG_LENGTH` (16), `CHACHA20_NONCE_LENGTH` (12), type `EncryptedData` (`{ciphertext; authTag}`) |
| `Ed25519` | `generateKeyPair(): KeyPair`, `sign(message, secretKey)`, `verify(message, signature, publicKey)` |
| `Curve25519` | `generateKeyPair(): KeyPair`, `generateSharedSecKey(priKey, pubKey)` |
| `AES` | `encrypt(key, iv, plaintext)`, `decrypt(key, iv, ciphertext)`; AES-128-CTR |
| `HKDF` | `derive(options: HKDFOptions): Buffer`; `deriveEncryptionKeys(sharedSecret: Buffer, salt: string, readInfo: string, writeInfo: string): {readKey: Buffer; writeKey: Buffer}` |
| type `HKDFOptions` | `{hash; key; length; salt; info}` |
| type `KeyPair` | `{publicKey: Uint8Array; secretKey: Uint8Array}` |

## @corewire/core/discovery

| Export | Description |
| --- | --- |
| `class Discovery` | `constructor(service: string)`; `find(useCache?)`, `findUntil(id, tries?, timeout?)`, `findByAddress(address, timeout?, wakePorts?)`; static `findServices(services, useCache?)`, `clearCache()`, `wake(address, ports)` |
| `mdnsMulticast(services: string[], timeout = 4): Promise<MdnsService[]>` | Timeout in seconds |
| `mdnsUnicast(hosts: string[], services: string[], timeout = 4, wakePorts = []): Promise<MdnsService[]>` | |
| `class MdnsResponder` | `constructor(logger: Logger, service: AdvertisedService, address?: string)`; `start()`, `stop()`, static `localAddress()` |
| type `DiscoveryResult` | `{id: string; fqdn: string; address: string; txt: Readonly<Record<string, string>>; service: {port: number; protocol: 'tcp' \| 'udp'; type: string}}`. The model identifier sits in `txt.model` or `txt.am` |
| type `DiscoverOptions` | `{useCache?: boolean}`; reuses a scan younger than 30 seconds, default true. The one definition the protocol packages and devices share |
| type `MdnsService` | `{name; type; address; port; properties: Record<string, string>}` |
| type `AdvertisedService` | `{instance; type; host; port; txt}` |

## @corewire/core/encoding

| Export | Description |
| --- | --- |
| `TLV8` | `encode(entries)`, `decode(buffer): Map<number, Buffer>` (one value per type, the first run wins), `decodeEntries(buffer): [number, Buffer][]` (every entry in order, adjacent fragments joined), `bail(data)` (throws `TLV8PairingError` from the main entry), `Value`, `State`, `Method`, `Flags`, `ErrorCode` |
| `OPack` | `encode(data)`, `decode(data)`, `int(value: number \| bigint)`, `float(value)`, `sizedInteger(value: number \| bigint, size)` with a size of 1, 2, 4, 8 or 16 bytes. Integers are signed; a sized integer outside the safe number range holds a `bigint`. Dates encode and decode as `Date`. `encode` throws a `TypeError` beyond 32 nested containers and `decode` throws a `TypeError` on a truncated buffer, a bad reference, deeper nesting or an unsupported tag |
| `Plist` | `serialize(value)`, `parse(buffer)`, types `PlistValue`, `PlistDictionary` |
| `DAAP` | `encodeTag()`, `encodeContainer()`, `encodeTagWithSize()`, `encodePlaybackStatus()`, `encodeTrackMetadata()`, `decode()`, `decodeTag()`, `decodeToObject()`, `decodeTrackMetadata()`, `ContentCode`, `TagType` and their types |
| `NTP` | `now(): bigint`, `parts(ntp: bigint): [number, number]`, `encode(fields)`, `decode(buffer)`, type `PacketFields` |
| `NSKeyedArchiver` | `decode(archive)`, `decodeAsArray(archive)` |
| `RTI` | `decodeSession(data)`, `encodeOperation(sessionUUID, text, clear)`, type `TextInputSession` |

## @corewire/core/pairing

| Export | Description |
| --- | --- |
| `class PairSetup` | `constructor(context: Context, transport: PairingTransport, options?: PairingOptions)`; `start()`, `pin(askPin): Promise<AccessoryCredentials>`, `transient(): Promise<AccessoryKeys>`, `m1()` to `m6()` |
| `class PairVerify` | `constructor(context, transport, options?)`; `start(credentials: AccessoryCredentials): Promise<AccessoryKeys>` |
| `class PairSetupServer` | `constructor(context, identity: AccessoryIdentity, pin: string, options?)`; `handle(request: Buffer): Promise<Buffer>`, `controller`, `sharedSecret` |
| `class PairVerifyServer` | `constructor(context, identity, resolveController: ControllerResolver, options?)`; `handle(request)`, `pairingId`, `sharedSecret` |
| `generateAccessoryIdentity(): AccessoryIdentity` | A random identifier and a new Ed25519 key pair |
| `deriveControlKeys(sharedSecret: Buffer): {accessoryToControllerKey: Buffer; controllerToAccessoryKey: Buffer}` | |
| `TRANSIENT_PAIRING_PIN` | `'3939'` |
| type `AccessoryCredentials` | `{accessoryIdentifier: string; accessoryLongTermPublicKey: Buffer; pairingId: Buffer; publicKey: Buffer; secretKey: Buffer}` |
| type `AccessoryKeys` | `{pairingId; sharedSecret; accessoryToControllerKey; controllerToAccessoryKey}`, all `Buffer` |
| type `AccessoryIdentity` | `{identifier: string; publicKey: Buffer; secretKey: Buffer}` |
| type `PairedController` | `{identifier: string; longTermPublicKey: Buffer}` |
| type `ControllerResolver` | `(pairingId: string) => Buffer \| undefined` |
| type `PairingOptions` | `{useAes?: boolean}` |
| type `PairingStep` | `'m1' \| 'm3' \| 'm5'` |
| type `PairingTransport` | `(payload: Buffer, step: PairingStep) => Promise<Buffer>` |

## @corewire/core/transport

| Export | Description |
| --- | --- |
| `class Context` | `constructor(deviceId: string, identity: DeviceIdentity = {}, sink?: LogSink)`; `deviceId`, `identity` (only the overrides you passed), `logger` |
| `DEFAULT_CLIENT_NAME` | `'CoreWire'`: the name a client presents when `identity.name` is not set |
| `class Connection<TEventMap>` | `constructor(context, address, port)`; `connect()`, `disconnect()`, `destroy(error?)`, `write(data)`, `debug(enabled)`; `address`, `port`, `localAddress`, `isConnected`, `state`, `signal`, `context`; events `connect`, `data`, `error`, `closed` |
| `class EncryptionAwareConnection<TEventMap>` | `Connection` plus `enableEncryption(readKey, writeKey)`, `isEncrypted`, and the protected getter `encryption: EncryptionState \| undefined` for subclasses |
| `class EncryptionState` | `constructor(readKey, writeKey)`; `readKey`, `readCount`, `writeKey`, `writeCount` |
| `buildNonce(counter: number, format: NonceFormat): Buffer` | A 12-byte nonce |
| type `NonceFormat` | `'counter-at-0' \| 'counter-at-4'` |
| type `DeviceIdentity` | `{name?; model?; osName?; osVersion?; osBuildVersion?; sourceVersion?; applicationBundleIdentifier?; applicationBundleVersion?}`, all strings. A field you leave out, or set to `undefined`, keeps the default of the protocol package |
| type `CloseReason` | `'local' \| 'remote' \| 'timeout' \| 'error'` |
| type `ConnectionState` | `'closing' \| 'connected' \| 'connecting' \| 'disconnected' \| 'failed'` |
