Problem
lib-streaming currently rejects non-system events when EmitRequest.TenantID is empty. That is correct for SaaS multi-tenant deployments, but it leaves single-tenant/BYOC services with only bad choices:
- skip emitting business events when no tenant-manager tenant exists;
- put
organizationId into TenantID, which is semantically wrong;
- invent a synthetic tenant UUID, which violates services that intentionally have no tenant identity in single-tenant mode.
In plugin-br-bank-transfer, we rejected the organizationId -> TenantID fallback because tenant identity and organization scope are distinct:
tenantId comes from tenant-manager/JWT context and routes tenant-scoped infrastructure.
organizationId is a business-scope identifier for CRM/Midaz/Fees calls.
- Overloading
EmitRequest.TenantID with organization metadata contaminates CloudEvents/outbox routing metadata and can mislead downstream consumers.
The current safe plugin behavior is to skip emits when no real tenant is present. That preserves correctness but means single-tenant deployments lose streaming events.
Desired Capability
Add an explicit single-tenant routing mode that lets services emit non-system business events without providing a fake TenantID.
Possible API shapes:
- Add a routing scope field:
type EmitRequest struct {
DefinitionKey string
TenantID string
RoutingScope RoutingScope // e.g. MultiTenant, SingleTenant, System
Subject string
EventID string
Payload []byte
}
- Add a dedicated single-tenant routing key/config at builder level:
streaming.WithSingleTenantRoutingKey("default")
- Add an explicit method/helper:
emitter.EmitSingleTenant(ctx, EventRequest{...})
The important contract: the emitted event must not label this value as tenant identity (tenantId, ce-tenantid, outbox tenant_id) unless it is an actual tenant-manager tenant.
Acceptance Criteria
- Multi-tenant non-system events still fail fast when
TenantID is missing.
- Single-tenant non-system events can be emitted through an explicit API/config path.
- CloudEvents/outbox metadata does not misrepresent organization IDs or synthetic IDs as tenant identity.
- Event manifests/documentation make the distinction between tenant identity and single-tenant routing metadata clear.
- Existing multi-tenant behavior remains backward compatible.
Downstream Context
This was found during Gate 8 review of plugin-br-bank-transfer. The branch temporarily considered using either organizationId or a fixed UUID as EmitRequest.TenantID; both were rejected because they violate the plugin tenant-isolation rules and produce misleading event metadata.
Until this is supported upstream, the plugin will skip emits when no real tenant exists rather than emit events with a false tenant identity.
Problem
lib-streamingcurrently rejects non-system events whenEmitRequest.TenantIDis empty. That is correct for SaaS multi-tenant deployments, but it leaves single-tenant/BYOC services with only bad choices:organizationIdintoTenantID, which is semantically wrong;In
plugin-br-bank-transfer, we rejected theorganizationId -> TenantIDfallback because tenant identity and organization scope are distinct:tenantIdcomes from tenant-manager/JWT context and routes tenant-scoped infrastructure.organizationIdis a business-scope identifier for CRM/Midaz/Fees calls.EmitRequest.TenantIDwith organization metadata contaminates CloudEvents/outbox routing metadata and can mislead downstream consumers.The current safe plugin behavior is to skip emits when no real tenant is present. That preserves correctness but means single-tenant deployments lose streaming events.
Desired Capability
Add an explicit single-tenant routing mode that lets services emit non-system business events without providing a fake
TenantID.Possible API shapes:
The important contract: the emitted event must not label this value as tenant identity (
tenantId,ce-tenantid, outboxtenant_id) unless it is an actual tenant-manager tenant.Acceptance Criteria
TenantIDis missing.Downstream Context
This was found during Gate 8 review of
plugin-br-bank-transfer. The branch temporarily considered using eitherorganizationIdor a fixed UUID asEmitRequest.TenantID; both were rejected because they violate the plugin tenant-isolation rules and produce misleading event metadata.Until this is supported upstream, the plugin will skip emits when no real tenant exists rather than emit events with a false tenant identity.