KAFKA · ASYNCAPI 3.1 · AT-LEAST-ONCE

События и правила consumer

AsyncAPI описывает фактически публикуемые event families и отдельно помеченные schema-only candidates. Local broker использует plaintext; production TLS, SASL, ACL, quotas и DR не заявлены.

Граница выполнения
LOCAL Kafka · plaintext development broker
Canonical-derived AsyncAPI event catalog
РЕАЛИЗОВАНО
Контракты и исходные файлы

OpenAPI, схемы и примеры для скачивания.

Каталог событий

Фильтруемый reference генерируется из canonical AsyncAPI и связанных JSON Schemas. Каждая карточка отделяет объявленный контракт от runtime evidence и сохраняет обе версии события, если они делят один topic.

GENERATED · CANONICAL-DERIVED

AsyncAPI event catalog

6 event versions across 5 Kafka topics. Producer and consumer labels describe only operations declared in canonical AsyncAPI.

Events with producer operation
4
Kafka not produced
2
Events with consumer operation
0
Source SHA-256
99597d6bb4b0

Фильтр работает локально по generated catalog; запрос не отправляется в сеть.

Показано событий: 6 из 6.

Event name

certarail.eligibility.decision.v1

Eligibility decision event v1

SEND OPERATION DECLARED

Immutable eligibility decision and audit receipt.

Canonical AsyncAPI declares a send operation. This documents the producer contract; it does not by itself prove a live or production publisher.

Channel / topic
certarail.decision.v1channel: eligibilityDecisions
Version
v1AsyncAPI document 1.3.1 · transport header 1

Producer

Send operation emitEligibilityDecision is declared in canonical AsyncAPI. This is a contract declaration, not runtime evidence.

  • emitEligibilityDecisionPublish committed eligibility decision evidence.

Consumer

No named consumer operation is declared in canonical AsyncAPI; the catalog does not infer one.

Payload schema

CertaRail eligibility decision event v1

urn:certarail:schema:event:certarail.eligibility.decision.v1

Raw JSON Schema
Top-level payload fields; nested constraints remain available in raw JSON Schema.
FieldRequirementType / constraintsDescription
event_idrequiredstringpattern ^evt_ · minLength 5No field description declared.
event_typerequiredstringconst "certarail.eligibility.decision.v1"No field description declared.
occurred_atrequiredstringformat date-timeNo field description declared.
tenant_idrequiredstringminLength 1No field description declared.
resultrequiredobject#/$defs/evaluationResultNo field description declared.
Full payload schema
urn:certarail:schema:event:certarail.eligibility.decision.v1json-schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:certarail:schema:event:certarail.eligibility.decision.v1",
  "title": "CertaRail eligibility decision event v1",
  "description": "Immutable event envelope written to the PostgreSQL outbox in the same transaction as the decision and audit receipt.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "event_id",
    "event_type",
    "occurred_at",
    "tenant_id",
    "result"
  ],
  "properties": {
    "event_id": {
      "type": "string",
      "pattern": "^evt_",
      "minLength": 5
    },
    "event_type": {
      "const": "certarail.eligibility.decision.v1"
    },
    "occurred_at": {
      "type": "string",
      "format": "date-time"
    },
    "tenant_id": {
      "type": "string",
      "minLength": 1
    },
    "result": {
      "$ref": "#/$defs/evaluationResult"
    }
  },
  "$defs": {
    "evaluationResult": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "decision",
        "audit"
      ],
      "properties": {
        "decision": {
          "$ref": "#/$defs/decision"
        },
        "audit": {
          "$ref": "#/$defs/auditReceipt"
        }
      }
    },
    "decision": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "decision_id",
        "request_id",
        "tenant_id",
        "outcome",
        "enforceable",
        "evaluated_at",
        "operation",
        "asset_code",
        "input_digest",
        "checks"
      ],
      "properties": {
        "decision_id": {
          "type": "string",
          "minLength": 1
        },
        "request_id": {
          "type": "string",
          "minLength": 1
        },
        "tenant_id": {
          "type": "string",
          "minLength": 1
        },
        "outcome": {
          "enum": [
            "ALLOW",
            "DENY",
            "REVIEW"
          ]
        },
        "enforceable": {
          "type": "boolean"
        },
        "evaluated_at": {
          "type": "string",
          "format": "date-time"
        },
        "evaluator_version": {
          "const": "certarail.eligibility.go.v1",
          "description": "Immutable rule-engine semantics identity. Optional in v1 only for pre-migration committed replays; every new decision includes it."
        },
        "operation": {
          "enum": [
            "BUY",
            "SELL",
            "DEPOSIT",
            "WITHDRAW",
            "EXCHANGE"
          ]
        },
        "asset_code": {
          "type": "string",
          "minLength": 1
        },
        "policy_id": {
          "type": "string",
          "minLength": 1
        },
        "policy_version": {
          "type": "string",
          "minLength": 1
        },
        "policy_digest": {
          "$ref": "#/$defs/digest"
        },
        "policy_mode": {
          "enum": [
            "SANDBOX",
            "PRODUCTION"
          ]
        },
        "input_digest": {
          "$ref": "#/$defs/digest"
        },
        "checks": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/check"
          }
        },
        "obligations": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "check": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "status",
        "summary"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "status": {
          "enum": [
            "PASS",
            "FAIL",
            "REVIEW"
          ]
        },
        "summary": {
          "type": "string"
        },
        "rule_id": {
          "type": "string",
          "minLength": 1
        },
        "source_refs": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "auditReceipt": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "lane",
        "sequence",
        "recorded_at",
        "event_hash"
      ],
      "properties": {
        "lane": {
          "type": "integer",
          "minimum": 0,
          "maximum": 31
        },
        "sequence": {
          "type": "integer",
          "minimum": 1
        },
        "recorded_at": {
          "type": "string",
          "format": "date-time"
        },
        "evaluator_version": {
          "const": "certarail.eligibility.go.v1",
          "description": "Evaluator identity bound into the audit event hash. Optional in v1 only for pre-migration committed replays; every new audit event includes it."
        },
        "previous_hash": {
          "$ref": "#/$defs/digest"
        },
        "event_hash": {
          "$ref": "#/$defs/digest"
        }
      }
    },
    "digest": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$"
    }
  }
}

Example

Schema-valid synthetic payload

Generated from the canonical JSON Schema for documentation. It is not a broker record, provider receipt, or runtime evidence.

certarail.eligibility.decision.v1 examplejson
{
  "event_id": "evt_x",
  "event_type": "certarail.eligibility.decision.v1",
  "occurred_at": "2026-01-01T00:00:00Z",
  "tenant_id": "tenant_example",
  "result": {
    "decision": {
      "decision_id": "decision_example",
      "request_id": "request_example",
      "tenant_id": "tenant_example",
      "outcome": "ALLOW",
      "enforceable": false,
      "evaluated_at": "2026-01-01T00:00:00Z",
      "evaluator_version": "certarail.eligibility.go.v1",
      "operation": "BUY",
      "asset_code": "BTC",
      "input_digest": "0000000000000000000000000000000000000000000000000000000000000000",
      "checks": [
        {
          "id": "example",
          "status": "PASS",
          "summary": "example"
        }
      ]
    },
    "audit": {
      "lane": 0,
      "sequence": 1,
      "recorded_at": "2026-01-01T00:00:00Z",
      "evaluator_version": "certarail.eligibility.go.v1",
      "event_hash": "0000000000000000000000000000000000000000000000000000000000000000"
    }
  }
}

Correlation fields

Identifiers and trace context

Identifier, reference, trace, and Kafka-key fields are listed from the canonical schemas and message bindings.

Correlation candidates derived from payload, headers, and Kafka binding.
LocationFieldRequirementMeaning
payloadpayload.event_idrequiredIdentifier declared without a field description.
payloadpayload.tenant_idrequiredIdentifier declared without a field description.
payloadpayload.result.decision.decision_idrequiredIdentifier declared without a field description.
payloadpayload.result.decision.request_idrequiredIdentifier declared without a field description.
payloadpayload.result.decision.tenant_idrequiredIdentifier declared without a field description.
payloadpayload.result.decision.policy_idoptionalIdentifier declared without a field description.
payloadpayload.result.decision.checks.[].idrequiredIdentifier declared without a field description.
payloadpayload.result.decision.checks.[].rule_idoptionalIdentifier declared without a field description.
headerheaders.certarail-event-idrequiredStable consumer deduplication identifier; equal to payload event_id.
headerheaders.traceparentoptionalOptional W3C Trace Context for the Kafka publisher attempt.
headerheaders.tracestateoptionalOptional W3C Trace Context tracestate value; omitted when empty.
kafka-keymessage.keydeclaredPseudonymous ordering key computed as `subject_` plus the first 16 bytes of SHA-256(tenant_id || NUL || subject_ref), encoded as lowercase hexadecimal. It is not an authentication or tenancy boundary.

Ordering semantics

declared

Ordering is guaranteed only for one Kafka key within certarail.decision.v1.

Scope: one Kafka key within certarail.decision.v1

Pseudonymous ordering key computed as `subject_` plus the first 16 bytes of SHA-256(tenant_id || NUL || subject_ref), encoded as lowercase hexadecimal. It is not an authentication or tenancy boundary.

Retry semantics

declared

at-least-once delivery is declared. Consumers must deduplicate by payload.event_id and tolerate redelivery.

Deduplication key: payload.event_id

Event name

certarail.asset.movement.v1

Asset movement event v1

SEND OPERATION DECLARED

This event records an observation and does not execute funds or asset movement.

Canonical AsyncAPI declares a send operation. This documents the producer contract; it does not by itself prove a live or production publisher.

Channel / topic
certarail.movement.v1channel: assetMovements
Version
v1AsyncAPI document 1.3.1 · transport header 1

Producer

Send operation emitAssetMovement is declared in canonical AsyncAPI. This is a contract declaration, not runtime evidence.

  • emitAssetMovementPublish committed asset movement observation evidence.

Consumer

No named consumer operation is declared in canonical AsyncAPI; the catalog does not infer one.

Payload schema

CertaRail Asset Movement Event v1

https://schemas.certarail.local/events/certarail.asset.movement.v1.schema.json

Raw JSON Schema
Top-level payload fields; nested constraints remain available in raw JSON Schema.
FieldRequirementType / constraintsDescription
event_idrequiredstringminLength 1No field description declared.
event_typerequiredstringconst "certarail.asset.movement.v1"No field description declared.
occurred_atrequiredstringformat date-timeNo field description declared.
tenant_idrequiredstringminLength 1 · maxLength 128No field description declared.
movementrequiredobjectNo field description declared.
Full payload schema
https://schemas.certarail.local/events/certarail.asset.movement.v1.schema.jsonjson-schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://schemas.certarail.local/events/certarail.asset.movement.v1.schema.json",
  "title": "CertaRail Asset Movement Event v1",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "event_id",
    "event_type",
    "occurred_at",
    "tenant_id",
    "movement"
  ],
  "properties": {
    "event_id": {
      "type": "string",
      "minLength": 1
    },
    "event_type": {
      "const": "certarail.asset.movement.v1"
    },
    "occurred_at": {
      "type": "string",
      "format": "date-time"
    },
    "tenant_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "movement": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "movement_id",
        "request_id",
        "tenant_id",
        "decision_id",
        "kind",
        "direction",
        "asset_code",
        "quantity",
        "network",
        "status",
        "wallet_ref",
        "source_system",
        "source_event_ref",
        "observed_at",
        "recorded_at",
        "evidence_digest"
      ],
      "properties": {
        "movement_id": {
          "type": "string",
          "minLength": 1
        },
        "request_id": {
          "type": "string",
          "minLength": 1
        },
        "tenant_id": {
          "type": "string",
          "minLength": 1
        },
        "decision_id": {
          "type": "string",
          "minLength": 1
        },
        "kind": {
          "enum": [
            "BUY_FILL",
            "SELL_FILL",
            "DEPOSIT",
            "WITHDRAWAL",
            "INTERNAL_TRANSFER",
            "EXTERNAL_TRANSFER",
            "SWAP_LEG",
            "FEE",
            "REFUND",
            "REVERSAL",
            "REWARD",
            "AIRDROP",
            "FORK",
            "MINING",
            "STAKING",
            "CUSTODY_MOVE",
            "ADJUSTMENT"
          ]
        },
        "direction": {
          "enum": [
            "CREDIT",
            "DEBIT"
          ]
        },
        "asset_code": {
          "type": "string",
          "minLength": 1
        },
        "quantity": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "atomic_units",
            "decimals"
          ],
          "properties": {
            "atomic_units": {
              "type": "string",
              "pattern": "^[1-9][0-9]{0,77}$"
            },
            "decimals": {
              "type": "integer",
              "minimum": 0,
              "maximum": 38
            }
          }
        },
        "network": {
          "type": "string",
          "minLength": 1
        },
        "status": {
          "enum": [
            "OBSERVED",
            "PENDING",
            "CONFIRMED",
            "FAILED",
            "REVERSED"
          ]
        },
        "wallet_ref": {
          "type": "string",
          "minLength": 1
        },
        "counterparty_ref": {
          "type": "string"
        },
        "venue_ref": {
          "type": "string"
        },
        "blockchain_transaction_id": {
          "type": "string"
        },
        "confirmations": {
          "type": "integer",
          "minimum": 0
        },
        "source_system": {
          "type": "string",
          "minLength": 1
        },
        "source_event_ref": {
          "type": "string",
          "minLength": 1
        },
        "kyt_assessment_ref": {
          "type": "string"
        },
        "address_verification_ref": {
          "type": "string"
        },
        "travel_rule_ref": {
          "type": "string"
        },
        "observed_at": {
          "type": "string",
          "format": "date-time"
        },
        "recorded_at": {
          "type": "string",
          "format": "date-time"
        },
        "evidence_digest": {
          "type": "string",
          "pattern": "^[a-f0-9]{64}$"
        }
      }
    }
  }
}

Example

Schema-valid synthetic payload

Generated from the canonical JSON Schema for documentation. It is not a broker record, provider receipt, or runtime evidence.

certarail.asset.movement.v1 examplejson
{
  "event_id": "event_example",
  "event_type": "certarail.asset.movement.v1",
  "occurred_at": "2026-01-01T00:00:00Z",
  "tenant_id": "tenant_example",
  "movement": {
    "movement_id": "movement_example",
    "request_id": "request_example",
    "tenant_id": "tenant_example",
    "decision_id": "decision_example",
    "kind": "BUY_FILL",
    "direction": "CREDIT",
    "asset_code": "BTC",
    "quantity": {
      "atomic_units": "1",
      "decimals": 0
    },
    "network": "bitcoin",
    "status": "OBSERVED",
    "wallet_ref": "wallet_example",
    "source_system": "example",
    "source_event_ref": "source_event_example",
    "observed_at": "2026-01-01T00:00:00Z",
    "recorded_at": "2026-01-01T00:00:00Z",
    "evidence_digest": "0000000000000000000000000000000000000000000000000000000000000000"
  }
}

Correlation fields

Identifiers and trace context

Identifier, reference, trace, and Kafka-key fields are listed from the canonical schemas and message bindings.

Correlation candidates derived from payload, headers, and Kafka binding.
LocationFieldRequirementMeaning
payloadpayload.event_idrequiredIdentifier declared without a field description.
payloadpayload.tenant_idrequiredIdentifier declared without a field description.
payloadpayload.movement.movement_idrequiredIdentifier declared without a field description.
payloadpayload.movement.request_idrequiredIdentifier declared without a field description.
payloadpayload.movement.tenant_idrequiredIdentifier declared without a field description.
payloadpayload.movement.decision_idrequiredIdentifier declared without a field description.
payloadpayload.movement.wallet_refrequiredIdentifier declared without a field description.
payloadpayload.movement.counterparty_refoptionalIdentifier declared without a field description.
payloadpayload.movement.venue_refoptionalIdentifier declared without a field description.
payloadpayload.movement.blockchain_transaction_idoptionalIdentifier declared without a field description.
payloadpayload.movement.source_event_refrequiredIdentifier declared without a field description.
payloadpayload.movement.kyt_assessment_refoptionalIdentifier declared without a field description.
payloadpayload.movement.address_verification_refoptionalIdentifier declared without a field description.
payloadpayload.movement.travel_rule_refoptionalIdentifier declared without a field description.
headerheaders.certarail-event-idrequiredStable consumer deduplication identifier; equal to payload event_id.
headerheaders.traceparentoptionalOptional W3C Trace Context for the Kafka publisher attempt.
headerheaders.tracestateoptionalOptional W3C Trace Context tracestate value; omitted when empty.
kafka-keymessage.keydeclaredPseudonymous ordering key computed as `movement_` plus the first 16 bytes of SHA-256(tenant_id || NUL || wallet_ref), encoded as lowercase hexadecimal. It is not an authentication or tenancy boundary.

Ordering semantics

declared

Ordering is guaranteed only for one Kafka key within certarail.movement.v1.

Scope: one Kafka key within certarail.movement.v1

Pseudonymous ordering key computed as `movement_` plus the first 16 bytes of SHA-256(tenant_id || NUL || wallet_ref), encoded as lowercase hexadecimal. It is not an authentication or tenancy boundary.

Retry semantics

declared

at-least-once delivery is declared. Consumers must deduplicate by payload.event_id and tolerate redelivery.

Deduplication key: payload.event_id

Event name

certarail.trade.order.v1

Legacy durable sandbox trading order event v1

SEND OPERATION DECLARED

Legacy records may omit or empty subject_ref and may contain BUY or SELL, or contain a three-letter quote outside the current v2 set. Existing orders have immutable event_contract=v1 and retain v1 for all later lifecycle facts. Historical v1 payloads may omit event_contract. No legacy order can be newly created. A legacy SELL can only be locally cancelled and cannot receive a fill. V1 represents only CertaRail sandbox state and is not live execution or a balance.

Canonical AsyncAPI declares a send operation. This documents the producer contract; it does not by itself prove a live or production publisher.

Channel / topic
certarail.trade.v1channel: sandboxTradingOrders
Version
v1AsyncAPI document 1.3.1 · transport header 1

Producer

Send operation emitSandboxTradingOrderFact is declared in canonical AsyncAPI. This is a contract declaration, not runtime evidence.

  • emitSandboxTradingOrderFactPublish a committed local non-monetary sandbox trading fact.

Consumer

No named consumer operation is declared in canonical AsyncAPI; the catalog does not infer one.

Payload schema

CertaRail legacy durable sandbox trading order event v1

urn:certarail:schema:event:certarail.trade.order.v1

Raw JSON Schema
Top-level payload fields; nested constraints remain available in raw JSON Schema.
FieldRequirementType / constraintsDescription
event_idrequiredstringpattern ^evt-trade_[0-9a-f]{32}$No field description declared.
event_typerequiredstringconst "certarail.trade.order.v1"No field description declared.
actionrequiredstringORDER_OPENED | SYNTHETIC_FILL_RECORDED | ORDER_CANCELLEDNo field description declared.
occurred_atrequiredstringformat date-timeNo field description declared.
tenant_idrequiredstringminLength 1 · maxLength 128No field description declared.
order_idrequiredstringpattern ^ord_[0-9a-f]{32}$No field description declared.
decision_idrequiredstringminLength 1 · maxLength 160No field description declared.
orderrequiredobject#/$defs/orderNo field description declared.
filloptionalobject#/$defs/fillNo field description declared.
evidence_digestrequiredstring#/$defs/digestNo field description declared.
safetyrequiredobject#/$defs/safetyNo field description declared.
external_callsrequirednumberconst 0No field description declared.
Full payload schema
urn:certarail:schema:event:certarail.trade.order.v1json-schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:certarail:schema:event:certarail.trade.order.v1",
  "title": "CertaRail legacy durable sandbox trading order event v1",
  "description": "Frozen legacy PostgreSQL transactional-outbox contract emitted before subject binding, the BUY-only gate, the current RUB/USD/EUR quote set and explicit order provenance. V1 may omit subject_ref, may contain BUY or SELL, and accepts any uppercase three-letter quote. Existing rows are assigned immutable event_contract v1 and retain v1 for subsequent lifecycle facts; historical v1 payloads may omit event_contract. A legacy SELL can only be locally cancelled and cannot receive a fill. V1 never authorizes a new legacy order or asserts a live venue call, market price, settlement, custody balance, ledger posting, asset movement, or legal permission to trade.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "event_id",
    "event_type",
    "action",
    "occurred_at",
    "tenant_id",
    "order_id",
    "decision_id",
    "order",
    "evidence_digest",
    "safety",
    "external_calls"
  ],
  "properties": {
    "event_id": {
      "type": "string",
      "pattern": "^evt-trade_[0-9a-f]{32}$"
    },
    "event_type": {
      "const": "certarail.trade.order.v1"
    },
    "action": {
      "enum": [
        "ORDER_OPENED",
        "SYNTHETIC_FILL_RECORDED",
        "ORDER_CANCELLED"
      ]
    },
    "occurred_at": {
      "type": "string",
      "format": "date-time"
    },
    "tenant_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "order_id": {
      "type": "string",
      "pattern": "^ord_[0-9a-f]{32}$"
    },
    "decision_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 160
    },
    "order": {
      "$ref": "#/$defs/order"
    },
    "fill": {
      "$ref": "#/$defs/fill"
    },
    "evidence_digest": {
      "$ref": "#/$defs/digest"
    },
    "safety": {
      "$ref": "#/$defs/safety"
    },
    "external_calls": {
      "const": 0
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "action": {
            "const": "SYNTHETIC_FILL_RECORDED"
          }
        },
        "required": [
          "action"
        ]
      },
      "then": {
        "properties": {
          "fill": {
            "$ref": "#/$defs/fill"
          }
        },
        "required": [
          "fill"
        ]
      },
      "else": {
        "properties": {
          "fill": false
        }
      }
    }
  ],
  "$defs": {
    "digest": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$"
    },
    "safety": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "environment",
        "production_enforceable",
        "monetary"
      ],
      "properties": {
        "environment": {
          "const": "sandbox"
        },
        "production_enforceable": {
          "const": false
        },
        "monetary": {
          "const": false
        }
      }
    },
    "instrument": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "base_asset",
        "quote_currency"
      ],
      "properties": {
        "base_asset": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64
        },
        "quote_currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        }
      }
    },
    "positiveQuantity": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "atomic_units",
        "decimals"
      ],
      "properties": {
        "atomic_units": {
          "type": "string",
          "pattern": "^[1-9][0-9]{0,77}$"
        },
        "decimals": {
          "type": "integer",
          "minimum": 0,
          "maximum": 38
        }
      }
    },
    "nonNegativeQuantity": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "atomic_units",
        "decimals"
      ],
      "properties": {
        "atomic_units": {
          "type": "string",
          "pattern": "^(0|[1-9][0-9]{0,77})$"
        },
        "decimals": {
          "type": "integer",
          "minimum": 0,
          "maximum": 38
        }
      }
    },
    "decimal": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "value",
        "scale"
      ],
      "properties": {
        "value": {
          "type": "string",
          "pattern": "^[1-9][0-9]{0,77}$"
        },
        "scale": {
          "type": "integer",
          "minimum": 0,
          "maximum": 38
        }
      }
    },
    "money": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "currency",
        "minor_units"
      ],
      "properties": {
        "currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "minor_units": {
          "type": "integer",
          "minimum": 1,
          "maximum": 9223372036854776000
        }
      }
    },
    "order": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "order_id",
        "request_id",
        "tenant_id",
        "decision_id",
        "portfolio_ref",
        "approval_ref",
        "connection_id",
        "account_ref",
        "client_order_id",
        "instrument",
        "side",
        "order_type",
        "base_quantity",
        "time_in_force",
        "max_notional",
        "command_id",
        "provider_order_ref",
        "status",
        "submit_disposition",
        "venue_state",
        "filled_quantity",
        "version",
        "created_at",
        "updated_at",
        "evidence_digest",
        "safety",
        "external_calls"
      ],
      "properties": {
        "order_id": {
          "type": "string",
          "pattern": "^ord_[0-9a-f]{32}$"
        },
        "request_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        },
        "tenant_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        },
        "decision_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 160
        },
        "event_contract": {
          "const": "certarail.trade.order.v1",
          "description": "Immutable order event-schema provenance. Optional because historical v1 payloads predate the field."
        },
        "subject_ref": {
          "type": "string",
          "minLength": 0,
          "maxLength": 256,
          "description": "Optional legacy field. It may be absent or empty because v1 predates mandatory eligibility subject binding."
        },
        "portfolio_ref": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256
        },
        "approval_ref": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256,
          "description": "Correlation only; not evidence that CertaRail performed maker-checker approval."
        },
        "connection_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        },
        "account_ref": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256
        },
        "client_order_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        },
        "instrument": {
          "$ref": "#/$defs/instrument"
        },
        "side": {
          "enum": [
            "BUY",
            "SELL"
          ],
          "description": "Legacy v1 facts may contain SELL. This does not make SELL executable in the current runtime."
        },
        "order_type": {
          "const": "MARKET"
        },
        "base_quantity": {
          "$ref": "#/$defs/positiveQuantity"
        },
        "limit_price": {
          "$ref": "#/$defs/decimal"
        },
        "time_in_force": {
          "const": "IOC"
        },
        "max_notional": {
          "$ref": "#/$defs/money"
        },
        "command_id": {
          "type": "string",
          "pattern": "^cmd_[0-9a-f]{32}$"
        },
        "provider_order_ref": {
          "type": "string",
          "pattern": "^syn-order_[0-9a-f]{32}$",
          "description": "Deterministic local reference, never a provider receipt."
        },
        "status": {
          "enum": [
            "OPEN",
            "PARTIALLY_FILLED",
            "FILLED",
            "CANCELLED"
          ]
        },
        "submit_disposition": {
          "const": "ACKNOWLEDGED",
          "description": "Local PostgreSQL acknowledgement only."
        },
        "venue_state": {
          "enum": [
            "OPEN",
            "PARTIALLY_FILLED",
            "FILLED",
            "CANCELLED"
          ]
        },
        "filled_quantity": {
          "$ref": "#/$defs/nonNegativeQuantity"
        },
        "version": {
          "type": "integer",
          "minimum": 1
        },
        "created_at": {
          "type": "string",
          "format": "date-time"
        },
        "updated_at": {
          "type": "string",
          "format": "date-time"
        },
        "evidence_digest": {
          "$ref": "#/$defs/digest"
        },
        "safety": {
          "$ref": "#/$defs/safety"
        },
        "external_calls": {
          "const": 0
        }
      }
    },
    "fill": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "fill_id",
        "order_id",
        "tenant_id",
        "fill_ref",
        "quantity",
        "price",
        "observed_at",
        "recorded_at",
        "evidence_digest",
        "safety"
      ],
      "properties": {
        "fill_id": {
          "type": "string",
          "pattern": "^fill_[0-9a-f]{32}$"
        },
        "order_id": {
          "type": "string",
          "pattern": "^ord_[0-9a-f]{32}$"
        },
        "tenant_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        },
        "fill_ref": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256,
          "description": "Synthetic deduplication reference, not a live venue fill ID."
        },
        "quantity": {
          "$ref": "#/$defs/positiveQuantity"
        },
        "price": {
          "allOf": [
            {
              "$ref": "#/$defs/decimal"
            }
          ],
          "description": "Caller-supplied local fixture, not real-time market data."
        },
        "observed_at": {
          "type": "string",
          "format": "date-time"
        },
        "recorded_at": {
          "type": "string",
          "format": "date-time"
        },
        "evidence_digest": {
          "allOf": [
            {
              "$ref": "#/$defs/digest"
            }
          ],
          "description": "Legacy lowercase SHA-256 evidence digest. Consumers must not assume v2 fill-preimage semantics for already published v1 payloads."
        },
        "evidence_format": {
          "enum": [
            "SYNTHETIC_FILL_EVIDENCE_V1",
            "LEGACY_COMMAND_PREIMAGE_V1"
          ],
          "description": "Optional compatibility tag. Historical v1 payloads may omit it; LEGACY_COMMAND_PREIMAGE_V1 identifies a repaired row whose digest is bound to the original immutable command preimage."
        },
        "safety": {
          "$ref": "#/$defs/safety"
        }
      }
    }
  }
}

Example

Schema-valid synthetic payload

Generated from the canonical JSON Schema for documentation. It is not a broker record, provider receipt, or runtime evidence.

certarail.trade.order.v1 examplejson
{
  "event_id": "evt-trade_00000000000000000000000000000000",
  "event_type": "certarail.trade.order.v1",
  "action": "ORDER_OPENED",
  "occurred_at": "2026-01-01T00:00:00Z",
  "tenant_id": "tenant_example",
  "order_id": "ord_00000000000000000000000000000000",
  "decision_id": "decision_example",
  "order": {
    "order_id": "ord_00000000000000000000000000000000",
    "request_id": "request_example",
    "tenant_id": "tenant_example",
    "decision_id": "decision_example",
    "event_contract": "certarail.trade.order.v1",
    "portfolio_ref": "portfolio_example",
    "approval_ref": "approval_example",
    "connection_id": "connection_example",
    "account_ref": "account_example",
    "client_order_id": "client_order_example",
    "instrument": {
      "base_asset": "BTC",
      "quote_currency": "RUB"
    },
    "side": "BUY",
    "order_type": "MARKET",
    "base_quantity": {
      "atomic_units": "1",
      "decimals": 0
    },
    "time_in_force": "IOC",
    "max_notional": {
      "currency": "RUB",
      "minor_units": 1
    },
    "command_id": "cmd_00000000000000000000000000000000",
    "provider_order_ref": "syn-order_00000000000000000000000000000000",
    "status": "OPEN",
    "submit_disposition": "ACKNOWLEDGED",
    "venue_state": "OPEN",
    "filled_quantity": {
      "atomic_units": "0",
      "decimals": 0
    },
    "version": 1,
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-01-01T00:00:00Z",
    "evidence_digest": "0000000000000000000000000000000000000000000000000000000000000000",
    "safety": {
      "environment": "sandbox",
      "production_enforceable": false,
      "monetary": false
    },
    "external_calls": 0
  },
  "evidence_digest": "0000000000000000000000000000000000000000000000000000000000000000",
  "safety": {
    "environment": "sandbox",
    "production_enforceable": false,
    "monetary": false
  },
  "external_calls": 0
}

Correlation fields

Identifiers and trace context

Identifier, reference, trace, and Kafka-key fields are listed from the canonical schemas and message bindings.

Correlation candidates derived from payload, headers, and Kafka binding.
LocationFieldRequirementMeaning
payloadpayload.event_idrequiredIdentifier declared without a field description.
payloadpayload.tenant_idrequiredIdentifier declared without a field description.
payloadpayload.order_idrequiredIdentifier declared without a field description.
payloadpayload.decision_idrequiredIdentifier declared without a field description.
payloadpayload.order.order_idrequiredIdentifier declared without a field description.
payloadpayload.order.request_idrequiredIdentifier declared without a field description.
payloadpayload.order.tenant_idrequiredIdentifier declared without a field description.
payloadpayload.order.decision_idrequiredIdentifier declared without a field description.
payloadpayload.order.subject_refoptionalOptional legacy field. It may be absent or empty because v1 predates mandatory eligibility subject binding.
payloadpayload.order.portfolio_refrequiredIdentifier declared without a field description.
payloadpayload.order.approval_refrequiredCorrelation only; not evidence that CertaRail performed maker-checker approval.
payloadpayload.order.connection_idrequiredIdentifier declared without a field description.
payloadpayload.order.account_refrequiredIdentifier declared without a field description.
payloadpayload.order.client_order_idrequiredIdentifier declared without a field description.
payloadpayload.order.command_idrequiredIdentifier declared without a field description.
payloadpayload.order.provider_order_refrequiredDeterministic local reference, never a provider receipt.
payloadpayload.fill.fill_idoptionalIdentifier declared without a field description.
payloadpayload.fill.order_idoptionalIdentifier declared without a field description.
payloadpayload.fill.tenant_idoptionalIdentifier declared without a field description.
payloadpayload.fill.fill_refoptionalSynthetic deduplication reference, not a live venue fill ID.
headerheaders.certarail-event-idrequiredStable consumer deduplication identifier; equal to payload event_id.
headerheaders.traceparentoptionalOptional W3C Trace Context for the Kafka publisher attempt.
headerheaders.tracestateoptionalOptional W3C Trace Context tracestate value; omitted when empty.
kafka-keymessage.keydeclaredPseudonymous ordering key computed as `order_` plus the first 16 bytes of SHA-256(tenant_id || NUL || order_id), encoded as lowercase hexadecimal. It is not authentication, tenancy, provider identity, or proof of execution.

Ordering semantics

declared

Ordering is guaranteed only for one order key within certarail.trade.v1.

Scope: one order key within certarail.trade.v1

Pseudonymous ordering key computed as `order_` plus the first 16 bytes of SHA-256(tenant_id || NUL || order_id), encoded as lowercase hexadecimal. It is not authentication, tenancy, provider identity, or proof of execution.

Retry semantics

declared

at-least-once delivery is declared. Consumers must deduplicate by payload.event_id and tolerate redelivery.

Deduplication key: payload.event_id

Scope: one event_id across v1 and v2

Event name

certarail.trade.order.v2

Current durable sandbox trading order event v2

SEND OPERATION DECLARED

Current v2 requires non-empty eligibility-bound subject_ref and side=BUY. New orders receive immutable event_contract=v2, which routes the full lifecycle to this schema. The event represents only CertaRail sandbox state. It is not a live venue execution, provider receipt, market price, bank/custody balance, asset movement, settlement record, or accounting posting.

Canonical AsyncAPI declares a send operation. This documents the producer contract; it does not by itself prove a live or production publisher.

Channel / topic
certarail.trade.v1channel: sandboxTradingOrders
Version
v2AsyncAPI document 1.3.1 · transport header 1

Producer

Send operation emitSandboxTradingOrderFact is declared in canonical AsyncAPI. This is a contract declaration, not runtime evidence.

  • emitSandboxTradingOrderFactPublish a committed local non-monetary sandbox trading fact.

Consumer

No named consumer operation is declared in canonical AsyncAPI; the catalog does not infer one.

Payload schema

CertaRail durable sandbox trading order event v2

urn:certarail:schema:event:certarail.trade.order.v2

Raw JSON Schema
Top-level payload fields; nested constraints remain available in raw JSON Schema.
FieldRequirementType / constraintsDescription
event_idrequiredstringpattern ^evt-trade_[0-9a-f]{32}$No field description declared.
event_typerequiredstringconst "certarail.trade.order.v2"No field description declared.
actionrequiredstringORDER_OPENED | SYNTHETIC_FILL_RECORDED | ORDER_CANCELLEDNo field description declared.
occurred_atrequiredstringformat date-timeNo field description declared.
tenant_idrequiredstringminLength 1 · maxLength 128No field description declared.
order_idrequiredstringpattern ^ord_[0-9a-f]{32}$No field description declared.
decision_idrequiredstringminLength 1 · maxLength 160No field description declared.
orderrequiredobject#/$defs/orderNo field description declared.
filloptionalobject#/$defs/fillNo field description declared.
evidence_digestrequiredstring#/$defs/digestNo field description declared.
safetyrequiredobject#/$defs/safetyNo field description declared.
external_callsrequirednumberconst 0No field description declared.
Full payload schema
urn:certarail:schema:event:certarail.trade.order.v2json-schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:certarail:schema:event:certarail.trade.order.v2",
  "title": "CertaRail durable sandbox trading order event v2",
  "description": "Current PostgreSQL transactional-outbox fact from the BUY-only non-monetary sandbox. V2 requires the eligibility-bound opaque subject_ref and never asserts a live venue call, market price, settlement, custody balance, ledger posting, asset movement, or legal permission to trade.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "event_id",
    "event_type",
    "action",
    "occurred_at",
    "tenant_id",
    "order_id",
    "decision_id",
    "order",
    "evidence_digest",
    "safety",
    "external_calls"
  ],
  "properties": {
    "event_id": {
      "type": "string",
      "pattern": "^evt-trade_[0-9a-f]{32}$"
    },
    "event_type": {
      "const": "certarail.trade.order.v2"
    },
    "action": {
      "enum": [
        "ORDER_OPENED",
        "SYNTHETIC_FILL_RECORDED",
        "ORDER_CANCELLED"
      ]
    },
    "occurred_at": {
      "type": "string",
      "format": "date-time"
    },
    "tenant_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "order_id": {
      "type": "string",
      "pattern": "^ord_[0-9a-f]{32}$"
    },
    "decision_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 160
    },
    "order": {
      "$ref": "#/$defs/order"
    },
    "fill": {
      "$ref": "#/$defs/fill"
    },
    "evidence_digest": {
      "$ref": "#/$defs/digest"
    },
    "safety": {
      "$ref": "#/$defs/safety"
    },
    "external_calls": {
      "const": 0
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "action": {
            "const": "SYNTHETIC_FILL_RECORDED"
          }
        },
        "required": [
          "action"
        ]
      },
      "then": {
        "properties": {
          "fill": {
            "$ref": "#/$defs/fill"
          }
        },
        "required": [
          "fill"
        ]
      },
      "else": {
        "properties": {
          "fill": false
        }
      }
    }
  ],
  "$defs": {
    "digest": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$"
    },
    "safety": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "environment",
        "production_enforceable",
        "monetary"
      ],
      "properties": {
        "environment": {
          "const": "sandbox"
        },
        "production_enforceable": {
          "const": false
        },
        "monetary": {
          "const": false
        }
      }
    },
    "instrument": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "base_asset",
        "quote_currency"
      ],
      "properties": {
        "base_asset": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64
        },
        "quote_currency": {
          "enum": [
            "RUB",
            "USD",
            "EUR"
          ]
        }
      }
    },
    "positiveQuantity": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "atomic_units",
        "decimals"
      ],
      "properties": {
        "atomic_units": {
          "type": "string",
          "pattern": "^[1-9][0-9]{0,77}$"
        },
        "decimals": {
          "type": "integer",
          "minimum": 0,
          "maximum": 38
        }
      }
    },
    "nonNegativeQuantity": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "atomic_units",
        "decimals"
      ],
      "properties": {
        "atomic_units": {
          "type": "string",
          "pattern": "^(0|[1-9][0-9]{0,77})$"
        },
        "decimals": {
          "type": "integer",
          "minimum": 0,
          "maximum": 38
        }
      }
    },
    "decimal": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "value",
        "scale"
      ],
      "properties": {
        "value": {
          "type": "string",
          "pattern": "^[1-9][0-9]{0,77}$"
        },
        "scale": {
          "type": "integer",
          "minimum": 0,
          "maximum": 38
        }
      }
    },
    "money": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "currency",
        "minor_units"
      ],
      "properties": {
        "currency": {
          "type": "string",
          "pattern": "^[A-Z]{3}$"
        },
        "minor_units": {
          "type": "integer",
          "minimum": 1,
          "maximum": 9223372036854776000
        }
      }
    },
    "order": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "order_id",
        "request_id",
        "tenant_id",
        "decision_id",
        "event_contract",
        "subject_ref",
        "portfolio_ref",
        "approval_ref",
        "connection_id",
        "account_ref",
        "client_order_id",
        "instrument",
        "side",
        "order_type",
        "base_quantity",
        "time_in_force",
        "max_notional",
        "command_id",
        "provider_order_ref",
        "status",
        "submit_disposition",
        "venue_state",
        "filled_quantity",
        "version",
        "created_at",
        "updated_at",
        "evidence_digest",
        "safety",
        "external_calls"
      ],
      "properties": {
        "order_id": {
          "type": "string",
          "pattern": "^ord_[0-9a-f]{32}$"
        },
        "request_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        },
        "tenant_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        },
        "decision_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 160
        },
        "event_contract": {
          "const": "certarail.trade.order.v2",
          "description": "Immutable event-schema provenance assigned when a new order is created."
        },
        "subject_ref": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256,
          "description": "Opaque reference bound to client.subject_ref in the verified eligibility evidence."
        },
        "portfolio_ref": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256
        },
        "approval_ref": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256,
          "description": "Correlation only; not evidence that CertaRail performed maker-checker approval."
        },
        "connection_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        },
        "account_ref": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256
        },
        "client_order_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        },
        "instrument": {
          "$ref": "#/$defs/instrument"
        },
        "side": {
          "const": "BUY",
          "description": "Current v2 sandbox events are BUY-only; SELL is not executable."
        },
        "order_type": {
          "const": "MARKET"
        },
        "base_quantity": {
          "$ref": "#/$defs/positiveQuantity"
        },
        "limit_price": {
          "$ref": "#/$defs/decimal"
        },
        "time_in_force": {
          "const": "IOC"
        },
        "max_notional": {
          "$ref": "#/$defs/money"
        },
        "command_id": {
          "type": "string",
          "pattern": "^cmd_[0-9a-f]{32}$"
        },
        "provider_order_ref": {
          "type": "string",
          "pattern": "^syn-order_[0-9a-f]{32}$",
          "description": "Deterministic local reference, never a provider receipt."
        },
        "status": {
          "enum": [
            "OPEN",
            "PARTIALLY_FILLED",
            "FILLED",
            "CANCELLED"
          ]
        },
        "submit_disposition": {
          "const": "ACKNOWLEDGED",
          "description": "Local PostgreSQL acknowledgement only."
        },
        "venue_state": {
          "enum": [
            "OPEN",
            "PARTIALLY_FILLED",
            "FILLED",
            "CANCELLED"
          ]
        },
        "filled_quantity": {
          "$ref": "#/$defs/nonNegativeQuantity"
        },
        "version": {
          "type": "integer",
          "minimum": 1
        },
        "created_at": {
          "type": "string",
          "format": "date-time"
        },
        "updated_at": {
          "type": "string",
          "format": "date-time"
        },
        "evidence_digest": {
          "$ref": "#/$defs/digest"
        },
        "safety": {
          "$ref": "#/$defs/safety"
        },
        "external_calls": {
          "const": 0
        }
      }
    },
    "fill": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "fill_id",
        "order_id",
        "tenant_id",
        "fill_ref",
        "quantity",
        "price",
        "observed_at",
        "recorded_at",
        "evidence_digest",
        "evidence_format",
        "safety"
      ],
      "properties": {
        "fill_id": {
          "type": "string",
          "pattern": "^fill_[0-9a-f]{32}$"
        },
        "order_id": {
          "type": "string",
          "pattern": "^ord_[0-9a-f]{32}$"
        },
        "tenant_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        },
        "fill_ref": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256,
          "description": "Synthetic deduplication reference, not a live venue fill ID."
        },
        "quantity": {
          "$ref": "#/$defs/positiveQuantity"
        },
        "price": {
          "allOf": [
            {
              "$ref": "#/$defs/decimal"
            }
          ],
          "description": "Caller-supplied local fixture, not real-time market data."
        },
        "observed_at": {
          "type": "string",
          "format": "date-time"
        },
        "recorded_at": {
          "type": "string",
          "format": "date-time"
        },
        "evidence_digest": {
          "allOf": [
            {
              "$ref": "#/$defs/digest"
            }
          ],
          "description": "SHA-256 of the persisted versioned canonical synthetic-fill evidence bytes."
        },
        "evidence_format": {
          "const": "SYNTHETIC_FILL_EVIDENCE_V1",
          "description": "Exact current canonical evidence format hashed by evidence_digest."
        },
        "safety": {
          "$ref": "#/$defs/safety"
        }
      }
    }
  }
}

Example

Schema-valid synthetic payload

Generated from the canonical JSON Schema for documentation. It is not a broker record, provider receipt, or runtime evidence.

certarail.trade.order.v2 examplejson
{
  "event_id": "evt-trade_00000000000000000000000000000000",
  "event_type": "certarail.trade.order.v2",
  "action": "ORDER_OPENED",
  "occurred_at": "2026-01-01T00:00:00Z",
  "tenant_id": "tenant_example",
  "order_id": "ord_00000000000000000000000000000000",
  "decision_id": "decision_example",
  "order": {
    "order_id": "ord_00000000000000000000000000000000",
    "request_id": "request_example",
    "tenant_id": "tenant_example",
    "decision_id": "decision_example",
    "event_contract": "certarail.trade.order.v2",
    "subject_ref": "subject_example",
    "portfolio_ref": "portfolio_example",
    "approval_ref": "approval_example",
    "connection_id": "connection_example",
    "account_ref": "account_example",
    "client_order_id": "client_order_example",
    "instrument": {
      "base_asset": "BTC",
      "quote_currency": "RUB"
    },
    "side": "BUY",
    "order_type": "MARKET",
    "base_quantity": {
      "atomic_units": "1",
      "decimals": 0
    },
    "time_in_force": "IOC",
    "max_notional": {
      "currency": "RUB",
      "minor_units": 1
    },
    "command_id": "cmd_00000000000000000000000000000000",
    "provider_order_ref": "syn-order_00000000000000000000000000000000",
    "status": "OPEN",
    "submit_disposition": "ACKNOWLEDGED",
    "venue_state": "OPEN",
    "filled_quantity": {
      "atomic_units": "0",
      "decimals": 0
    },
    "version": 1,
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-01-01T00:00:00Z",
    "evidence_digest": "0000000000000000000000000000000000000000000000000000000000000000",
    "safety": {
      "environment": "sandbox",
      "production_enforceable": false,
      "monetary": false
    },
    "external_calls": 0
  },
  "evidence_digest": "0000000000000000000000000000000000000000000000000000000000000000",
  "safety": {
    "environment": "sandbox",
    "production_enforceable": false,
    "monetary": false
  },
  "external_calls": 0
}

Correlation fields

Identifiers and trace context

Identifier, reference, trace, and Kafka-key fields are listed from the canonical schemas and message bindings.

Correlation candidates derived from payload, headers, and Kafka binding.
LocationFieldRequirementMeaning
payloadpayload.event_idrequiredIdentifier declared without a field description.
payloadpayload.tenant_idrequiredIdentifier declared without a field description.
payloadpayload.order_idrequiredIdentifier declared without a field description.
payloadpayload.decision_idrequiredIdentifier declared without a field description.
payloadpayload.order.order_idrequiredIdentifier declared without a field description.
payloadpayload.order.request_idrequiredIdentifier declared without a field description.
payloadpayload.order.tenant_idrequiredIdentifier declared without a field description.
payloadpayload.order.decision_idrequiredIdentifier declared without a field description.
payloadpayload.order.subject_refrequiredOpaque reference bound to client.subject_ref in the verified eligibility evidence.
payloadpayload.order.portfolio_refrequiredIdentifier declared without a field description.
payloadpayload.order.approval_refrequiredCorrelation only; not evidence that CertaRail performed maker-checker approval.
payloadpayload.order.connection_idrequiredIdentifier declared without a field description.
payloadpayload.order.account_refrequiredIdentifier declared without a field description.
payloadpayload.order.client_order_idrequiredIdentifier declared without a field description.
payloadpayload.order.command_idrequiredIdentifier declared without a field description.
payloadpayload.order.provider_order_refrequiredDeterministic local reference, never a provider receipt.
payloadpayload.fill.fill_idoptionalIdentifier declared without a field description.
payloadpayload.fill.order_idoptionalIdentifier declared without a field description.
payloadpayload.fill.tenant_idoptionalIdentifier declared without a field description.
payloadpayload.fill.fill_refoptionalSynthetic deduplication reference, not a live venue fill ID.
headerheaders.certarail-event-idrequiredStable consumer deduplication identifier; equal to payload event_id.
headerheaders.traceparentoptionalOptional W3C Trace Context for the Kafka publisher attempt.
headerheaders.tracestateoptionalOptional W3C Trace Context tracestate value; omitted when empty.
kafka-keymessage.keydeclaredPseudonymous ordering key computed as `order_` plus the first 16 bytes of SHA-256(tenant_id || NUL || order_id), encoded as lowercase hexadecimal. It is not authentication, tenancy, provider identity, or proof of execution.

Ordering semantics

declared

Ordering is guaranteed only for one order key within certarail.trade.v1.

Scope: one order key within certarail.trade.v1

Pseudonymous ordering key computed as `order_` plus the first 16 bytes of SHA-256(tenant_id || NUL || order_id), encoded as lowercase hexadecimal. It is not authentication, tenancy, provider identity, or proof of execution.

Retry semantics

declared

at-least-once delivery is declared. Consumers must deduplicate by payload.event_id and tolerate redelivery.

Deduplication key: payload.event_id

Scope: one event_id across v1 and v2

Event name

certarail.checkout.lifecycle.v1

Crypto Checkout lifecycle event v1

KAFKA NOT PRODUCED

Compatibility design only. The current checkout runtime does not emit this Kafka event. Partner webhook delivery signs exact HTTP body bytes under the separately documented webhook contract and must not be inferred from this schema.

Canonical AsyncAPI marks this Kafka event as NOT_PRODUCED. Its schema is browsable, but it is not evidence of a running publisher.

Channel / topic
certarail.checkout.lifecycle.v1channel: checkoutLifecycle
Version
v1AsyncAPI document 1.3.1 · transport header 1

Producer

No producer operation is declared because the canonical Kafka publication state is NOT_PRODUCED.

Consumer

No named consumer operation is declared in canonical AsyncAPI; the catalog does not infer one.

Payload schema

CertaRail checkout lifecycle event v1

urn:certarail:schema:event:certarail.checkout.lifecycle.v1

Raw JSON Schema
Top-level payload fields; nested constraints remain available in raw JSON Schema.
FieldRequirementType / constraintsDescription
event_idrequiredstringpattern ^checkout_evt_[A-Za-z0-9._:/-]{1,112}$No field description declared.
event_typerequiredstringcheckout.session.created | checkout.session.expired | deal.action_required | deal.review | +13No field description declared.
event_versionrequirednumberconst 1No field description declared.
tenant_idrequiredstringminLength 1 · maxLength 128No field description declared.
occurred_atrequiredstringformat date-timeNo field description declared.
aggregate_idrequiredstringpattern ^(checkout_|deal_)[A-Za-z0-9._:/-]{1,120}$No field description declared.
correlation_idrequiredstringminLength 1 · maxLength 128No field description declared.
sequencerequiredintegerminimum 1No field description declared.
payloadrequiredobject#/$defs/safePayloadNo field description declared.
safetyrequiredobject#/$defs/safetyNo field description declared.
Full payload schema
urn:certarail:schema:event:certarail.checkout.lifecycle.v1json-schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:certarail:schema:event:certarail.checkout.lifecycle.v1",
  "title": "CertaRail checkout lifecycle event v1",
  "description": "PII-minimized lifecycle evidence for the sandbox Crypto Checkout. This event is not proof of a real payment, venue execution, custody balance, or blockchain transfer.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "event_id",
    "event_type",
    "event_version",
    "tenant_id",
    "occurred_at",
    "aggregate_id",
    "correlation_id",
    "sequence",
    "payload",
    "safety"
  ],
  "properties": {
    "event_id": {
      "type": "string",
      "pattern": "^checkout_evt_[A-Za-z0-9._:/-]{1,112}$"
    },
    "event_type": {
      "enum": [
        "checkout.session.created",
        "checkout.session.expired",
        "deal.action_required",
        "deal.review",
        "deal.confirmed",
        "payment.pending",
        "payment.confirmed",
        "payment.failed",
        "execution.submitted",
        "execution.unknown",
        "execution.filled",
        "delivery.pending",
        "delivery.confirmed",
        "deal.completed",
        "deal.failed",
        "refund.pending",
        "refund.completed"
      ]
    },
    "event_version": {
      "const": 1
    },
    "tenant_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "occurred_at": {
      "type": "string",
      "format": "date-time"
    },
    "aggregate_id": {
      "type": "string",
      "pattern": "^(checkout_|deal_)[A-Za-z0-9._:/-]{1,120}$"
    },
    "correlation_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "sequence": {
      "type": "integer",
      "minimum": 1
    },
    "payload": {
      "$ref": "#/$defs/safePayload"
    },
    "safety": {
      "$ref": "#/$defs/safety"
    }
  },
  "$defs": {
    "axes": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "admission",
        "quote",
        "payment",
        "execution",
        "delivery",
        "ledger"
      ],
      "properties": {
        "admission": {
          "enum": [
            "NOT_STARTED",
            "EVALUATING",
            "ALLOW",
            "DENY",
            "REVIEW",
            "ACTION_REQUIRED"
          ]
        },
        "quote": {
          "enum": [
            "NOT_REQUESTED",
            "REQUESTING",
            "AVAILABLE",
            "EXPIRED",
            "REJECTED",
            "SUPERSEDED"
          ]
        },
        "payment": {
          "enum": [
            "NOT_STARTED",
            "RESERVING",
            "RESERVED",
            "PENDING",
            "CONFIRMED",
            "FAILED",
            "RELEASE_PENDING",
            "RELEASED",
            "REFUND_PENDING",
            "REFUNDED",
            "UNKNOWN"
          ]
        },
        "execution": {
          "enum": [
            "NOT_STARTED",
            "SUBMITTING",
            "UNKNOWN",
            "ACKNOWLEDGED",
            "PARTIALLY_FILLED",
            "FILLED",
            "CANCEL_PENDING",
            "CANCELLED",
            "REJECTED",
            "FAILED"
          ]
        },
        "delivery": {
          "enum": [
            "NOT_STARTED",
            "PENDING",
            "PROCESSING",
            "CONFIRMED",
            "FAILED",
            "UNKNOWN"
          ]
        },
        "ledger": {
          "enum": [
            "NOT_STARTED",
            "RESERVED",
            "POSTED",
            "REVERSED",
            "RECONCILIATION_REQUIRED"
          ]
        }
      }
    },
    "safePayload": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "checkout_session_reference",
        "overall_status",
        "axes"
      ],
      "properties": {
        "checkout_session_reference": {
          "type": "string",
          "pattern": "^checkout_[A-Za-z0-9._:/-]{1,120}$"
        },
        "deal_reference": {
          "type": "string",
          "pattern": "^deal_[A-Za-z0-9._:/-]{1,120}$"
        },
        "overall_status": {
          "enum": [
            "DRAFT",
            "CHECKING",
            "ACTION_REQUIRED",
            "READY_TO_CONFIRM",
            "QUOTE_EXPIRED",
            "PROCESSING_PAYMENT",
            "EXECUTING",
            "DELIVERING",
            "COMPLETED",
            "REVIEW",
            "FAILED",
            "CANCELLED",
            "REFUNDING"
          ]
        },
        "axes": {
          "$ref": "#/$defs/axes"
        },
        "action": {
          "enum": [
            "RETRY",
            "REFRESH_QUOTE",
            "REDUCE_AMOUNT",
            "COMPLETE_VERIFICATION",
            "CHANGE_PAYMENT_METHOD",
            "CONTACT_SUPPORT",
            "RETURN_TO_PARTNER",
            "WAIT",
            "NONE"
          ]
        },
        "receipt_reference": {
          "type": "string",
          "pattern": "^receipt_[A-Za-z0-9._:/-]{1,118}$"
        }
      }
    },
    "safety": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "environment",
        "external_calls",
        "monetary",
        "production_enforceable",
        "execution_mode"
      ],
      "properties": {
        "environment": {
          "const": "SANDBOX"
        },
        "external_calls": {
          "const": 0
        },
        "monetary": {
          "const": false
        },
        "production_enforceable": {
          "const": false
        },
        "execution_mode": {
          "const": "SANDBOX"
        }
      }
    }
  }
}

Example

Schema-valid synthetic payload

Generated from the canonical JSON Schema for documentation. It is not a broker record, provider receipt, or runtime evidence.

certarail.checkout.lifecycle.v1 examplejson
{
  "event_id": "checkout_evt_A",
  "event_type": "checkout.session.created",
  "event_version": 1,
  "tenant_id": "tenant_example",
  "occurred_at": "2026-01-01T00:00:00Z",
  "aggregate_id": "checkout_A",
  "correlation_id": "correlation_example",
  "sequence": 1,
  "payload": {
    "checkout_session_reference": "checkout_A",
    "overall_status": "DRAFT",
    "axes": {
      "admission": "NOT_STARTED",
      "quote": "NOT_REQUESTED",
      "payment": "NOT_STARTED",
      "execution": "NOT_STARTED",
      "delivery": "NOT_STARTED",
      "ledger": "NOT_STARTED"
    }
  },
  "safety": {
    "environment": "SANDBOX",
    "external_calls": 0,
    "monetary": false,
    "production_enforceable": false,
    "execution_mode": "SANDBOX"
  }
}

Correlation fields

Identifiers and trace context

Identifier, reference, trace, and Kafka-key fields are listed from the canonical schemas and message bindings.

Correlation candidates derived from payload, headers, and Kafka binding.
LocationFieldRequirementMeaning
payloadpayload.event_idrequiredIdentifier declared without a field description.
payloadpayload.tenant_idrequiredIdentifier declared without a field description.
payloadpayload.aggregate_idrequiredIdentifier declared without a field description.
payloadpayload.correlation_idrequiredIdentifier declared without a field description.
payloadpayload.payload.checkout_session_referencerequiredIdentifier declared without a field description.
payloadpayload.payload.deal_referenceoptionalIdentifier declared without a field description.
payloadpayload.payload.receipt_referenceoptionalIdentifier declared without a field description.
headerheaders.certarail-event-idrequiredStable consumer deduplication identifier; equal to payload event_id.
headerheaders.traceparentoptionalOptional W3C Trace Context for the Kafka publisher attempt.
headerheaders.tracestateoptionalOptional W3C Trace Context tracestate value; omitted when empty.
kafka-keymessage.keydeclaredTenant-scoped aggregate ordering key; never an authentication credential.

Ordering semantics

contract-only

A Kafka key schema is documented for compatibility, but runtime ordering is not claimed while publication is NOT_PRODUCED.

Tenant-scoped aggregate ordering key; never an authentication credential.

Retry semantics

not-applicable

Kafka retry and redelivery semantics do not apply while the canonical publication state is NOT_PRODUCED. Other transport retries are separate contracts.

Event name

certarail.checkout.partner-delivery.v1

Crypto Checkout partner webhook delivery event v1

KAFKA NOT PRODUCED

PostgreSQL-backed sandbox Inbox delivery evidence exists and checkout.completed is linked to the tenant movement outbox fact. The current runtime does not emit this candidate as a Kafka event.

Canonical AsyncAPI marks this Kafka event as NOT_PRODUCED. Its schema is browsable, but it is not evidence of a running publisher.

Channel / topic
certarail.checkout.partner-delivery.v1channel: checkoutPartnerDeliveries
Version
v1AsyncAPI document 1.3.1 · transport header 1

Producer

No producer operation is declared because the canonical Kafka publication state is NOT_PRODUCED.

Consumer

No named consumer operation is declared in canonical AsyncAPI; the catalog does not infer one.

Payload schema

CertaRail checkout partner delivery event v1

urn:certarail:schema:event:certarail.checkout.partner-delivery.v1

Raw JSON Schema
Top-level payload fields; nested constraints remain available in raw JSON Schema.
FieldRequirementType / constraintsDescription
delivery_event_idrequiredstringpattern ^webhook_evt_[A-Za-z0-9._:/-]{1,111}$No field description declared.
event_typerequiredstringconst "certarail.checkout.partner-delivery.v1"No field description declared.
event_versionrequirednumberconst 1No field description declared.
tenant_idrequiredstringminLength 1 · maxLength 128No field description declared.
aggregate_idrequiredstringpattern ^(checkout_|deal_)[A-Za-z0-9._:/-]{1,120}$No field description declared.
correlation_idrequiredstringminLength 1 · maxLength 128No field description declared.
occurred_atrequiredstringformat date-timeNo field description declared.
partner_endpoint_referencerequiredstringminLength 1 · maxLength 160No field description declared.
key_idrequiredstringminLength 1 · maxLength 96No field description declared.
body_sha256requiredstringpattern ^[a-f0-9]{64}$No field description declared.
attemptrequiredintegerminimum 1 · maximum 32No field description declared.
staterequiredstringPENDING | IN_FLIGHT | DELIVERED | RETRY_SCHEDULED | +1No field description declared.
next_attempt_atoptionalstringformat date-timeNo field description declared.
delivered_atoptionalstringformat date-timeNo field description declared.
safetyrequiredobjectNo field description declared.
Full payload schema
urn:certarail:schema:event:certarail.checkout.partner-delivery.v1json-schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:certarail:schema:event:certarail.checkout.partner-delivery.v1",
  "title": "CertaRail checkout partner delivery event v1",
  "description": "Durable, PII-minimized partner-webhook delivery evidence. The exact body bytes are replayed for retries; URLs and signing secrets are deliberately excluded.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "delivery_event_id",
    "event_type",
    "event_version",
    "tenant_id",
    "aggregate_id",
    "correlation_id",
    "occurred_at",
    "partner_endpoint_reference",
    "key_id",
    "body_sha256",
    "attempt",
    "state",
    "safety"
  ],
  "properties": {
    "delivery_event_id": {
      "type": "string",
      "pattern": "^webhook_evt_[A-Za-z0-9._:/-]{1,111}$"
    },
    "event_type": {
      "const": "certarail.checkout.partner-delivery.v1"
    },
    "event_version": {
      "const": 1
    },
    "tenant_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "aggregate_id": {
      "type": "string",
      "pattern": "^(checkout_|deal_)[A-Za-z0-9._:/-]{1,120}$"
    },
    "correlation_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "occurred_at": {
      "type": "string",
      "format": "date-time"
    },
    "partner_endpoint_reference": {
      "type": "string",
      "minLength": 1,
      "maxLength": 160
    },
    "key_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 96
    },
    "body_sha256": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$"
    },
    "attempt": {
      "type": "integer",
      "minimum": 1,
      "maximum": 32
    },
    "state": {
      "enum": [
        "PENDING",
        "IN_FLIGHT",
        "DELIVERED",
        "RETRY_SCHEDULED",
        "QUARANTINED"
      ]
    },
    "next_attempt_at": {
      "type": "string",
      "format": "date-time"
    },
    "delivered_at": {
      "type": "string",
      "format": "date-time"
    },
    "safety": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "environment",
        "execution_mode",
        "external_calls",
        "monetary",
        "production_enforceable"
      ],
      "properties": {
        "environment": {
          "const": "SANDBOX"
        },
        "execution_mode": {
          "const": "SANDBOX"
        },
        "external_calls": {
          "const": 0
        },
        "monetary": {
          "const": false
        },
        "production_enforceable": {
          "const": false
        }
      }
    }
  }
}

Example

Schema-valid synthetic payload

Generated from the canonical JSON Schema for documentation. It is not a broker record, provider receipt, or runtime evidence.

certarail.checkout.partner-delivery.v1 examplejson
{
  "delivery_event_id": "webhook_evt_A",
  "event_type": "certarail.checkout.partner-delivery.v1",
  "event_version": 1,
  "tenant_id": "tenant_example",
  "aggregate_id": "checkout_A",
  "correlation_id": "correlation_example",
  "occurred_at": "2026-01-01T00:00:00Z",
  "partner_endpoint_reference": "example",
  "key_id": "key_example",
  "body_sha256": "0000000000000000000000000000000000000000000000000000000000000000",
  "attempt": 1,
  "state": "PENDING",
  "safety": {
    "environment": "SANDBOX",
    "execution_mode": "SANDBOX",
    "external_calls": 0,
    "monetary": false,
    "production_enforceable": false
  }
}

Correlation fields

Identifiers and trace context

Identifier, reference, trace, and Kafka-key fields are listed from the canonical schemas and message bindings.

Correlation candidates derived from payload, headers, and Kafka binding.
LocationFieldRequirementMeaning
payloadpayload.delivery_event_idrequiredIdentifier declared without a field description.
payloadpayload.tenant_idrequiredIdentifier declared without a field description.
payloadpayload.aggregate_idrequiredIdentifier declared without a field description.
payloadpayload.correlation_idrequiredIdentifier declared without a field description.
payloadpayload.partner_endpoint_referencerequiredIdentifier declared without a field description.
payloadpayload.key_idrequiredIdentifier declared without a field description.
headerheaders.certarail-event-idrequiredStable consumer deduplication identifier; equal to payload event_id.
headerheaders.traceparentoptionalOptional W3C Trace Context for the Kafka publisher attempt.
headerheaders.tracestateoptionalOptional W3C Trace Context tracestate value; omitted when empty.
kafka-keymessage.keydeclaredTenant-scoped aggregate ordering key; never an endpoint or signing secret.

Ordering semantics

contract-only

A Kafka key schema is documented for compatibility, but runtime ordering is not claimed while publication is NOT_PRODUCED.

Tenant-scoped aggregate ordering key; never an endpoint or signing secret.

Retry semantics

not-applicable

Kafka retry and redelivery semantics do not apply while the canonical publication state is NOT_PRODUCED. Other transport retries are separate contracts.

Как читать consumer contract

  • Producer и Consumer показывают только send/receive operations, объявленные в canonical AsyncAPI; пустое состояние не заменяется предположением об имени сервиса.
  • Correlation fields включают идентификаторы, references, trace headers и Kafka key, реально присутствующие в schema и message bindings.
  • Ordering и retry читаются отдельно для каждой версии события: schema-only запись не превращается в published event.
  • Synthetic example валидируется по canonical JSON Schema, но не является broker record или доказательством внешнего эффекта.

At-least-once delivery

Проектируйте обработчик так, будто одна и та же запись придёт повторно: producer гарантирует durable publication, но не end-to-end exactly-once между PostgreSQL, Kafka и состоянием consumer.

Факт и outbox envelope фиксируются одной PostgreSQL-транзакцией, после чего отдельный publisher отправляет запись в Kafka. API не ждёт broker delivery и не откатывает уже committed факт при недоступности Kafka.

Главное окно дубликата возникает, когда Kafka ACK уже получен, а отметка PUBLISHED в PostgreSQL не зафиксирована. После lease recovery publisher имеет право отправить тот же immutable envelope и event_id ещё раз.

  1. Считать event_id обязательным ключом обработки

    Сверьте certarail-event-id с payload event_id и не выполняйте domain effect до успешной durable dedupe-проверки.

  2. Фиксировать результат до offset

    Сначала атомарно сохраните inbox и локальный effect, затем commit Kafka offset. Обратный порядок создаёт окно потери.

  3. Ожидать повтор после любого ambiguous outcome

    Timeout, rebalance или crash после database commit должны сходиться через inbox, а не запускать effect повторно.

  4. Сохранять порядок только в declared scope

    Порядок существует для одного Kafka key внутри topic; между keys, partitions и topics глобального порядка нет.

Окна отказа и безопасная реакция consumer
Окно отказаЧто может произойтиОбязательная реакция
До Kafka ACKProducer повторит immutable записьDedupe по event_id до effect
После ACK, до PUBLISHEDТа же запись может быть опубликована повторноСверить сохранённый payload digest и вернуть уже committed результат
После consumer DB commit, до offset commitKafka повторно отдаст записьInbox завершает повтор без второго effect
Offset committed до local effectЗапись может быть потеряна для consumerТакой порядок запрещён
Exactly-once не заявлен
Idempotent Kafka producer уменьшает broker duplicates, но не делает atomic downstream side effect. Публичный контракт остаётся at-least-once + durable consumer dedupe.

Dedupe

Dedupe — это durable бизнес-инвариант consumer, а не in-memory cache: повтор должен распознаваться после restart, rebalance, redeploy и восстановления offset.

  • Используйте payload event_id как dedupe key; header certarail-event-id обязан совпадать с ним. Kafka partition/offset — позиция доставки, а не business identity.
  • Храните event_type и SHA-256 точных payload bytes рядом с event_id. Один event_id с другим type или payload SHA-256 — conflict, а не допустимый duplicate.
  • Уникальность задавайте в durable store в scope конкретного consumer: например, UNIQUE (consumer_name, event_id). Не полагайтесь на срок жизни pod или process.
  • Duplicate с тем же digest возвращает уже committed outcome без повторного side effect. Duplicate conflict переводится в quarantine и поднимает alert.
  • tenant_id нужен для tenant-local state и audit, но не заменяет authenticated service identity, Kafka ACL или authorization boundary.
Решение dedupe до domain processing
Inbox stateDigest/typeДействие
Записи нетВалидныСоздать RECEIVED и продолжить обработку
PROCESSEDСовпадаютПодтвердить duplicate без повторного effect
RECEIVEDСовпадаютЗаблокировать строку и завершить либо восстановить обработку
ЛюбоеНе совпадаютConflict: quarantine, alert, offset не продвигать без durable disposition
Consumer dedupe не поставляется CertaRail
Canonical producer публикует stable event_id. Durable dedupe store и его backup/restore остаются ответственностью каждой consumer-команды и должны быть проверены до активации.

Inbox pattern

Inbox связывает получение Kafka record, проверку duplicate и локальный state transition в одной транзакции; commit offset остаётся последним подтверждением доставки.

  1. Проверить envelope до domain effect

    Проверьте обязательные headers, совпадение event_id/event_type, размер и exact JSON Schema. Invalid record ещё не считается обработанным.

  2. Вставить или заблокировать inbox row

    UNIQUE key сериализует конкурентные повторы; существующая row требует сравнения type и payload digest.

  3. Выполнить local effect атомарно

    Projection, journal или durable downstream command фиксируются в одной транзакции с переходом inbox в PROCESSED и processed_at.

  4. Подтвердить offset после commit

    Если процесс упадёт между database commit и offset commit, повтор безопасно завершится через PROCESSED row.

Consumer-owned reference pattern — не готовая migrationsql
-- Reference pattern owned by one consumer service.
CREATE TABLE consumer_inbox (
  consumer_name text NOT NULL,
  event_id text NOT NULL,
  tenant_id text NOT NULL,
  event_type text NOT NULL,
  payload_sha256 char(64) NOT NULL,
  status text NOT NULL CHECK (status IN ('RECEIVED', 'PROCESSED', 'QUARANTINED')),
  received_at timestamptz NOT NULL,
  processed_at timestamptz,
  quarantine_reason_code text,
  PRIMARY KEY (consumer_name, event_id)
);

BEGIN;

-- Insert RECEIVED, or lock the existing event_id row.
-- A duplicate is safe only when event_type and payload_sha256 still match.
-- Validate and apply the local projection or side effect in this transaction.
-- Set status='PROCESSED' and processed_at before COMMIT.

COMMIT;

-- Commit the Kafka offset only after the database commit succeeds.
ЕЩЁ НЕ РЕАЛИЗОВАНО: reference consumer runtime
Reference consumer runtime в репозитории не реализован. DDL показывает обязательные invariants, но не поставляет готовый consumer, inbox migration или offset coordinator; их нельзя считать активированными по наличию этого guide.

Replay

Replay — контролируемое повторное чтение immutable broker history; он не должен создавать новый business fact, менять event_id или обходить исходные authorization и idempotency rules.

  1. Зафиксировать replay manifest

    Запишите reason, ticket, owner, source topic, partitions, start/end offsets, schema set, consumer build и ожидаемый результат.

  2. Проверить на shadow consumer group

    Сначала используйте shadow consumer group с отключёнными внешними effects либо с тем же durable idempotency boundary.

  3. Сохранить исходную identity

    Повторно обрабатывайте те же bytes, key, event_type и тот же event_id; не перепубликовывайте факт как новое событие.

  4. Вести checkpoint по partition

    Фиксируйте последний verified offset и счётчики new, duplicate, quarantined и failed для возобновляемого replay.

  5. Выполнить reconciliation

    Сравните входные event IDs/digests, inbox outcomes и rebuilt projection до переключения рабочего consumer group.

Разные операции, которые нельзя называть одним replay
ОперацияНазначениеIdentity rule
Broker offset replayПовторно прочитать retained Kafka recordsИсходные event_id и bytes неизменны
Projection rebuildПостроить новую projection из историиНовый consumer scope, та же event identity
Producer outbox REQUEUEСнять конкретный quarantined publish после remediationИсходный envelope и event_id неизменны
Не сбрасывайте рабочий group без manifest
Offset reset без bounded range, checkpoint и reconciliation может повторить внешний effect или скрыть пропуск. Production replay требует approval и recovery evidence.

DLQ

DLQ хранит terminal processing failure отдельно от обычного потока, но не заменяет inbox, quarantine review или исправление причины; consumer DLQ в CertaRail ЕЩЁ НЕ РЕАЛИЗОВАНО.

  • Записывайте original topic, partition, offset, key digest, event_id, event_type, schema identity, consumer name/build и first/last failure time.
  • Храните bounded error code/class и payload digest. Raw payload допускается только по утверждённой data classification; секреты, credentials и URL с token запрещены.
  • DLQ record получает собственную delivery identity, но сохраняет ссылку на исходный event_id. Повтор одного DLQ write также должен дедуплицироваться.
  • Commit source offset разрешён только после durable DLQ/quarantine disposition. Если запись terminal evidence не подтверждена, offset остаётся непроведённым.
  • Re-drive из DLQ проходит через тот же inbox и idempotent processing, а не вызывает отдельный обходной handler.
Классификация consumer failure
КлассDispositionАвтоматический retry
Transient dependencyОставить на source offsetBounded backoff
Invalid/poison payloadQuarantine или DLQ evidenceНет
Unknown schema/event typeQuarantine до compatibility decisionНет
Duplicate digest conflictSecurity quarantine + alertНет
Application bugPause partition, исправить, controlled replayНет до fix
ЕЩЁ НЕ РЕАЛИЗОВАНО: Kafka DLQ topics и re-drive worker
Текущий producer имеет PostgreSQL quarantine, но не объявляет consumer DLQ channel. Имена topics, ACL, retention и operator workflow должны появиться в canonical AsyncAPI до активации.

Quarantine

Quarantine останавливает автоматическую обработку записи с permanent, integrity или compatibility failure и сохраняет evidence для отдельного operator-owned решения.

  1. Остановить только затронутый stream

    Quarantined producer head блокирует только свой key; unrelated keys продолжают публиковаться. Не расширяйте outage без evidence.

  2. Осмотреть immutable episode

    Сверьте event identity, topic/key, attempts, last error и expected digest, не изменяя payload или queue state.

  3. Устранить root cause

    Schema/configuration/transport defect исправляется и проверяется до любого REQUEUE. Retry не является remediation.

  4. Применить maker-checker

    Разные maker и checker фиксируют reason, ticket и expected digest для конкретного quarantine episode.

  5. Requeue с тем же envelope

    Audited REQUEUE сбрасывает delivery attempts, но не меняет event_id, payload, created_at или ordering position.

Что уже реализовано и что принадлежит consumer
КонтурСостояниеГраница
Producer outboxРЕАЛИЗОВАНОQUARANTINED, immutable inspection digest, maker-checker REQUEUE journal
Consumer quarantine storeЕЩЁ НЕ РЕАЛИЗОВАНОConsumer должен реализовать durable record, ACL, retention и re-drive
Quarantine не равна удалению или skip
Запись остаётся предметом reconciliation. Нельзя менять offset, payload либо expected digest вручную и затем заявлять восстановление.

Schema compatibility

Consumer выбирает schema по payload event_type, проверяет header identity и поддерживает только явно объявленные версии; transport header version не заменяет payload version.

  1. Dispatch по event_type

    Сначала сопоставьте certarail-event-type и payload event_type, затем выберите exact JSON Schema. Несовпадение является integrity failure.

  2. Поддерживать overlap версий

    На общем trading topic одновременно встречаются v1 и v2. Consumer обязан валидировать обе версии и дедуплицировать один event_id между ними.

  3. Считать schema closed

    Published payload schemas используют additionalProperties=false: unknown field не является автоматически совместимым additive change.

  4. Разворачивать consumer первым

    Новая версия сначала проходит compatibility CI и canary/shadow consumer, затем producer emission и только после окна поддержки удаляется старая версия.

  5. Quarantine неизвестное

    Unknown event_type/schema не пропускается, не преобразуется эвристически и не получает committed offset без durable disposition.

Правила изменения payload contract
ИзменениеОценкаБезопасный rollout
Новое optional поле при additionalProperties=falseНе совместимо со старым strict consumer автоматическиConsumer-first coordination либо новая event version
Удаление/required/type/enum changeBreakingНовая event version; при смене partition contract — новый topic
Новый optional headerСовместимо при ignore-unknown policyНе использовать header как auth/tenant evidence
Partition count/key algorithm changeOrdering-breakingНовый versioned topic и controlled cutover
Schema-only candidate не является rollout evidence
Compatibility metadata у непубликуемого события описывает design intent. Только CI, consumer canary и observed broker records подтверждают готовность конкретного producer/consumer pair.

Consumer recovery

Recovery начинается с сохранения evidence и ограничения blast radius: сначала pause и classification, затем исправление, bounded replay и reconciliation, а не blind offset reset.

  1. Зафиксировать incident checkpoint

    Сохраните group, topic, partition, current/committed offset, event_id/digest, lag, inbox state, consumer build и время первого отказа.

  2. Остановить затронутые partitions

    Pause предотвращает retry storm. Не commit failed offset и не останавливайте unrelated partitions без причины.

  3. Классифицировать отказ

    Разделите transient dependency, poison/schema, duplicate conflict, code regression и потерю dedupe store.

  4. Восстановить durable prerequisites

    Верните database/inbox, примените совместимый build и проверьте backup/restore до чтения retained history.

  5. Прогнать bounded shadow replay

    Начните с сохранённого checkpoint, отключите неподтверждённые external effects и сравните outcomes.

  6. Выполнить reconciliation

    Сведите counts, event IDs/digests, duplicates, quarantined records и итоговую projection с ожидаемым manifest.

  7. Возобновить и наблюдать

    Resume только после approval; контролируйте lag, error rate, inbox conflicts и offset progress до закрытия incident.

Recovery rule по типу отказа
СимптомДо исправленияOffset rule
Dependency unavailablePause + bounded retryНе commit failed record
Poison/unknown schemaDurable quarantine + alertCommit только после disposition evidence
Consumer bugDeploy compatible fix + shadow replayВернуться к recorded checkpoint
Inbox потерян/повреждёнRestore и verify dedupe stateНе replay до восстановления idempotency boundary
Не пропускать record молча
Skip или offset jump без durable quarantine/DLQ evidence превращает operational incident в необнаружимую потерю данных.

Retention

Local broker хранит Kafka log 168 часов и работает с RF=1; это developer profile, а не утверждённая production retention, availability или disaster-recovery policy.

  • Consumer inbox хранится не меньше максимального replay window и срока, в котором Kafka record либо архив ещё могут быть повторно прочитаны.
  • Payload retention определяется data classification и purpose limitation. Для долгой dedupe достаточно event_id, event_type, payload digest и outcome, если raw payload больше не нужен.
  • DLQ и quarantine имеют отдельные access, retention, deletion и legal-hold правила; их нельзя очищать вместе с обычным processed inbox без incident closure.
  • Offset retention, broker log retention, inbox retention и projection backup должны образовывать один проверенный recovery window.
  • Опубликованные producer outbox rows пока не имеют автоматического purge/archive workflow. ЕЩЁ НЕ РЕАЛИЗОВАНО: approved outbox retention, legal hold и deletion evidence.
Retention layers и минимальная проверка
LayerТекущее evidenceProduction decision
Kafka logLocal default: 168 часов, single node RF=1Capacity, RF, archive и restore drill
Consumer inboxConsumer-owned; runtime отсутствуетНе короче replay/recovery window
DLQ/quarantineProducer quarantine есть; consumer store отсутствуетInvestigation, legal hold, deletion approval
Producer outboxDurable rows без автоматического purgePartition/archive/purge и immutable evidence policy
ЕЩЁ НЕ РЕАЛИЗОВАНО: production retention policy
Нельзя переносить 168-часовой Local default в договор, SLA или privacy schedule. До активации нужны владельцы, data classes, сроки, legal hold, purge evidence и restore tests.

Idempotent processing

Idempotent processing означает, что повтор record приводит к тому же durable outcome: atomic inbox и local effect фиксируются вместе, а внешняя неопределённость восстанавливается lookup/reconciliation.

  1. Валидировать до mutation

    Envelope, event type, schema, tenant scope и digest проходят fail-closed проверку до начала domain changes.

  2. Открыть одну local transaction

    Inbox row блокируется либо создаётся тем же commit, что projection, journal или durable downstream command.

  3. Сделать effect естественно идемпотентным

    Используйте unique business key, compare-and-set/version или compensating journal; не полагайтесь только на handler branch.

  4. Связать внешний command с identity

    Если egress разрешён, передавайте stable provider idempotency key, производный от consumer и event_id, и храните request digest.

  5. Восстановить ambiguous result

    Timeout после submit переводится в UNKNOWN: выполняйте lookup/reconciliation, а не повтор исходного side effect.

  6. Подтвердить delivery последним

    Commit Kafka offset выполняется только после durable local commit; crash до offset commit создаёт безопасный duplicate.

Processing order — language-neutral referencetext
receive(record)
  -> validate headers, event_type and exact payload schema
  -> begin local database transaction
  -> insert or lock inbox(event_id, payload_sha256)
  -> if PROCESSED with the same digest: commit and acknowledge duplicate
  -> if the digest conflicts: quarantine and alert
  -> apply projection / create durable command with a stable idempotency key
  -> mark inbox PROCESSED and commit
  -> commit Kafka offset

If an external response is ambiguous, persist UNKNOWN and reconcile by lookup;
never submit the original external side effect again merely because Kafka redelivered.
Idempotent не означает retry любого effect
Если downstream не предоставляет idempotency/lookup contract, автоматический внешний retry должен оставаться отключённым до отдельной recovery design и approval.

TLS transport

Production listeners шифруют и взаимно проверяют каждый client, inter-broker и controller channel; plaintext listener не должен существовать ни в workload network, ни как аварийный обход.

  • Разрешены TLS 1.3 и, только для утверждённой совместимости, TLS 1.2; более ранние версии и weak cipher suites запрещены. TLS 1.3 является preferred profile.
  • Hostname verification и SAN обязательны. IP, DNS name и service discovery identity должны совпадать с сертификатом; insecure skip verify и trust-all callback запрещены.
  • Client-to-broker, inter-broker, controller quorum, admin tooling, Schema Registry и replication links получают отдельные reviewed trust boundaries. Шифрование только внешнего listener недостаточно.
  • Private keys поступают из bank-managed KMS/HSM или workload secret delivery, никогда не входят в image, environment dump, URL, log или replay manifest.
  • Certificate rotation поддерживает перекрытие current/next CA и client certificates, имеет expiry alerts и проверяется без downtime до каждой Production activation.
  • Egress policy разрешает workload только broker bootstrap/DNS, identity provider и approved telemetry endpoints; произвольный broker address запрещён.
Listener security boundary
ChannelTarget protocolMandatory verification
Application workloadSASL_SSL или SSL/mTLSCA, SAN, principal, revocation/expiry
Broker replicationTLS-authenticated private listenerDedicated broker principal and SAN
KRaft controllerTLS-authenticated controller listenerDedicated controller principal and network segment
Admin planemTLS-only isolated listenerNamed operator/service principal and approval
ТРЕБУЕТ АКТИВАЦИИ: Production Kafka identity
Local Compose intentionally advertises PLAINTEXT. Client TLS and SASL_SSL/SCRAM-SHA-512 are implemented with CA/hostname verification and negative tests for Hosted Sandbox. Production OAUTHBEARER, client-certificate mTLS and automated credential lifecycle remain unimplemented; Production outbox remains fail-closed.

SASL, OAuth и mTLS

Каждый process получает собственную workload identity; один credential нельзя разделять между producer, consumer, operator или tenant, а network reachability никогда не заменяет authentication.

  • Target Production hosted profile: SASL_SSL + OAUTHBEARER с machine-to-machine token от approved identity provider. Token short-lived, не дольше 10 минут, audience привязан к Kafka cluster, scope — к одной workload role.
  • Kafka unsecured OAUTHBEARER запрещён: production validation проверяет issuer, audience, signature, expiry, not-before и principal mapping; token не попадает в logs, metrics, traces или command history.
  • On-prem alternative: SSL/mTLS на отдельном listener с однозначным certificate-to-principal mapping. Он не включается как silent fallback при отказе OAuth.
  • Admin и break-glass используют отдельный mTLS principal, hardware-backed key, time-bounded elevation и maker-checker approval. Application identity не получает admin ACL.
  • SASL/PLAIN и static shared password запрещены. SCRAM допускается только как отдельно согласованный exception over TLS с vault rotation и не меняет least-privilege ACL.
  • Token/certificate refresh проходит до expiry; auth failure не переключает client на plaintext, другой listener, другой principal или бесконечный retry.
Approved authentication profiles
Use caseAuthenticationFail-closed rule
Hosted applicationSASL_SSL / OAUTHBEARERValidated short-lived JWT; no unsecured token mode
On-prem applicationSSL / mTLSDedicated listener and deterministic DN/SAN mapping
Broker/controllerDedicated TLS or mTLS identityNo application principal reuse
AdministrationIsolated mTLS + temporary elevationNo standing super-user credential
Один environment — один выбранный profile
OAuth и mTLS являются reviewed deployment profiles, а не цепочкой fallback. Production manifest фиксирует listener, principal mapping, issuer/CA fingerprints и rotation evidence без самих credentials.

Tenant и topic ACL

Kafka ACL применяется к principal, topic, consumer group и admin resource; он не умеет безопасно ограничить tenant по payload field внутри общего shared topic.

  • KRaft StandardAuthorizer работает deny-by-default с allow.everyone.if.no.acl.found=false. Wildcard grants для application principals запрещены; super.users ограничен broker/controller break-glass contour.
  • Outbox principal получает Write и Describe только на approved internal topics плюс минимальный IdempotentWrite, если он требуется producer client. Create, Delete, Alter и AlterConfigs ему запрещены.
  • Consumer principal получает Read/Describe только на назначенные topics и Read/Describe только на собственный consumer group prefix. Один consumer не может присоединиться к group другой команды.
  • Canonical shared topics доступны только внутренним multi-tenant services, которые применяют server-side tenant boundary. Внешнему tenant consumer нельзя дать ACL на shared topic с чужими records.
  • Если bank или tenant получает direct Kafka delivery, создаётся отдельный cluster/namespace либо opaque tenant topic prefix и отдельный group prefix. Opaque tenant alias не содержит названия клиента или PII.
  • Topic creation, partition count, replication, retention, cleanup policy, quotas и ACL управляются IaC через отдельный release principal. Runtime auto-create выключен.
  • Per-principal byte/request quotas берутся из approved load test; unlimited quota или silent throttling без alert и ownership не допускаются.
Minimum ACL matrix
PrincipalTopic rightsGroup/admin rights
Outbox producerWrite + Describe on explicit internal topicsNo group; minimal cluster IdempotentWrite only
Internal consumerRead + Describe on assigned topicsRead + Describe on own group prefix
Tenant delivery consumerRead + Describe on isolated tenant namespaceOwn tenant group prefix only
Release controllerReviewed Create/Alter/DescribeConfigsNo application data consumption
Break-glass adminTime-bound audited elevationMaker-checker and session recording
Record-level tenant ACL не заявлен
tenant_id в payload и hashed partition key помогают domain processing, но не являются Kafka authorization boundary. Direct tenant access требует physically or logically isolated topic namespace, declared production address and negative cross-tenant ACL tests.

Schema Registry

Production registry становится контролируемым каталогом immutable JSON Schema identities; canonical repository остаётся источником review, а registry — promotion и runtime resolution layer.

  • Формат payload остаётся JSON Schema 2020-12. Каждый promoted schema artifact связывается с canonical $id, event_type, major version, Git commit и SHA-256 exact bytes.
  • Subject scope — topic + event_type, чтобы несколько event families или versions на одном topic не делили несовместимую compatibility chain. Key и value subjects разделены.
  • Runtime auto-registration=off. CI validates schema/examples/compatibility, затем release principal регистрирует exact digest; application principal может только read approved schema ID.
  • Producer wire contract содержит immutable schema ID или утверждённый registry framing. До изменения wire format соответствующий binding/header добавляется в canonical AsyncAPI и проходит consumer overlap.
  • Registry endpoint использует TLS, workload authentication, least-privilege ACL и отдельный audit stream. Unknown schema ID или digest mismatch fail closed и отправляет record в quarantine.
  • Metadata database, subjects, compatibility settings и soft-delete state входят в encrypted backup/restore drill. Permanent delete в Production запрещён до data-governance approval.
  • Consumer держит bounded cache уже проверенных schema IDs для краткого registry outage, но не принимает незнакомую schema по inference или latest lookup.
Schema promotion lifecycle
StageActorRequired evidence
ValidateCIJSON Schema validity, examples, references and digest
CompareCI compatibility gateTransitive consumer compatibility report
RegisterRelease principalApproved subject, schema ID and immutable digest
ActivateProducer releaseOld/new consumer matrix and rollback plan
RestorePlatform operatorBackup manifest, recovered IDs and digest equality
ЕЩЁ НЕ РЕАЛИЗОВАНО: Schema Registry integration
Current producer publishes raw canonical JSON and does not resolve or attach a registry schema ID. Registry service, auth, backup, client cache and promotion gate must be implemented and tested before activation.

Compatibility policy

Внутри одного major event subject действует FULL_TRANSITIVE compatibility и consumer-first rollout; любое исключение оформляется как reviewed migration, а не registry override в incident.

  • Удаление или переименование field, изменение type/format, добавление required field, сужение range, изменение enum semantics и изменение event identity считаются breaking.
  • Current payload schemas используют additionalProperties=false. Поэтому даже новый optional field отвергается strict old consumer и не считается автоматически backward-compatible.
  • Breaking evolution получает new major topic и новый subject. Producer overlap публикует обе версии только после capacity, dedupe и reconciliation review; old topic остаётся до подтверждённого consumer migration.
  • Frozen v1 и v2 на одном существующем topic являются documented migration exception: consumer dispatches exact event_type и поддерживает overlap. Этот precedent не разрешает новые mixed-version migrations без approval.
  • Compatibility gate проверяет все исторические promoted schemas, schema-valid examples и current consumer matrix. Registry setting нельзя ослаблять вручную для прохождения release.
  • Enum расширение рассматривается отдельно: consumer должен иметь explicit UNKNOWN/quarantine path либо новая value требует major version. Silent default запрещён.
Change disposition under strict schemas
ChangeDefault decisionRelease path
Description/example onlyCompatible after validationSame subject
Optional field with strict old consumerBreaking in current contractConsumer-first migration or new major
New required fieldBreakingNew major topic and subject
Removed/narrowed field or typeBreakingNew major topic and subject
Enum value or meaning changedBreaking unless UNKNOWN path is provenCompatibility review or new major
Auth/topic/key semantics changedBreaking operational contractNew version plus migration runbook
Registry green не равен end-to-end compatibility
Activation requires parser/validator tests in every registered consumer, old/new replay fixtures and rollback evidence. Schema comparison alone cannot prove application semantics.

Consumer lag

Production monitoring измеряет не только records lag, но и lag age до durable consumer outcome; маленькое число records может скрывать старый или остановленный partition.

  • Для каждого approved consumer group/topic/partition собираются log-end offset, committed offset, records lag, oldest-unprocessed lag age, last successful process time и fetch/error rate.
  • Отдельно наблюдаются paused partition count/reason, rebalance duration/count, assigned partitions, commit failures, inbox conflicts, quarantine/DLQ backlog и schema lookup failures.
  • Alert строится по workload, environment, consumer group, topic и bounded outcome. tenant_id, event_id, partition key, raw error и customer reference не являются metric label.
  • Dashboard показывает max и p95 lag age, total records lag, zero-throughput с растущим end offset, partition skew и связь с producer outbox backlog.
  • Warning открывается до исчерпания freshness SLO; critical page срабатывает при projected SLO breach, stopped consumption, unknown schema или growing quarantine.
  • Planned replay использует отдельный group и annotation, но не скрывает lag рабочего consumer. Silence time-bounded, approved и остаётся в audit evidence.
Lag signals and operator meaning
SignalInterpretationRequired action
records lag growsConsumer throughput below ingressScale or throttle after bottleneck evidence
lag age grows with few recordsOld record is blocked or partition pausedInspect exact partition and disposition
zero fetch + moving log endConsumer stopped or unauthorizedPage owner; verify auth and assignment
rebalance stormUnstable membership or processing timeoutStabilize group before offset changes
quarantine growsIntegrity/schema/application failurePause affected partition and remediate
ЕЩЁ НЕ РЕАЛИЗОВАНО: consumer lag telemetry
Current repository exports producer outbox backlog and publish outcomes, but consumer lag telemetry не экспортируется: reference consumer and consumer lag instruments отсутствуют. Production SLI cannot be claimed until a real group emits and exercises these signals.

Kafka SLO

SLO фиксирует измеряемую границу от committed fact до broker acknowledgement и durable consumer outcome; local alert threshold или successful smoke не становится Production SLO.

  • Окно оценки — rolling 30 days; planned maintenance исключается только по заранее утверждённому change window, а incident silence не удаляет bad events из SLI.
  • Error budget считается раздельно для publication, broker availability и каждого critical consumer. Fast/slow burn alerts имеют owner, page route и linked runbook.
  • Invalid record считается успешным только после durable quarantine disposition в установленный срок; silent skip, manual offset jump или loss исключать из denominator запрещено.
  • Цели активируются только после 30-day baseline, security-on load test, broker/AZ failover и consumer replay drill. До этого это design targets, не измеренная гарантия.
Activation targets; not current measured guarantees
SLITargetMeasurement boundary
Publication freshness99.9% within 60 секундPostgreSQL commit time → Kafka durable ACK
Critical consumer freshness99.9% within 120 секундBroker append time → inbox PROCESSED or durable quarantine
Authorized broker API availability99.95% monthlySuccessful eligible produce/fetch requests
Integrity and silent loss100%; any mismatch is incidentCommitted event set → broker/consumer reconciliation
ТРЕБУЕТ АКТИВАЦИИ: measured SLO
Numbers above are release gates to validate with the selected bank/vendor topology. They are not current service claims, and the existing ten-minute local backlog alert is not evidence that these targets are met.

Production replay procedure

Production replay является maker-checker change-controlled восстановлением exact retained records; он сохраняет event identity, не создаёт новый business fact и не обходит normal consumer authorization.

  1. Открыть replay request

    Maker фиксирует incident/change ticket, reason, data classification, consumer owner и ожидаемый reconciliation result.

  2. Собрать replay manifest

    Зафиксировать immutable source cluster, topic, partitions, start/end offsets, event-count digest, schema snapshot и consumer build digest.

  3. Получить независимый approval

    Checker отличается от maker и подтверждает range, ACL, retention availability, side-effect mode, quota и rollback/stop conditions.

  4. Запустить shadow consumer group

    External effects disabled либо защищены тем же durable idempotency key; рабочие group offsets не изменяются.

  5. Выполнять bounded replay

    Throttle не превышает security-on load-test limit; per-partition checkpoints и new/duplicate/quarantined/failed counters сохраняются.

  6. Остановиться на conflict

    Digest mismatch, unknown schema, authorization drift или unexpected side effect немедленно останавливает affected partition.

  7. Выполнить reconciliation

    Сверить source event set, inbox outcomes, rebuilt projection, quarantine и external-effect journal до любого cutover.

  8. Закрыть change

    Подписать result manifest, сохранить evidence, удалить temporary ACL/group по approved cleanup и зафиксировать остаточный risk.

Replay manifest — values are placeholders, never credentialsyaml
version: certarail.kafka-replay.v1
request_id: replay-<ticket-id>
reason: <approved incident or rebuild reason>
maker: <workload-or-operator-principal>
checker: <different approving principal>
source_cluster: <immutable cluster identity>
consumer:
  name: <consumer-name>
  build_digest: sha256:<image-digest>
  shadow_group: <new-isolated-group>
range:
  topic: <approved-topic>
  partitions:
    - partition: 0
      start_offset: 1200
      end_offset: 1750
schemas:
  registry_snapshot_sha256: <sha256>
effects:
  mode: disabled-or-idempotent
limits:
  max_records_per_second: <load-tested-limit>
evidence:
  expected_event_count: 551
  expected_event_set_sha256: <sha256>
  output_manifest_uri: <approved-evidence-location>
Рабочий offset reset запрещён как первый шаг
Manual group reset without an approved bounded manifest, shadow validation and reconciliation can duplicate effects or hide loss. Break-glass execution still produces the same evidence and post-incident review.

Disaster recovery test

Production topology использует минимум три brokers в независимых failure domains, RF=3, min.insync.replicas=2 и acks=all; DR считается проверенным только после measured restore/failover drill.

  1. Зафиксировать baseline

    Сохранить topic configs, ACL/schema snapshots, cluster IDs, offsets, outbox head, event-set digest, current lag и SLO burn.

  2. Изолировать failure domain

    Quarterly убрать broker/AZ; semiannual изолировать primary cluster/region. Test controller не должен зависеть от затронутого domain.

  3. Проверить protected write behavior

    Writes продолжаются только при required ISR; unclean leader election и silent downgrade durability запрещены.

  4. Promote DR path

    Переключить reviewed bootstrap endpoint, restore Schema Registry and consumer offsets, then rotate cluster authority epoch to prevent split-brain clients.

  5. Drain authoritative backlog

    Outbox publishes committed facts through the new cluster; duplicates converge through stable event_id and inbox.

  6. Reconcile and measure

    Compare source/outbox/broker/inbox event sets and record actual RPO, RTO, lag, duplicates, quarantine and error-budget impact.

  7. Return without split brain

    Old cluster rejoins only after fencing and re-seed; evidence owner signs findings and tracks every failed acceptance item.

DR targets requiring drill evidence
Failure domainTarget RPOTarget RTOFrequency
Single broker or AZ0 acknowledged records≤ 5 minQuarterly and after topology change
Primary Kafka region≤ 5 min replicated log; PostgreSQL facts remain authoritative≤ 60 minSemiannual
Schema Registry≤ 5 min metadata≤ 60 minQuarterly restore
Consumer offsets/checkpoints≤ 5 min≤ 60 min plus replayQuarterly restore
Local broker не проверяет DR
Compose runs one broker with RF=1 and min ISR=1. Multi-AZ quorum, cross-region replication, registry/offset restore and fenced failover remain unimplemented activation gates.

Audit evidence

Каждая security, schema, replay и recovery операция оставляет privacy-minimised, tamper-evident manifest, позволяющий независимо восстановить кто, что, когда и по какому approval изменил.

  • Release evidence включает broker/controller version and image digest, cluster ID, listener/TLS policy, topic configs, replication/ISR state, quotas и payload-free health result.
  • Access evidence включает normalized principal map, ACL snapshot/diff, denied cross-tenant canary, group ownership, temporary elevation expiry и maker-checker approval.
  • Schema evidence включает registry subject/schema ID, canonical $id, schema SHA-256, FULL_TRANSITIVE result, example validation, consumer matrix и promotion actor.
  • Operational evidence включает lag/SLO report, alert delivery drill, replay manifest/result, DR test timeline, measured RPO/RTO и reconciliation event-set digest.
  • Credentials, OAuth token, private key, truststore password, raw payload, tenant PII и unrestricted error text не входят в evidence или logs.
  • Manifest canonicalized, hashed with SHA-256, signed by approved workload/operator identity, timestamped and anchored in bank-managed WORM/SIEM storage with retention/legal-hold policy.
  • Evidence has an owner, classification, retention_until, verification status and immutable link to incident/change/release. Missing artifact blocks Production activation or closes drill as failed.
Minimum activation evidence pack
Evidence classRequired artifactsFail gate
Transport and identityTLS scan, CA/principal map, rotation and negative auth testsPlaintext or fallback path exists
AuthorizationACL snapshot/diff, cross-tenant deny and admin separationWildcard or unowned grant
SchemaRegistry IDs/digests and schema compatibility reportUnregistered or incompatible schema
ReliabilityLag dashboard, SLO burn and alert receiver drillNo consumer freshness signal
RecoveryReplay/DR manifests, event-set reconciliation and actual RPO/RTOLoss, split brain or unresolved mismatch
Design artifact не подтверждает готовность Production
This contract records required controls and evidence. Production status remains blocked until deployed infrastructure, client implementation, negative security tests, SLO baseline, replay and DR drills produce verifiable artifacts.

Production activation boundary

Статус в каталоге берётся из operation и x-certarail runtime/publication metadata. Поэтому наличие schema, topic или Kafka key не заявляет существование publisher, consumer либо доставки.

Versioned production design фиксирует обязательные controls, SLO targets, replay/DR procedures и evidence pack. Он не меняет Local server в canonical AsyncAPI и не включает отсутствующие runtime integrations.

Production Kafka остаётся заблокированным
До активации должны существовать client TLS/SASL, workload identity, tenant-safe ACL, Schema Registry, consumer lag telemetry, measured SLO, multi-broker/DR topology и signed audit evidence. Local publish и design artifact сами по себе этого не подтверждают.

Нашли неточность?

Участники private repository могут предложить правку через reviewed pull request. Остальные пользователи — отправить техническое сообщение без credentials и чувствительных данных.