Skip to content

Documentation · design

Importers and connectors

Design document. The core of Archipelago depends on neither; it depends on the facts they produce.

Importer

interface Importer {
  id: string                         // 'niagara-station', 'jci-caf', ...
  formats: FormatSniffer[]           // magic bytes and structure, never extension alone
  import(bytes, options): ImportResult
}
ImportResult = { artifact, assertions[], bindingHints[], questions[], coverage, redactions[], warnings[] }

Pure: no sockets, no second file, no database, run in a worker with memory and time limits. Archive handling is hardened: caps on entry count, entry size, inflated size and ratio; entries read by name into memory and never written to disk; XML parsed by a reader that resolves no entities. The result reports coverage (typed, attached, unknown) so an import is honest about what it did not understand.

Connector

interface Connector {
  id: string
  describe(): { capabilities: { discover, read, subscribe, history, alarms, write: false }, configSchema, nativeRefSchema }
  open(config, context): Promise<Session>
}
interface Session {
  discover(scope?): AsyncIterable<DiscoveredNode>
  read(refs): Promise<Reading[]>
  subscribe(refs, opts?): AsyncIterable<Reading>
  history?(ref, range, cursor?): AsyncIterable<HistoryBatch>
  alarms?(cursor?): AsyncIterable<AlarmEvent>
  health(): Health
  close(): Promise<void>
}

Every reading carries its source, so the model records it as observed with the connector as provenance. Cursors make histories and alarms resumable across restarts. Backpressure is the connector's job. A connector runs in the edge process by default and can be moved to a child process over the same interface when isolation matters.

Write capability

A separate interface, WritableSession, with write(ref, value, priority, reason) and release. No early connector implements it. When it arrives: enabling it per connector is an admin action, each write needs a role that grants it, carries a reason, and is audited. Read-only by default is a type, not a setting.

Binding hints

A point carries zero or more bindings: a connector id and a native reference, with provenance. Importers emit binding hints when a file holds a native address. A station backup's BACnet proxy extensions hold device instance, object type and object instance for every proxied point, so the BACnet/IP connector can read those controllers directly, with no station software in the loop. A controller file holds the same object list. A register map is itself a set of bindings.

Planned implementations

See Integrations for every source with its status and what it cannot see.