Core System Architecture Part 75 — Anonymous Network Event Streaming, Message Bus, Durable Queues, Internal Pub/Sub & Distributed Workflow Architecture

SIAR — Secure, Local-First, Multi-Transport Communication Platform

Series: Core System Architecture
Part: 75
Status: Architecture specification
Primary language: Rust
Builds on: Core System Architecture Parts 4–9, 30–33, 46–50, 61–74

Primary purpose: define SIAR's internal event-streaming, durable queue, message-bus, pub/sub, distributed workflow, command/event separation, ordering, retry, backpressure, saga, timeout, dead-letter, observability, and failure-isolation architecture across clients, providers, control-plane services, tenants, federation domains, and long-running workflows.


1. Purpose

Distributed systems need asynchronous coordination.

Without a disciplined event architecture, components become coupled through:

direct RPC chains
shared database polling
ad-hoc retry loops
global mutable state
implicit ordering assumptions

These patterns are fragile.

The governing principle is:

SIAR asynchronous coordination must be explicit, durable where required, idempotent, bounded, privacy-safe, and domain-scoped.


2. Architectural Position

Domain State Change
       │
       ▼
Transactional Outbox
       │
       ▼
Durable Event Bus / Queue
       │
  ┌────┼────┐
  │    │    │
Consumer A Consumer B Workflow Engine
  │    │    │
  └────┼────┘
       ▼
Idempotent Side Effects

3. Core Separation

Keep distinct:

command
domain event
integration event
queue job
notification
workflow state
audit event
telemetry event

4. Non-Goals

Part 75 does not create:

one global event bus carrying every user action
event sourcing for every domain
unbounded queues
best-effort delivery for critical state transitions
cross-tenant event fanout by default

5. Event Classes

#![allow(unused)]
fn main() {
pub enum EventClass {
    Domain,
    Integration,
    Workflow,
    Audit,
    Operational,
}
}

6. Domain Event

Represents completed domain fact.

Example:

MessagePersisted
DeviceRevoked
TenantProvisioned

7. Integration Event

Stable boundary event between services/domains.


8. Workflow Event

Progress/completion signal in long-running process.


9. Audit Event

Admin/governance/infrastructure action.


10. Operational Event

Health/capacity/deployment state.


11. Commands

Commands express intent.


12. Command Examples

SendMessage
RotateProviderKey
ProvisionTenant
DrainRelay

13. Hard Rule

Events are facts; commands are requests.

Do not conflate.


14. Event Envelope

#![allow(unused)]
fn main() {
pub struct EventEnvelope<T> {
    pub event_id: EventId,
    pub event_type: EventTypeId,
    pub schema_version: EventSchemaVersion,
    pub occurred_at: EventTime,
    pub partition_key: PartitionKey,
    pub payload: T,
}
}

15. Event ID

Random or monotonic per domain, but not global user identity.


16. No Global User-Correlatable Event ID

Hard rule.


17. Event Schema Version

Independent from DB/API/protocol versions.


18. Event Time

Coarsened where privacy-sensitive.


19. Partition Key

Domain-scoped.


20. Examples

conversation-local key
tenant-local key
federation-domain key
workflow key

21. No Global Person Key

Hard rule.


22. Command Envelope

#![allow(unused)]
fn main() {
pub struct CommandEnvelope<T> {
    pub command_id: CommandId,
    pub command_type: CommandTypeId,
    pub idempotency_key: IdempotencyKey,
    pub payload: T,
}
}

23. Command Idempotency

Mandatory for retried commands.


24. Command Result

#![allow(unused)]
fn main() {
pub enum CommandResult<T> {
    Accepted,
    Completed(T),
    Rejected(CommandRejection),
}
}

25. Event Bus Scope

Internal only.


26. No Direct End-User Subscription

Hard rule.


27. Client Event Bus

Local process/device scope.


28. Server Event Bus

Infrastructure/service scope.


29. Federation Event Bus

Not shared globally.


30. Multi-Tenant Bus

Tenant-isolated partitions/namespaces.


31. Delivery Semantics

Preferred default:

at-least-once

32. Why

Exactly-once across distributed systems is expensive/fragile.


33. Hard Rule

Do not claim globally exactly-once delivery.


34. Exactly-Once Effect

Achieved via:

at-least-once delivery
+
idempotent consumer
+
transactional state change

35. Consumer Idempotency

Required.


36. Consumer Receipt

#![allow(unused)]
fn main() {
pub struct ConsumerReceipt {
    pub consumer: ConsumerId,
    pub event: EventId,
    pub status: ConsumerApplyStatus,
}
}

37. Consumer State

#![allow(unused)]
fn main() {
pub enum ConsumerApplyStatus {
    Applied,
    Duplicate,
    Rejected,
    Deferred,
}
}

38. Transactional Consumer

Flow:

begin tx
→ check event_id
→ apply domain change
→ persist consumer receipt
→ commit

39. ACK After Commit

Hard rule.


40. Transactional Outbox

Part 74.


41. Publisher Flow

begin tx
→ domain state write
→ outbox event write
→ commit
→ async dispatcher publishes

42. Hard Rule

No publish-before-domain-commit for durable domain events.


43. Outbox Record

#![allow(unused)]
fn main() {
pub struct OutboxEventRecord {
    pub event: EventEnvelope<Bytes>,
    pub publish_state: OutboxPublishState,
}
}

44. Publish State

#![allow(unused)]
fn main() {
pub enum OutboxPublishState {
    Pending,
    Publishing,
    Published,
    FailedRetryable,
    FailedPermanent,
}
}

45. Crash Recovery

Pending/Publishing safely retried.


46. Inbox Pattern

Consumer dedup/inbox table.


47. Inbox Record

#![allow(unused)]
fn main() {
pub struct InboxEventRecord {
    pub event_id: EventId,
    pub consumer: ConsumerId,
    pub received_at: EventTime,
}
}

48. No Duplicate Side Effect

Hard rule.


49. Durable Queue

For work requiring eventual execution.


50. Queue Job

#![allow(unused)]
fn main() {
pub struct QueueJob<T> {
    pub job_id: JobId,
    pub job_type: JobTypeId,
    pub priority: QueuePriority,
    pub payload: T,
    pub retry: RetryPolicy,
}
}

51. Priority Classes

Reuse:

Emergency
SecurityControl
Realtime
Interactive
Background
Bulk

52. Queue Priority

Must not bypass privacy/security.


53. Queue Budget

Bounded.


54. No Unbounded Durable Queue

Hard rule.


55. Queue Capacity Dimensions

items
bytes
age

56. Queue Limit

#![allow(unused)]
fn main() {
pub struct QueueCapacity {
    pub max_items: u64,
    pub max_bytes: u64,
    pub max_age: Option<Duration>,
}
}

57. Backpressure

Upstream producer must slow/reject.


58. No Infinite Retry Growth

Hard rule.


59. Retry Policy

#![allow(unused)]
fn main() {
pub struct RetryPolicy {
    pub max_attempts: u32,
    pub base_delay: Duration,
    pub max_delay: Duration,
    pub jitter: bool,
}
}

60. Exponential Backoff

Recommended.


61. Jitter

Required for distributed retry.


62. Retry Classification

#![allow(unused)]
fn main() {
pub enum RetryClass {
    Never,
    ImmediateLimited,
    Exponential,
    Scheduled,
}
}

63. Retryable Error

Explicit.


64. No Retry On Permanent Validation Error

Hard rule.


65. Poison Message

Malformed or repeatedly failing job.


66. Dead-Letter Queue

Use carefully.


67. Dead-Letter Record

#![allow(unused)]
fn main() {
pub struct DeadLetterRecord {
    pub job_id: JobId,
    pub reason: FailureClass,
    pub failure_count: u32,
}
}

68. Privacy

No sensitive payload copied indefinitely.


69. Hard Rule

Dead-letter storage must not become permanent sensitive-data archive.


70. Dead-Letter Payload

Prefer:

opaque reference
digest
minimal failure metadata

71. Quarantine Queue

Alternative to full payload DLQ.


72. Workflow

Long-running multi-step process.


73. Examples

tenant provisioning
provider migration
key rotation
account deletion
backup restore
release rollout

74. Workflow State

#![allow(unused)]
fn main() {
pub enum WorkflowState {
    Pending,
    Running,
    Waiting,
    Compensating,
    Completed,
    Failed,
    Cancelled,
}
}

75. Workflow ID

Scoped.


76. No Global User Workflow ID

Hard rule.


77. Workflow Step

#![allow(unused)]
fn main() {
pub struct WorkflowStep {
    pub step_id: WorkflowStepId,
    pub command: CommandEnvelope<Bytes>,
    pub compensation: Option<CommandEnvelope<Bytes>>,
}
}

78. Saga

Preferred for cross-service operations.


79. Saga Principle

Local transactions + durable events + compensation.


80. No Distributed 2PC Requirement

Hard rule.


81. Workflow Journal

Durable.


82. Workflow Journal Entry

#![allow(unused)]
fn main() {
pub struct WorkflowJournalEntry {
    pub workflow: WorkflowId,
    pub step: WorkflowStepId,
    pub state: WorkflowStepState,
}
}

83. Step State

#![allow(unused)]
fn main() {
pub enum WorkflowStepState {
    Pending,
    Dispatched,
    Applied,
    Compensated,
    Failed,
}
}

84. Idempotent Steps

Mandatory.


85. Compensation

Best-effort, domain-safe.


86. Compensation Is Not Rollback

Hard truth.


87. Timeout

Explicit per step.


88. Workflow Timeout

#![allow(unused)]
fn main() {
pub struct WorkflowTimeout {
    pub step: WorkflowStepId,
    pub deadline: Duration,
}
}

89. Time Source

Part 60.


90. Scheduled Job

Timer-driven work.


91. Scheduler

Part 34 integration.


92. Durable Timer

Stored.


93. Timer Fire

Idempotent.


94. Crash

Timer recovers.


95. No In-Memory-Only Critical Timer

Hard rule.


96. Internal Pub/Sub

Best for transient notifications.


97. Pub/Sub Classes

#![allow(unused)]
fn main() {
pub enum PubSubClass {
    Ephemeral,
    Durable,
}
}

98. Ephemeral Pub/Sub

Examples:

UI refresh
local state invalidation
noncritical cache update

99. Durable Pub/Sub

For integration events.


100. Hard Rule

Critical state transition cannot depend solely on ephemeral pub/sub.


101. Topic

Logical domain namespace.


102. Topic ID

#![allow(unused)]
fn main() {
pub struct TopicId(pub String);
}

103. Topic Naming

Domain scoped.


104. Avoid

users.*
messages.*

global patterns.


105. Prefer

tenant.<opaque>.policy
federation.<domain>.events

internally.


106. But

Topic names still should avoid sensitive IDs where logs expose them.


107. Topic Alias

Opaque internal identifier.


108. Subscription

#![allow(unused)]
fn main() {
pub struct Subscription {
    pub consumer: ConsumerId,
    pub topic: TopicId,
    pub mode: SubscriptionMode,
}
}

109. Subscription Mode

#![allow(unused)]
fn main() {
pub enum SubscriptionMode {
    CompetingConsumer,
    Fanout,
}
}

110. Competing Consumer

One worker gets job.


111. Fanout

Each subscriber gets event.


112. Fanout Cost

Bounded.


113. No Unlimited Dynamic Fanout

Hard rule.


114. Consumer Group

Useful.


115. Consumer Group ID

Service-scoped.


116. Ordering

Need explicit guarantees.


117. Ordering Levels

#![allow(unused)]
fn main() {
pub enum OrderingGuarantee {
    None,
    Partition,
    Total,
}
}

118. Default

Partition ordering.


119. Total Ordering

Rare and expensive.


120. Hard Rule

Do not assume total ordering unless explicitly guaranteed.


121. Partition Ordering

Examples:

conversation
tenant policy
workflow

122. Partition Sequence

#![allow(unused)]
fn main() {
pub struct PartitionSequence(pub u64);
}

123. Sequence Monotonicity

Per partition.


124. Cross-Partition Ordering

Undefined.


125. Reordering

Consumers must tolerate where allowed.


126. Event Versioning

Schema evolution.


127. Expand Compatibility

Add optional fields.


128. Breaking Event Change

New event type/version.


129. No Semantic Reuse Of Old Field

Hard rule.


130. Event Compatibility Matrix

Current/previous.


131. Consumer Compatibility

Declared.


132. Consumer Capability

#![allow(unused)]
fn main() {
pub struct ConsumerCapability {
    pub event_type: EventTypeId,
    pub versions: VersionRange,
}
}

133. Unknown Event

Safe handling.


134. Critical Consumer

May block deployment if incompatible.


135. Event Payload

Minimal.


136. No Full Domain Object By Default

Hard rule.


137. Event Projection

Purpose-specific.


138. Good Event

MessagePersisted {
    message_ref,
    conversation_ref,
}

139. Bad Event

EntireMessageIncludingPlaintext

unless strictly local/trusted boundary.


140. Server Bus

Should never carry E2EE plaintext.


141. Hard rule.


142. Privacy Event Classification

#![allow(unused)]
fn main() {
pub enum EventPrivacyClass {
    Public,
    Internal,
    SensitiveReference,
    ForbiddenRemote,
}
}

143. ForbiddenRemote

Never leaves local trust boundary.


144. Bus Broker

Enforces class.


145. Event Encryption

At transport layer.


146. Sensitive Internal Event

May also encrypt payload/application layer.


147. Broker Should Not Need Plaintext User Content

Hard rule.


148. Multi-Tenant Isolation

Part 69.


149. Tenant Namespace

Queue/topic isolated.


150. No Cross-Tenant Consumer Group

Hard rule.


151. Tenant Event Context

#![allow(unused)]
fn main() {
pub struct TenantEventContext {
    pub tenant: TenantId,
}
}

152. Explicit.


153. No Missing Tenant Context For Tenant Event

Hard rule.


154. Federation

Separate bus per domain/peer.


155. No Global Inter-Federation Bus

Hard rule.


156. Integration Event

Cross-domain only through explicit gateway.


157. Gateway validates/reprojects.


158. No Raw Internal Event Bridging

Hard rule.


159. Local Client Event Bus

Can be in-process.


160. Tokio broadcast/mpsc

Possible.


161. But

Critical durable events still persisted.


162. UI Events

Ephemeral.


163. Domain Events

May be durable.


164. Bounded Channels

Mandatory.


165. Tokio Channel

No unbounded hot path.


166. Queue Fairness

Per tenant/domain/class.


167. Fair Scheduling

Possible:

deficit round robin
weighted fair queue

168. No One Tenant Starvation

Hard rule.


169. Priority Inversion

Prevent.


170. Security-control event

Must not wait behind bulk backlog.


171. Resource Budget

Part 63.


172. Queue Storage

Database/KV-backed.


173. Durable Queue Storage Requirements

atomic enqueue
lease/claim
ack
retry metadata

174. Queue Lease

#![allow(unused)]
fn main() {
pub struct QueueLease {
    pub job: JobId,
    pub consumer: ConsumerId,
    pub expires_at: LeaseDeadline,
}
}

175. Lease Expiry

Job becomes visible again.


176. No Permanent Hidden InFlight Job

Hard rule.


177. Visibility Timeout

Configured by job type.


178. Heartbeat

Optional for long jobs.


179. Long Job

Prefer workflow step with checkpoint.


180. Queue Claim

Atomic compare-and-set/transaction.


181. Duplicate Claim

Prevented or harmless via idempotency.


182. Queue Sharding

Partition by:

tenant
workflow
service

183. No Global User Shard Key

Hard rule.


184. Broker Technology

Possible:

PostgreSQL queue
embedded durable queue
NATS JetStream
Kafka-like system
custom Rust queue

185. No Mandatory Broker Product

Hard rule.


186. Selection Based On

ordering
durability
throughput
operational complexity
privacy

187. Initial Recommendation

Use simplest engine that satisfies invariants.


188. Small/Self-Hosted

PostgreSQL-backed durable queue can suffice.


189. Large Deployment

Dedicated streaming broker may be useful.


190. Client

Embedded queue in local DB.


191. Broker Abstraction

#![allow(unused)]
fn main() {
pub trait EventBus {
    fn publish(
        &self,
        event: EventEnvelope<Bytes>,
    ) -> Result<(), BusError>;

    fn subscribe(
        &self,
        subscription: Subscription,
    ) -> Result<Box<dyn EventStream>, BusError>;
}
}

192. Durable Queue Trait

#![allow(unused)]
fn main() {
pub trait DurableQueue {
    fn enqueue(
        &self,
        job: QueueJob<Bytes>,
    ) -> Result<(), QueueError>;

    fn claim(
        &self,
        consumer: ConsumerId,
    ) -> Result<Option<LeasedJob>, QueueError>;
}
}

193. Workflow Engine Trait

#![allow(unused)]
fn main() {
pub trait WorkflowEngine {
    fn start(
        &self,
        workflow: WorkflowDefinition,
    ) -> Result<WorkflowId, WorkflowError>;

    fn signal(
        &self,
        workflow: WorkflowId,
        event: WorkflowSignal,
    ) -> Result<(), WorkflowError>;
}
}

194. Workflow Definition

Versioned.


195. Workflow Version

#![allow(unused)]
fn main() {
pub struct WorkflowVersion(pub u16);
}

196. In-Flight Workflow Upgrade

Hard problem.


197. Recommendation

Pin workflow version at start.


198. New Workflow Uses New Version.


199. Migration

Explicit for long-lived workflow.


200. No Silent Definition Swap Mid-Workflow

Hard rule.


201. Workflow Compensation

Versioned.


202. Workflow Replay

Can reconstruct state from journal.


203. Deterministic Workflow Logic

Preferred.


204. External Side Effect

Must be recorded.


205. No Nondeterministic Hidden Side Effect During Replay

Hard rule.


206. Scheduled Workflow

Timer events durable.


207. Clock Skew

Part 60.


208. Timer semantics

Use monotonic/epoch where possible.


209. Wall-clock scheduled tasks

Need skew policy.


210. Message Bus Security

Authenticate producers/consumers.


211. Producer Identity

Service-scoped.


212. Consumer Identity

Service-scoped.


213. No User Messaging Identity

Hard rule.


214. Authorization

Topic/queue scoped.


215. Producer ACL

Explicit.


216. Consumer ACL

Explicit.


217. No Wildcard * For Critical Service

Hard rule.


218. Least Privilege

Each service reads/writes only required topics.


219. Broker Credential

Short-lived/scoped.


220. Credential Rotation

Part 61/66.


221. Encryption In Transit

Required.


222. Broker At Rest

Encrypted where sensitive metadata exists.


223. No Plaintext Secret In Event Payload

Hard rule.


224. Audit

Broker admin actions only.


225. No User Event Body Logging

Hard rule.


226. Observability

Safe metrics:

queue depth
event rate
consumer lag
retry count
dead-letter count

227. Forbidden Metrics

No:

message text
contact IDs
conversation names
raw mailbox IDs

228. Consumer Lag

Partition/service aggregate.


229. No Per-User Lag Dashboard

Hard rule.


230. Event Tracing

Privacy-sensitive.


231. Correlation ID

Scoped.


232. No Global Cross-Service User Correlation ID

Hard rule.


233. Workflow Correlation

WorkflowId only.


234. Trace Sampling

Infrastructure synthetic/aggregate.


235. No Full User Event Trace In Production

Hard rule.


236. Backpressure States

#![allow(unused)]
fn main() {
pub enum QueuePressure {
    Normal,
    Elevated,
    Saturated,
}
}

237. Saturated

Reject/defer low-priority producers.


238. No Memory Spill Explosion

Hard rule.


239. Spill To Disk

Only within bounded quota.


240. Queue Disk Full

Fail safely.


241. Security-Control Queue

Reserved capacity.


242. Emergency Queue

Reserved capacity.


243. Bulk Queue

First to throttle.


244. Queue Aging

Avoid starvation.


245. Priority Aging

Controlled.


246. But

Bulk never outranks security controls.


247. Retry Storm Protection

Central.


248. Circuit Breaker

For failing downstream.


249. Breaker State

#![allow(unused)]
fn main() {
pub enum CircuitState {
    Closed,
    Open,
    HalfOpen,
}
}

250. Open

Stop immediate retries.


251. HalfOpen

Probe.


252. No Retry Storm Across Fleet

Hard rule.


253. Rate Limits

Per producer/topic.


254. Queue Admission Control

Based on capacity.


255. No Unlimited Producer

Hard rule.


256. Workflow Examples

Tenant provisioning:

allocate namespace
→ create key root
→ allocate storage
→ apply policy
→ activate

257. Provider Migration

provision target
→ replicate state
→ dual receive
→ cutover
→ retire old

258. Account Deletion

revoke access
→ delete active data
→ revoke keys
→ process backups
→ complete

259. Key Rotation

generate next
→ distribute
→ overlap
→ prefer next
→ retire old

260. Release Rollout

canary
→ verify
→ wave
→ verify
→ complete

261. Each Workflow

Durable, idempotent, restart-safe.


262. No In-Memory Orchestration For Critical Multi-Step Flow

Hard rule.


263. Event Store

Do not automatically store every event forever.


264. Retention

Part 67.


265. Event Retention Class

#![allow(unused)]
fn main() {
pub enum EventRetentionClass {
    Ephemeral,
    Short,
    Operational,
    Audit,
}
}

266. Domain Event Retention

Only as needed.


267. Integration Event

Until all consumers/retention window.


268. Audit Event

Longer if justified.


269. No Infinite Event History By Default

Hard rule.


270. Compaction

Possible for monotonic state.


271. But

Do not compact audit semantics incorrectly.


272. Event Replay

Allowed within retention.


273. Consumer Reset

High-risk.


274. Replay Could Duplicate side effects.


275. Replay Requires Idempotent consumer.


276. Hard rule.


277. Schema Registry

Event types/version registry.


278. Event Type ID

Stable.


279. No Dynamic Arbitrary Payload Type

Hard rule.


280. Registry Entry

#![allow(unused)]
fn main() {
pub struct EventSchemaDescriptor {
    pub event_type: EventTypeId,
    pub versions: VersionRange,
    pub privacy: EventPrivacyClass,
    pub retention: EventRetentionClass,
}
}

281. CI Gate

Every durable event has registry entry.


282. Unknown Durable Event

Consumer handles explicitly.


283. Integration Boundary

Use integration event, not DB CDC by default.


284. CDC

Change Data Capture may leak schema/internal data.


285. Hard rule

Do not expose raw CDC across trust boundaries.


286. Internal CDC

Possible for analytics/rebuildable systems.


287. But

Apply privacy review.


288. Analytics Bus

Separate from operational bus.


289. No User Content.


290. Command Bus

Could be internal.


291. But

Commands requiring immediate auth should be validated at ingress.


292. Command Handler

Checks:

auth
capability
policy
idempotency

293. Command Cannot Grant Itself Authority

Hard rule.


294. External API

Part 68 translates request → command.


295. Internal Event

Never directly trusted as auth proof.


296. Hard rule.


297. Distributed Locking

Avoid if possible.


298. Prefer

single partition owner
DB constraint
lease
compare-and-swap

299. Global Lock

Hard rule against.


300. Leader Election

Needed for some workflows.


301. Scope leader by domain.


302. No Global Coordinator If Avoidable

Hard rule.


303. Queue Lease + fencing token

Useful.


304. Fencing Token

#![allow(unused)]
fn main() {
pub struct FencingToken(pub u64);
}

305. Monotonic.


306. Prevent stale worker write.


307. Hard rule for exclusive workflows.


308. Workflow Ownership

Lease.


309. Lease Expiry

Another worker resumes.


310. State in durable journal.


311. Exactly-Once External Side Effect

Use external idempotency key if provider supports.


312. Otherwise

Need compensating/reconciliation.


313. No False Exactly-Once Claim

Hard rule.


314. Reconciliation

Part 46.


315. Workflow Reconciler

Checks stuck/inconsistent state.


316. Reconciliation Event

Could repair.


317. No Blind Auto-Repair For High-Risk Security State

Hard rule.


318. Human approval for critical correction.


319. Event Bus Failure

Critical.


320. Failure Modes

broker down
partition
storage full
consumer lag
duplicate flood
schema incompatibility

321. Broker Down

Producer outbox continues locally until bounded limit.


322. No memory-only buffering.


323. Hard rule.


324. Consumer Down

Lag grows bounded.


325. Queue Saturated

Admission control.


326. Schema Incompatibility

Consumer quarantines event or deployment blocked.


327. No Silent Drop

Hard rule.


328. Event Corruption

Digest/checksum detect.


329. Corrupted Event

Do not apply.


330. Dead-letter/quarantine minimal metadata.


331. Broker Replication

Depends on criticality.


332. Control-plane event bus

HA.


333. Client local bus

single device.


334. Federation queue

per peer.


335. Durability Requirement

#![allow(unused)]
fn main() {
pub enum EventDurability {
    Ephemeral,
    LocalDurable,
    ReplicatedDurable,
}
}

336. Security-control events

Replicated where infrastructure requires.


337. UI events

Ephemeral.


338. Work queue jobs

Local/replicated durable.


339. Event Encryption

Could use payload envelope.


340. Key Scope

Service/tenant.


341. No Shared Global Event Encryption Key

Hard rule.


342. Multi-Tenant Bus

Tenant-specific encryption key if required.


343. Queue Metadata Leakage

Minimize.


344. Broker may see:

topic
size
timing

345. Do not put sensitive semantics in topic name.


346. Hard rule.


347. Topic IDs opaque in production.


348. Event Size

Bounded.


349. Large Payload

Store object separately; event contains reference.


350. No Multi-MB Event Payload

Hard rule.


351. Max Event Size

#![allow(unused)]
fn main() {
pub struct EventSizePolicy {
    pub max_bytes: usize,
}
}

352. Attachment/Event

Use blob ref.


353. Serialization

Postcard preferred internally.


354. JSON only external interop.


355. Versioned envelope.


356. Strict decode bounds.


357. No Serde Untagged Ambiguity For Security-Critical Events

Preferred hard rule.


358. Event Registry

Compile-time/generated enum where feasible.


359. Performance

Part 63.


360. Measure:

publish latency
consumer lag
queue throughput
retry rate
workflow completion time

361. Tail Latency

Important.


362. But

Throughput cannot justify dropping security events.


363. Batch Publish

Allowed.


364. Preserve per-partition ordering where required.


365. Batch Consume

Allowed.


366. Transaction boundaries clear.


367. Batch Failure

Retry idempotently.


368. No Partial Ack Ambiguity

Hard rule.


369. Consumer Concurrency

Per partition.


370. Parallelism bounded.


371. Work-Stealing

Possible for independent partitions.


372. No Cross-Tenant fairness collapse.


373. Broker Choice Criteria

durability
ordering
operational simplicity
Rust client quality
offline/self-host support

374. Recommendation

Do not introduce Kafka-class infrastructure before scale requires it.


375. Small deployments

PostgreSQL durable queue + local Tokio channels.


376. Larger deployments

NATS JetStream/Kafka-like options can be adapter implementations.


377. Core must remain broker-agnostic.


378. Event Bus Capability Model

#![allow(unused)]
fn main() {
pub struct BusCapabilities {
    pub durable: bool,
    pub partition_ordering: bool,
    pub consumer_groups: bool,
    pub delayed_delivery: bool,
    pub transactions: bool,
}
}

379. Domain Requirement

Explicit.


380. No Adapter Used If Missing Required Capability

Hard rule.


381. Observability Integration

Part 51.


382. Queue Metrics

Aggregate.


383. Alert

lag high
retry storm
dead-letter growth
disk saturation

384. Alert Must Link Runbook

Part 65.


385. No payload in alert.


386. Testing

Dedicated event/workflow testkit.


387. Test Scenarios

duplicate delivery
reordering
consumer crash
producer crash
broker outage
queue full
workflow restart

388. Duplicate Test

One semantic effect.


389. Producer Crash

Committed outbox publishes later.


390. Consumer Crash

Unacked event redelivered.


391. ACK Crash Race

No lost event.


392. Reordering Test

Consumer remains correct if order guarantee says none.


393. Partition Order Test

Sequence preserved per partition.


394. Queue Full Test

Producer backpressure/rejects safely.


395. Retry Storm Test

Backoff/jitter works.


396. Dead-Letter Test

No indefinite sensitive payload retention.


397. Workflow Crash Test

Resume from journal.


398. Compensation Test

Idempotent.


399. Timeout Test

Correct state transition.


400. Tenant Isolation Test

Tenant A event cannot reach B consumer.


401. Federation Isolation Test

Remote domain gets only integration event projection.


402. Schema Evolution Test

N-1 consumer handles supported version.


403. Large Payload Test

Rejected/reference path used.


404. Security Event Priority Test

Security queue not starved by bulk.


405. Fuzzing

Fuzz:

event envelope
command envelope
workflow journal
queue job
schema registry

406. Property Tests

Properties:

ack never precedes durable consumer commit
same event applied twice has one semantic effect
workflow state never advances past failed required step
tenant event never escapes tenant scope

407. Formal Verification Targets

Strong candidates:

outbox/inbox delivery
queue lease/fencing
workflow/saga state machine
retry/timeout state

408. TLA+ Candidate

At-least-once delivery + consumer crash.


409. Kani Candidate

queue state/lease/fencing arithmetic.


410. Loom Candidate

concurrent consumer claim/ack.


411. Chaos

Part 64/70.


412. Broker Partition

Exercise.


413. Region Loss

Queue continuity/failover.


414. Control Plane Loss

Local outbox continues bounded.


415. No silent event loss.


416. Disaster Recovery

Event bus state backup depending authority.


417. Queue Recovery

Must not replay beyond retention incorrectly.


418. Workflow Recovery

Journal authoritative.


419. No reconstruct from logs alone.


420. Supply Chain

Part 73.


421. Broker client/runtime dependencies pinned.


422. Crate Layout

Recommended:

crates/
├── siar-event-core/
├── siar-command-core/
├── siar-event-registry/
├── siar-event-bus/
├── siar-durable-queue/
├── siar-outbox/
├── siar-inbox/
├── siar-workflow/
├── siar-scheduler/
├── siar-event-observability/
└── siar-event-testkit/

423. siar-event-core

Owns:

event IDs
envelopes
ordering
durability
errors

424. siar-command-core

Command envelopes/idempotency.


425. siar-event-registry

Schema/version/privacy/retention metadata.


426. siar-event-bus

Broker abstraction/adapters.


427. siar-durable-queue

Lease/claim/ack/retry.


428. siar-outbox

Transactional publish intent.


429. siar-inbox

Consumer dedup/receipt.


430. siar-workflow

Saga/workflow/journal.


431. siar-scheduler

Durable timers/delayed jobs.


432. siar-event-observability

Privacy-safe lag/queue metrics.


433. siar-event-testkit

Crash/duplicate/reorder/fault simulation.


434. Error Taxonomy

#![allow(unused)]
fn main() {
pub enum BusError {
    Unavailable,
    Unauthorized,
    TopicDenied,
    SchemaUnsupported,
    EventTooLarge,
    PublishFailed,
    Internal,
}

pub enum QueueError {
    QueueFull,
    LeaseConflict,
    JobNotFound,
    AckInvalid,
    RetryExhausted,
    Internal,
}

pub enum WorkflowError {
    WorkflowNotFound,
    VersionMismatch,
    InvalidTransition,
    StepFailed,
    CompensationFailed,
    Timeout,
    Internal,
}
}

435. Security, Privacy & Correctness Invariants

Mandatory:

1. Durable domain events are published through transactional outbox semantics after the corresponding domain commit.
2. Consumers ACK only after durable idempotent apply/receipt commit.
3. At-least-once delivery is the default; exactly-once semantic effects are achieved through idempotency, not marketing claims.
4. Every critical queue is bounded by count, bytes, and/or age; overload causes backpressure rather than unbounded memory/disk growth.
5. Commands, events, jobs, and workflow states are distinct typed concepts.
6. Critical multi-step workflows are journaled durably and survive process/node restart.
7. Tenant/federation boundaries are explicit in queue/topic/consumer scope; there is no global cross-tenant bus.
8. Event payloads are purpose-specific and minimal; server-side buses never carry E2EE plaintext.
9. Dead-letter/quarantine handling cannot become an indefinite archive of sensitive payloads.
10. Ordering guarantees are explicit and scoped; total ordering is never assumed globally.
11. Security-control/emergency queues retain reserved capacity and cannot be starved by bulk/background traffic.
12. Broker/runtime failures never silently authorize privacy downgrade or direct-routing bypass.

436. Initial Production Scope

Implement first:

typed event/command envelopes
event schema registry
transactional outbox/inbox
embedded client durable queue
PostgreSQL-backed server durable queue
bounded Tokio local channels
partition ordering
retry/backoff/jitter
queue leases
dead-letter minimal metadata
durable workflow journal
durable timers
tenant-scoped queue/topic namespaces
privacy-safe queue observability
event/workflow contract tests

Then add:

dedicated streaming broker adapters
distributed workflow sharding
advanced delayed scheduling
formal queue/workflow verification
cross-region event replication
adaptive queue fairness

437. Definition of Done

Part 75 is complete when:

  • commands, events, jobs, audit records, and workflow state are clearly separated
  • durable domain events use transactional outbox
  • consumers are idempotent and ACK after commit
  • queue/topic scopes are bounded and tenant-aware
  • partition ordering rules are explicit
  • retries are bounded and jittered
  • durable timers survive restart
  • critical workflows use durable journals/sagas
  • dead-letter handling is minimal and lifecycle-bound
  • server buses never carry E2EE plaintext
  • event schema/privacy/retention metadata is machine-readable
  • security/emergency queues cannot be starved
  • broker choice is replaceable behind capability-validated adapters
  • duplicate/reordering/crash/partition/tenant/schema/fuzz/formal tests are specified

438. Final Architecture

                      DOMAIN TRANSACTION
                              │
                              ▼
                       OUTBOX RECORD
                              │
                              ▼
                    DURABLE EVENT BUS
                              │
          ┌───────────────────┼───────────────────┐
          │                   │                   │
      Consumer A          Consumer B         Workflow Engine
          │                   │                   │
          ▼                   ▼                   ▼
      Inbox/Dedup         Inbox/Dedup        Workflow Journal
          │                   │                   │
          └───────────────────┼───────────────────┘
                              ▼
                     IDEMPOTENT SIDE EFFECTS

Distributed-workflow safety model:

transactional outbox
+
at-least-once delivery
+
idempotent consumers
+
bounded durable queues
+
explicit ordering
+
durable workflow journals
+
privacy-scoped event payloads

not:

fire events into an unbounded bus and assume every consumer sees them once and in order

439. Final Principle

The internal event fabric should decouple services without becoming a global metadata spine.

The correct model is:

explicit commands
+
durable facts
+
bounded queues
+
idempotent consumers
+
scoped workflows
+
privacy-safe observability

This architecture gives SIAR a reliable asynchronous coordination layer for messaging, tenants, federation, provisioning, recovery, key rotation, deployment, and long-running workflows while preserving the transaction, privacy, and isolation guarantees established across Parts 34–74.