tx

tx sync

Git-backed synchronization via append-only stream event logs

Purpose

tx sync keeps local state portable across machines by writing append-only event logs under .tx/streams/ and replaying them into SQLite.

This gives you:

  • Git-friendly sync artifacts
  • Cross-machine collaboration
  • Deterministic rebuilds from event history
  • Local-first operation (SQLite remains the runtime store)

At A Glance

Cross-Machine Sync

Rendering diagram…
Export writes the local event log. Git moves the log. Import materializes unseen events on the other machine.

Rebuild Path

Rendering diagram…
Hydrate replays stream logs into SQLite. Status tells you whether the materialized state is healthy and dirty.

Sync Model (Default)

tx sync uses a stream event log model:

.tx/
├── stream.json                              # Local stream identity
└── streams/
    └── <stream-id>/
        └── events-YYYY-MM-DD.jsonl          # Append-only events

Each event is a JSON object with envelope metadata and payload:

{
  "event_id": "01JZ...",
  "stream_id": "01JY...",
  "seq": 184,
  "ts": "2026-03-05T13:12:04.123Z",
  "type": "task.upsert",
  "entity_id": "tx-abc123",
  "v": 2,
  "payload": { "id": "tx-abc123", "title": "Implement auth" }
}

Subcommands

CommandDescription
tx sync exportEmit current state as stream events
tx sync importIncrementally apply stream events
tx sync streamShow local stream ID + sequence state
tx sync hydrateRebuild materialized state from event logs
tx sync statusShow sync health and dirty status
tx sync autoEnable/disable automatic export on mutations

tx sync export

Write events to the local stream log.

tx sync export [--json]
# Export events
tx sync export

# JSON output
tx sync export --json

CLI output (human mode):

Events: 42 event(s) → .tx/streams/<stream-id>/events-2026-03-05.jsonl
import { TxClient } from '@jamesaphoenix/tx-agent-sdk'

const tx = new TxClient({ dbPath: '.tx/tasks.db' })

const result = await tx.sync.export()
console.log(result.eventCount, result.streamId, result.path)

Tool: tx_sync_export

{
  "name": "tx_sync_export",
  "arguments": {}
}
POST /api/sync/export
curl -X POST http://localhost:3456/api/sync/export \
  -H "Content-Type: application/json" \
  -d '{}'

Response:

{
  "eventCount": 42,
  "streamId": "01JY...",
  "path": ".tx/streams/01JY.../events-2026-03-05.jsonl"
}

tx sync import

Read stream event logs and apply unseen events.

tx sync import [--json]
tx sync import
tx sync import --json

CLI output (human mode):

Events: imported=42, applied=42, streams=1
const result = await tx.sync.import()
console.log(result.importedEvents, result.appliedEvents, result.streamCount)

Tool: tx_sync_import

{
  "name": "tx_sync_import",
  "arguments": {}
}
POST /api/sync/import
curl -X POST http://localhost:3456/api/sync/import \
  -H "Content-Type: application/json" \
  -d '{}'

Response:

{
  "importedEvents": 42,
  "appliedEvents": 42,
  "streamCount": 1
}

tx sync stream

Inspect local stream identity and sequence progress.

tx sync stream [--json]
Stream: 01JY...
  nextSeq: 185
  lastSeq: 184
  eventsDir: /.../.tx/streams/01JY...
const stream = await tx.sync.stream()
console.log(stream.streamId, stream.nextSeq, stream.lastSeq, stream.eventsDir)

Tool: tx_sync_stream

{
  "name": "tx_sync_stream",
  "arguments": {}
}
GET /api/sync/stream
curl http://localhost:3456/api/sync/stream

tx sync hydrate

Clear and rebuild materialized task state by replaying event logs.

tx sync hydrate [--json]
const hydrated = await tx.sync.hydrate()
console.log(hydrated.importedEvents, hydrated.appliedEvents, hydrated.rebuilt)

Tool: tx_sync_hydrate

{
  "name": "tx_sync_hydrate",
  "arguments": {}
}
POST /api/sync/hydrate
curl -X POST http://localhost:3456/api/sync/hydrate

tx sync status

Show high-level sync state.

tx sync status [--json]
Sync Status:
  Tasks in database: 156
  Events in stream logs: 312
  Last export: 2026-03-05T12:40:33.000Z
  Last import: 2026-03-05T12:41:18.000Z
  Dirty (unexported changes): no
  Auto-sync: enabled
const status = await tx.sync.status()
console.log(status.dbTaskCount, status.isDirty, status.autoSyncEnabled)

Tool: tx_sync_status

{
  "name": "tx_sync_status",
  "arguments": {}
}
GET /api/sync/status
curl http://localhost:3456/api/sync/status

tx sync auto

Enable or disable automatic export on mutations.

tx sync auto --enable
tx sync auto --disable
tx sync auto --json

When enabled, mutating operations trigger sync export automatically.

Sync Constraints

  • Legacy single-file sync arguments are no longer supported.

Git Workflow

# Before commit
tx sync export
git add .tx/stream.json .tx/streams

git commit -m "chore: sync stream events"
git push

# On another machine
git pull
tx sync import

.gitignore Recommendation

# Keep database local
.tx/tasks.db
.tx/tasks.db-wal
.tx/tasks.db-shm

# Track sync stream identity + event logs
!.tx/stream.json
!.tx/streams/
!.tx/streams/**
  • tx ready - Pull the next workable task after import
  • tx done - Mark work complete (can trigger auto-sync)
  • tx pin - Sync context files alongside task/event sync

On this page