Concept

Message Model: Envelope + Fact

Structure and semantics of BSFG messages

Audience: Architects, integrators, schema designers. Use: Understand the envelope-plus-fact message structure and field semantics.

Structure: Envelope + Fact

A BSFG message consists of two parts: an envelope and a fact.

message = {
  envelope: {
    message_id
    from_zone
    to_zone
    produced_at_unix_ms
    correlation_id
    causation_id
    labels
    object_media_type       (if artifact reference)
    object_schema           (if artifact reference)
  },
  fact: {
    subject
    predicate
    object_json
  }
}
    

The envelope carries system-level metadata: transport semantics, correlation, and artifact references. The fact carries domain-level information: the semantic content that applications care about.

Envelope Fields

The envelope is required on every message. It provides the transport and traceability context.

Field Type Purpose Required?
message_id string (UUID or stable hash) Idempotency key for deduplication. Must be deterministically derived from the business event. Yes
from_zone string (zone name) Source zone. Validated by BSFG peers against authenticated mTLS certificate. Yes
to_zone string (zone name) Destination zone. Declarative, not a routing directive. Yes
produced_at_unix_ms integer (unix milliseconds) Timestamp when the event was produced. Producer clock, not system clock. Yes
correlation_id string (optional) Links related messages across zones. Application-defined semantics. No
causation_id string (optional) Links a message to the event that caused it. Preserves causal ordering. No
labels map of string → string Operational and routing metadata. Examples: priority=high, silo=A. No
object_media_type string (MIME type) Media type of the referenced artifact (if any). Examples: application/pdf, image/png. If artifact
object_schema string (optional schema reference) Optional schema identifier for the artifact or object_json. No

Fact Structure

The fact is the domain-level semantic unit. It represents a single, immutable event.

fact = {
  subject: "kind:id" | "kind:scope/id",
  predicate: "lower_snake_case_relation",
  object_json: "<canonical JSON bytes>"
}
    

Subject

The subject identifies what the fact is about. Two formats:

The kind determines the subject domain (manufacturing, logistics, etc.). The scope is optional and contextual (production line, plant, shift).

Predicate

The predicate is a relation name in lower_snake_case. It describes the relationship between the subject and the object. Examples:

Predicate names must be stable across versions. Renaming a predicate is a breaking change.

Object (Payload)

The object is the data associated with the subject-predicate pair. It is serialized as canonical JSON.

Canonical JSON means:

Why canonical JSON? The idempotency key for the message may be derived from a hash of the object. Canonical form ensures deterministic hashing.

Example:

{
  "status":"completed",
  "completed_at":"2026-03-06T14:30:00Z",
  "duration_ms":1250,
  "result_code":0
}
    

Idempotency Key Derivation

The message_id in the envelope is the idempotency key for the boundary. It must be deterministically derived from the business event. Three strategies:

The idempotency key strategy is a zone-level configuration decision, not a per-message choice.

System-Level vs Domain-Level

Envelope = System Level. The envelope is owned and interpreted by BSFG. It controls transport, deduplication, routing, and traceability. Applications do not create envelope fields.

Fact = Domain Level. The fact is owned and interpreted by applications. BSFG treats the fact body as opaque bytes. Applications define subject, predicate, and object semantics.

This boundary ensures that BSFG remains a generic boundary transport, not a semantic gateway.

Immutability

Once a message is appended via AppendFact, it is immutable. Facts are append-only; corrections are new facts. The same envelope and fact cannot be modified or deleted.