Integration

Producer Guide

Implementing the emitter role

Audience: Integrators, application engineers. Use: Implement emitter-side append, retry, and artifact publication behavior correctly.

Producer Role

A producer is any system that emits facts into BSFG by calling AppendFact (and optionally PutObject for large artifacts).

Producers are responsible for:

Required Call Sequence

The canonical producer sequence is:

1. [OPTIONAL] PutObject(bucket, key, blob)
   ↓
   [Wait for confirmation: {digest, size}]
   ↓
2. AppendFact({
     envelope: {message_id, from_zone, to_zone, produced_at_unix_ms, ...},
     fact: {subject, predicate, object_json}
   })
   ↓
   [Wait for confirmation: {offset}]
   ↓
3. On error: retry AppendFact with SAME message_id and SAME payload

Step 1: Upload Artifact (if needed)

If the fact references a large artifact, upload it first:

PutObject(
  bucket: "batch-files",
  key: "order-2026-03-06-001.pdf",
  blob: <binary data>
) → {digest: "sha256:...", size: 2048576}

Wait for the operation to complete and durability to be acknowledged. Do not proceed to AppendFact until the artifact is durably stored.

Step 2: Append Fact

Create and append the fact with the message ID and optional artifact reference:

AppendFact({
  envelope: {
    message_id: "<stable-id>",
    from_zone: "Plant A",
    to_zone: "Enterprise",
    produced_at_unix_ms: 1741248600000,
    correlation_id: "order:12345",
    labels: {"priority": "high"}
  },
  fact: {
    subject: "work_order:WO-2026-001",
    predicate: "has_batch_attachment",
    object_json: {
      "bucket": "batch-files",
      "key": "order-2026-03-06-001.pdf",
      "digest": "sha256:...",
      "size": 2048576,
      "media_type": "application/pdf",
      "file_name": "batch_order.pdf"
    }
  }
})

→ Confirmation: {offset: 42}

Step 3: Retry on Failure

If AppendFact times out or fails:

if (error during AppendFact) {
  // Retry with SAME message_id and SAME payload
  retry_count = 0
  max_retries = 3
  backoff = exponential(base=100ms)

  while (retry_count < max_retries) {
    try {
      result = AppendFact(same_message_id, same_payload)
      // Success — break
      break
    } catch (error) {
      retry_count++
      if (retry_count == max_retries) {
        throw error  // Give up after max retries
      }
      wait(backoff)
      backoff *= 2
    }
  }
}

Generating Stable Message IDs

The message_id must be deterministically derived from the business event. It is the idempotency key that prevents duplicates.

Good: Deterministic Derivation

Bad: Non-Deterministic IDs

Retry Safety

Retrying with the same message_id and payload is safe because:

  1. The forward buffer (IFB/EFB) uses putIfAbsent(message_id, payload)
  2. If the ID already exists, the insertion is rejected
  3. The boundary returns the same confirmation (offset) for repeated attempts

Example:

Attempt 1: AppendFact(message_id="X", payload="P") → offset: 100
Attempt 2: Network timeout, retry
Attempt 2: AppendFact(message_id="X", payload="P") → offset: 100 (same)
Attempt 3: AppendFact(message_id="X", payload="P") → offset: 100 (same)

Result: ONE fact at offset 100, not three.

Artifact Obligations

If a fact references an artifact:

Before Appending the Fact

After Appending the Fact

Artifact Existence Guarantee

Appending a fact with an artifact reference is a producer guarantee that the artifact exists and is accessible. If the consumer later tries to retrieve the artifact and it is missing, that is a producer defect.

What AppendFact Confirms

AppendFact confirmation means:

It does NOT mean:

Handling Producer Errors

Network Timeouts

If AppendFact times out (no response from boundary), retry with the same message ID and payload.

Application Crashes

If the producer crashes after AppendFact succeeds but before the application can record success:

Artifact Upload Failure

If PutObject fails:

Performance Characteristics

Under normal operation with a healthy boundary:

Producers should not wait for consumer processing. Consumer latency is decoupled and may be much longer (hours or days in autonomous mode).