Multi-channel notifications (Email, SMS, Push, In-App)
| Tier | infrastructure |
| Role | unclassified (baselined) |
| Path | crates/infrastructure/communication |
| Edition | 2021 |
| Targets | infrastructure_communication, integration_tests |
| Public items | 18 across 1 module |
| Tests | 99 |
What it is for
Multi-channel notifications and conversations.
Provides:
- Unified interface for sending notifications across different channels
- Conversation threading for chat/inbox functionality
- Persistent message logging for audit trail
Capabilities
verb:add
Repository functions for communication models.
| Item |
|---|
async fn add_participant(pool : & PgPool, participant : & ConversationParticipant,) -> Result <ConversationParticipant, CommunicationError> |
verb:assign
Repository functions for communication models.
| Item |
|---|
async fn assign_conversation(pool : & PgPool, id : Uuid, assigned_to : Uuid,) -> Result <(), CommunicationError> |
async fn assign_conversation_tx(conn : & mut PgConnection, id : Uuid, assigned_to : Uuid,) -> Result <(), CommunicationError> |
verb:close
Repository functions for communication models.
| Item |
|---|
async fn close_conversation(pool : & PgPool, id : Uuid, closed_by : Option <Uuid>,) -> Result <(), CommunicationError> |
verb:create
Repository functions for communication models.
| Item |
|---|
async fn create_conversation(pool : & PgPool, conversation : & Conversation,) -> Result <Conversation, CommunicationError> |
async fn create_message(pool : & PgPool, message : & ChatMessage,) -> Result <ChatMessage, CommunicationError> |
async fn create_message_tx(conn : & mut PgConnection, message : & ChatMessage,) -> Result <ChatMessage, CommunicationError> |
verb:find
Repository functions for communication models.
| Item |
|---|
async fn find_conversation_by_id(pool : & PgPool, id : Uuid,) -> Result <Option <Conversation>, CommunicationError> |
async fn find_conversations_needing_reply(pool : & PgPool,) -> Result <Vec <Conversation>, CommunicationError> |
async fn find_participants_by_conversation(pool : & PgPool, conversation_id : Uuid,) -> Result <Vec <ConversationParticipant>, CommunicationError> |
async fn find_conversations_for_party(pool : & PgPool, party_id : PartyId,) -> Result <Vec <Conversation>, CommunicationError> |
async fn find_messages_by_conversation(pool : & PgPool, conversation_id : Uuid,) -> Result <Vec <ChatMessage>, CommunicationError> |
verb:list
Repository functions for communication models.
| Item |
|---|
async fn list_active_conversations(pool : & PgPool,) -> Result <Vec <Conversation>, CommunicationError> |
verb:mark
Repository functions for communication models.
| Item |
|---|
async fn mark_participant_read(pool : & PgPool, conversation_id : Uuid, party_id : PartyId,) -> Result <(), CommunicationError> |
verb:remove
Repository functions for communication models.
| Item |
|---|
async fn remove_participant(pool : & PgPool, conversation_id : Uuid, party_id : PartyId,) -> Result <(), CommunicationError> |
verb:set
Repository functions for communication models.
| Item |
|---|
async fn set_message_status_tx(conn : & mut PgConnection, message_id : Uuid, status : MessageStatus, error : Option <& str>,) -> Result <(), CommunicationError> |
async fn set_message_status(pool : & PgPool, message_id : Uuid, status : MessageStatus, error : Option <& str>,) -> Result <(), CommunicationError> |
verb:soft
Repository functions for communication models.
| Item |
|---|
async fn soft_delete_conversation(pool : & PgPool, id : Uuid) -> Result <(), CommunicationError> |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
infrastructure_communication
repo
Public surface
`repo`
| Item | What it is |
|---|---|
async fn create_conversation(pool : & PgPool, conversation : & Conversation,) -> Result <Conversation, CommunicationError> | Create a new conversation |
async fn find_conversation_by_id(pool : & PgPool, id : Uuid,) -> Result <Option <Conversation>, CommunicationError> | Find a conversation by ID |
async fn list_active_conversations(pool : & PgPool,) -> Result <Vec <Conversation>, CommunicationError> | List active conversations (not closed or deleted) |
async fn find_conversations_needing_reply(pool : & PgPool,) -> Result <Vec <Conversation>, CommunicationError> | Find conversations needing a reply (last message was inbound) |
async fn close_conversation(pool : & PgPool, id : Uuid, closed_by : Option <Uuid>,) -> Result <(), CommunicationError> | Close a conversation |
async fn assign_conversation(pool : & PgPool, id : Uuid, assigned_to : Uuid,) -> Result <(), CommunicationError> | Assign a conversation to a staff user, leaving it active |
async fn assign_conversation_tx(conn : & mut PgConnection, id : Uuid, assigned_to : Uuid,) -> Result <(), CommunicationError> | Assign a conversation on a caller-supplied connection |
async fn soft_delete_conversation(pool : & PgPool, id : Uuid) -> Result <(), CommunicationError> | Soft delete a conversation |
async fn add_participant(pool : & PgPool, participant : & ConversationParticipant,) -> Result <ConversationParticipant, CommunicationError> | Add a participant to a conversation |
async fn find_participants_by_conversation(pool : & PgPool, conversation_id : Uuid,) -> Result <Vec <ConversationParticipant>, CommunicationError> | Find all participants in a conversation |
async fn find_conversations_for_party(pool : & PgPool, party_id : PartyId,) -> Result <Vec <Conversation>, CommunicationError> | Find all conversations a party is participating in |
async fn mark_participant_read(pool : & PgPool, conversation_id : Uuid, party_id : PartyId,) -> Result <(), CommunicationError> | Mark a participant as having read the conversation |
async fn remove_participant(pool : & PgPool, conversation_id : Uuid, party_id : PartyId,) -> Result <(), CommunicationError> | Remove a participant from a conversation |
async fn create_message(pool : & PgPool, message : & ChatMessage,) -> Result <ChatMessage, CommunicationError> | Create a new chat message, on its own transaction |
async fn create_message_tx(conn : & mut PgConnection, message : & ChatMessage,) -> Result <ChatMessage, CommunicationError> | Create a new chat message on a caller-supplied connection |
async fn find_messages_by_conversation(pool : & PgPool, conversation_id : Uuid,) -> Result <Vec <ChatMessage>, CommunicationError> | Find messages in a conversation |
async fn set_message_status_tx(conn : & mut PgConnection, message_id : Uuid, status : MessageStatus, error : Option <& str>,) -> Result <(), CommunicationError> | Record a message's delivery outcome |
async fn set_message_status(pool : & PgPool, message_id : Uuid, status : MessageStatus, error : Option <& str>,) -> Result <(), CommunicationError> | Record a message's delivery outcome, on its own transaction |
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
CommunicationError | error::CommunicationError |
SmtpConfig | smtp::SmtpConfig |
{AlwaysFailOutboundSender,OutboundError,OutboundOutcome,OutboundSender,StubOutboundSender,} | outbound::{AlwaysFailOutboundSender,OutboundError,OutboundOutcome,OutboundSender,StubOutboundSender,} |
{Channel,EmailChannel,InAppChannel,PushChannel,SmsChannel} | channel::{Channel,EmailChannel,InAppChannel,PushChannel,SmsChannel} |
{ChatMessage,MessageChannel,MessageDirection,MessageStatus} | chat_message::{ChatMessage,MessageChannel,MessageDirection,MessageStatus} |
{Conversation,ConversationParticipant,ConversationStatus,ConversationType,} | conversation::{Conversation,ConversationParticipant,ConversationStatus,ConversationType,} |
{DiscordConfig,DiscordOutboundSender,DiscordProviderBuilder} | discord::{DiscordConfig,DiscordOutboundSender,DiscordProviderBuilder} |
{EmailMessage,InAppMessage,Message,PushMessage,SmsMessage} | message::{EmailMessage,InAppMessage,Message,PushMessage,SmsMessage} |
{EnqueueRequest,InMemorySendService,PgSendService,SendChannel,SendJob,SendJobError,SendJobStatus,SendService,} | send_jobs::{EnqueueRequest,InMemorySendService,PgSendService,SendChannel,SendJob,SendJobError,SendJobStatus,SendService,} |
{FlowrouteConfig,FlowrouteOutboundSender,FlowrouteProviderBuilder} | flowroute::{FlowrouteConfig,FlowrouteOutboundSender,FlowrouteProviderBuilder} |
{InMemoryRateLimiter,RateLimitDecision,RateLimiter} | rate_limit::{InMemoryRateLimiter,RateLimitDecision,RateLimiter} |
{Notification,NotificationStatus} | notification::{Notification,NotificationStatus} |
{NtfyConfig,NtfyOutboundSender,NtfyProviderBuilder} | ntfy::{NtfyConfig,NtfyOutboundSender,NtfyProviderBuilder} |
{OutboundBuildError,OutboundProviderRegistry,ProviderBuilder,SmtpProviderBuilder,} | provider_registry::{OutboundBuildError,OutboundProviderRegistry,ProviderBuilder,SmtpProviderBuilder,} |
{SignalConfig,SignalOutboundSender,SignalProviderBuilder} | signal::{SignalConfig,SignalOutboundSender,SignalProviderBuilder} |
{SlackConfig,SlackOutboundSender,SlackProviderBuilder} | slack::{SlackConfig,SlackOutboundSender,SlackProviderBuilder} |
{SmtpOutboundSender,SmtpSenderInitError} | smtp::{SmtpOutboundSender,SmtpSenderInitError} |
{TeamsConfig,TeamsOutboundSender,TeamsProviderBuilder} | teams::{TeamsConfig,TeamsOutboundSender,TeamsProviderBuilder} |
{TelegramConfig,TelegramOutboundSender,TelegramProviderBuilder} | telegram::{TelegramConfig,TelegramOutboundSender,TelegramProviderBuilder} |
{TwilioConfig,TwilioOutboundSender,TwilioProviderBuilder} | twilio::{TwilioConfig,TwilioOutboundSender,TwilioProviderBuilder} |
{WhatsappConfig,WhatsappOutboundSender,WhatsappProviderBuilder} | whatsapp::{WhatsappConfig,WhatsappOutboundSender,WhatsappProviderBuilder} |
{delete_system_config,delete_tenant_config,get_system_config,get_tenant_config,list_system_providers,list_tenant_providers,resolve_config,store_system_config,store_tenant_config,touch_tenant_config,ConfigScope,MessagingCredentialsError,MessagingProviderInfo,ResolvedConfig,} | messaging_credentials::{delete_system_config,delete_tenant_config,get_system_config,get_tenant_config,list_system_providers,list_tenant_providers,resolve_config,store_system_config,store_tenant_config,touch_tenant_config,ConfigScope,MessagingCredentialsError,MessagingProviderInfo,ResolvedConfig,} |
{run_outbound_worker,run_outbound_worker_with_registry,OutboundWorkerConfig,} | outbound_worker::{run_outbound_worker,run_outbound_worker_with_registry,OutboundWorkerConfig,} |
Boundary
Reaches into foundation, identity.
Shares tier infrastructure with 82 other crates: infrastructure-acquire, infrastructure-adapters-google-calendar, infrastructure-adapters-google-gmail, infrastructure-adapters-google-places, infrastructure-adapters-google-trends, infrastructure-adapters-shodan, infrastructure-adapters-yelp, infrastructure-agent, … (82 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | infrastructure |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/infrastructure/communication |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_infrastructure["infrastructure"] --> n_foundation["foundation"] n_infrastructure["infrastructure"] --> n_identity["identity"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `foundation-basemodels` | foundation | no | always |
| `foundation-smtp-transport` | foundation | no | always |
| `identity-parties` | identity | no | always |
| `infrastructure-fetcher` | infrastructure | yes | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
async-trait | ^0.1 | — | no | always |
aws-config | ^1 | — | yes | always |
aws-sdk-ses | ^1 | — | yes | always |
chrono | ^0.4 | serde | no | always |
lettre | ^0.11 | tokio1-native-tls | no | always |
reqwest | ^0.12 | json, rustls-tls | yes | always |
serde | ^1 | derive | no | always |
serde_json | ^1 | — | no | always |
sqlx | ^0.8 | runtime-tokio, postgres, chrono, uuid, json | no | always |
thiserror | ^2 | — | no | always |
tokio | ^1 | full | no | always |
tokio-util | ^0.7 | rt | no | always |
tracing | ^0.1 | — | no | always |
uuid | ^1 | v4, v7, serde, js | no | always |
Development, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
dotenvy | ^0.15 | — | no | always |
tokio | ^1 | full | no | always |
tokio-test | ^0.4 | — | no | always |
Build. None.
Depended on by. 3 workspace crates.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR n_application_communication["application-communication"] -->|uses| SELF n_application_conversation["application-conversation"] -->|uses| SELF n_application_messaging["application-messaging"] -->|uses| SELF SELF["infrastructure-communication"] SELF -->|runtime| n_foundation_basemodels["foundation-basemodels"] SELF -->|runtime| n_foundation_smtp_transport["foundation-smtp-transport"] SELF -->|runtime| n_identity_parties["identity-parties"] SELF -->|runtime| n_infrastructure_fetcher["infrastructure-fetcher"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
| Feature | Enables | On by default |
|---|---|---|
aws-config | dep:aws-config | no |
aws-sdk-ses | dep:aws-sdk-ses | no |
default | http, twilio, whatsapp, flowroute, slack, telegram, discord, teams, … (10 total) | yes |
discord | http | yes |
flowroute | http | yes |
http | reqwest, dep:infrastructure-fetcher | yes |
ntfy | http | yes |
postgres | — | no |
reqwest | dep:reqwest | no |
ses | aws-sdk-ses, aws-config | no |
signal | http | yes |
slack | http | yes |
teams | http | yes |
telegram | http | yes |
twilio | http | yes |
whatsapp | http | yes |
flowchart LR n_aws_config["aws-config"] --> n_dep_aws_config["dep:aws-config"] n_aws_sdk_ses["aws-sdk-ses"] --> n_dep_aws_sdk_ses["dep:aws-sdk-ses"] n_default["default"] --> n_http["http"] n_default["default"] --> n_twilio["twilio"] n_default["default"] --> n_whatsapp["whatsapp"] n_default["default"] --> n_flowroute["flowroute"] n_default["default"] --> n_slack["slack"] n_default["default"] --> n_telegram["telegram"] n_default["default"] --> n_discord["discord"] n_default["default"] --> n_teams["teams"] n_default["default"] --> n_signal["signal"] n_default["default"] --> n_ntfy["ntfy"] n_discord["discord"] --> n_http["http"] n_flowroute["flowroute"] --> n_http["http"] n_http["http"] --> n_reqwest["reqwest"] n_http["http"] --> n_dep_infrastructure_fetcher["dep:infrastructure-fetcher"] n_ntfy["ntfy"] --> n_http["http"] n_postgres["postgres"] n_reqwest["reqwest"] --> n_dep_reqwest["dep:reqwest"] n_ses["ses"] --> n_aws_sdk_ses["aws-sdk-ses"] n_ses["ses"] --> n_aws_config["aws-config"] n_signal["signal"] --> n_http["http"] n_slack["slack"] --> n_http["http"] n_teams["teams"] --> n_http["http"] n_telegram["telegram"] --> n_http["http"] n_twilio["twilio"] --> n_http["http"] n_whatsapp["whatsapp"] --> n_http["http"]
Targets
| Kind | Name | Source |
|---|---|---|
| lib | infrastructure_communication | `src/lib.rs` |
| test | integration_tests | `tests/integration_tests.rs` |
Error model
No public error type was detected: no public item declares a type named *Error, and no public signature returns one.
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | yes |
| async runtime | yes |
| database access | yes |
| network I/O | yes |
| unsafe code | none detected |
| environment variables | none detected |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.
Related capabilities
3 workspace crates depend on this one: application-communication, application-conversation, application-messaging.
Verification
| Kind | Count |
|---|---|
| Unit tests | 86 |
| Integration tests | 13 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
repo | 18 | 0 | 5 |
What the tests establish, by name:
test_add_participant_to_conversation—tests/integration_tests.rstest_close_conversation—tests/integration_tests.rstest_conversations_needing_reply—tests/integration_tests.rstest_create_chat_message—tests/integration_tests.rstest_create_conversation—tests/integration_tests.rstest_find_conversation_by_id—tests/integration_tests.rstest_find_conversations_for_party—tests/integration_tests.rstest_find_messages_by_conversation—tests/integration_tests.rstest_find_participants_by_conversation—tests/integration_tests.rstest_list_active_conversations—tests/integration_tests.rstest_mark_participant_read—tests/integration_tests.rstest_message_updates_conversation_timestamps—tests/integration_tests.rstest_remove_participant—tests/integration_tests.rsconfig_debug_redacts_webhook_url—src/discord.rsconfig_round_trips_through_json—src/discord.rssend_refuses_internal_metadata_endpoint—src/discord.rsconfig_debug_redacts_secret_key—src/flowroute.rsconfig_default_base_url—src/flowroute.rsconfig_round_trips_through_json—src/flowroute.rssend_refuses_internal_metadata_base_url—src/flowroute.rstest_chat_message_failure—src/lib.rstest_chat_message_inbound—src/lib.rstest_chat_message_new—src/lib.rstest_chat_message_outbound—src/lib.rstest_chat_message_preview—src/lib.rstest_chat_message_status_transitions—src/lib.rstest_chat_message_system—src/lib.rstest_conversation_close—src/lib.rstest_conversation_needs_reply—src/lib.rstest_conversation_new—src/lib.rs- _… 69 more_
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 18 | 18 |
Public modules with a //! block | 1 | 1 |
pie showData
title Public items with rustdoc
"Documented" : 18
"No rustdoc detected" : 0
Metrics
| Metric | Value |
|---|---|
| Rust source files | 24 |
| Source lines | 7291 |
| Code lines | 5232 |
| Public API items | 18 |
| Public modules | 1 |
| Tests | 99 |
| Examples | 0 |
| Cargo features | 16 |
| Direct runtime dependencies | 18 |
| Workspace reverse dependencies | 3 |
pie showData
title Public API by kind
"function" : 18
pie showData
title Rust source composition
"Code" : 5232
"Blank or comment" : 2059
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.