← Developers

01.2 — CDC outbox & sync channel

Spec matching SPEC-DX-01 / sync v1

The Invariant

A committed business write must never be lost between the local commit and cloud delivery. The kernel writes the business row, the audit row and the outbox entry in the same transaction — if the row exists, the event will eventually reach the cloud. The sync channel is asynchronous by design: edge autonomy first, cloud convergence eventually.

The Contract

interface OutboxEntry {          // ax_cdc_outbox (edge-side)
  id: string;                    // uuid — becomes the global dedup key
  aggregate_type: string;        // BIZ_*
  aggregate_id: string;
  event_type: string;            // INSERT_* | UPDATE_*
  payload: object;
  created_at: string;            // UTC
}
// Cloud side: cp_events stores it verbatim; cp_event_dedup(id) is the
// exactly-once frontier — processed ids are never re-processed.

Channel limits (hard contract): ingest batches ≤ 1000 events (413 BATCH_TOO_LARGE), edge-audit NDJSON ≤ 5000 lines. 503 code: DRAINING means the cloud is shutting down — back off, do not alert.

The Flow

txn: biz row + audit + outbox ──commit──► daemon polls ax_cdc_outbox
                                              │  batch (≤1000), ordered by seq
                                              ▼
                              POST /sync/ingest  (Bearer edge api_key)
                                              │  dedup on id
                                              ▼
                                          cp_events ──► projections on read
                                              │
                              ack → edge marks entries delivered
        cloud down? entries stay queued, edge keeps producing — offline-first

Every authenticated sync call also touches last_poll_at — the fleet heartbeat is a free side-effect of delivery.

Payload Example

# The daemon's drain — exactly what it sends, nothing more
curl -X POST https://cp.axiom-trace.eu/api/v1/sync/ingest \
  -H "Authorization: Bearer $EDGE_API_KEY" -H 'content-type: application/json' \
  -d '{"events":[{"id":"…","aggregate_type":"BIZ_CYCLES",
        "aggregate_id":"…","event_type":"INSERT_BIZ_CYCLES",
        "payload":{"cycle_seq":1042,"duration_ms":812},
        "created_at":"2026-09-26T14:01:03Z"}]}'

Known Failures

Symptom Cause → fix
401 on ingest API key rotated at re-claim; old key invalid by design → re-provision
403 EDGE_CAP_EXCEEDED Tenant over licensed edge count → admin must raise max_edges
413 BATCH_TOO_LARGE >1000 events → split; never retry the same oversized batch
429 Per-tenant ingest bucket → honor Retry-After, jittered backoff
503 DRAINING Cloud restart → retry later; alerting on this is a false positive
Events stuck in outbox Check daemon logs and network — the rows are safe, delivery is deferred