openexit
v0.1.0
Published
OpenExit Protocol toolkit for portable application state
Readme
OpenExit
OpenExit is a developer toolkit for moving application state between software systems. A vendor's source adapter maps proprietary data into canonical records. OpenExit writes a portable bundle; a destination adapter maps those records into another application's state.
This package implements OpenExit Protocol version 1.0. The public protocol name can evolve independently of the stable openexit.bundle wire-format identifier.
Install and run
Requires Node.js 20 or newer. There are no runtime dependencies.
npm install -g openexit
openexit --version
openexit --helpFrom this repository, the included adapter demonstrates export and import:
openexit export --adapter ./examples/adapter.js --dir ./bundle --chunk-size 2
openexit inspect --dir ./bundle
openexit import --adapter ./examples/adapter.js --dir ./bundle --batch-size 1During development, node src/cli.js works in place of openexit. The example reads examples/customers.ndjson and writes account-shaped records to examples/imported/. Set OPENEXIT_INPUT and OPENEXIT_OUTPUT to use other paths. After an interrupted export, repeat the export command with --resume. Import resumes automatically.
Adapter contract
An adapter module exports source, target, or both. A source provides:
export const source = {
describe() {
return { name: 'my-app', schemaVersion: '1', snapshot: 'stable-snapshot-id' };
},
async readPage({ cursor, limit }) {
// Return at most limit canonical records.
return { records: [], nextCursor: cursor, done: true };
}
};describe() identifies the source schema and snapshot. A cursor identifies the first unread record. The source must keep record order and contents stable during export and resume. OpenExit commits the next cursor only after writing the corresponding chunk and manifest.
A target provides:
export const target = {
describe() { return { name: 'my-destination', instance: 'tenant-123' }; },
async writeBatch(records, { bundleId, chunkIndex, batchIndex }) {
// Apply records to the destination.
}
};describe() identifies the exact destination for its checkpoint. writeBatch must be idempotent. A stopped import can replay its last chunk. Upsert by (type, id), or deduplicate with the supplied bundle, chunk, and batch identity. The included example uses deterministic output filenames.
Canonical records
Each record requires type, id, and state:
{"type":"customer","id":"c-1","state":{"name":"Ada"},"links":{"team":["t-1"]},"meta":{"sourceVersion":"1"}}state, optional links, and optional meta contain JSON values. Adapters own mappings between their proprietary schemas and this interface. The JavaScript API exports exportBundle, importBundle, inspectBundle, and validators. Export and import accept an async transform(record, context) callback; returning null drops a record.
Bundle format
A bundle directory contains manifest.json and numbered NDJSON chunk files. The manifest records the OpenExit Protocol version, source snapshot, chunk order, record counts, SHA-256 digests, resume cursor, and completion state. Import checks each chunk's digest before applying it, validates records as it reads them, and checkpoints after each chunk. See PROTOCOL.md for the wire format.
Current limitations
- Export holds one page in memory and import holds one batch. Very large individual records or chunks can still use substantial memory.
- Bundles use local files and NDJSON. Remote storage, compression, encryption, and other bundle encodings are not implemented.
- Source adapters must provide stable snapshots. Target adapters must make writes idempotent. There is no distributed transaction across applications.
- One process should write a bundle or target checkpoint at a time; multi-process locking is not implemented.
- The toolkit is JavaScript today. The documented bundle format is intended for other languages, but other language SDKs are not included.
- OpenExit Protocol version 1.0 validates the canonical envelope and major wire version; it does not provide a domain-schema migration registry.
Licensed under MIT. The example adapter and sample input are included in the npm package.
