Architecture
Connector Architecture
Connector system architecture — notification providers for email, SMS, WhatsApp, and Telegram.
Connector Architecture
The connector system provides a unified interface for sending notifications and OTPs through multiple providers.
Overview
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
│ │
│ Connector Manager (interface) │
│ │ │ │
│ ┌───────────┴───────────┐ │ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Email │ │ SMS │ │ WhatsApp │ │
│ │ Provider │ │ Provider │ │ Provider │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ SMTP │ │ Twilio │ │ Meta API │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘Interface
Connector Interface
type Connector interface {
Type() ConnectorType
Send(ctx context.Context, msg *Message) error
SendOTP(ctx context.Context, to, code string) error
Test(ctx context.Context, destination string) error
}Message Structure
type Message struct {
To string
Subject string
Body string
HTMLBody string
Type MessageType // notification, otp, alert
}Supported Providers
Email (SMTP)
| Property | Value |
|---|---|
| Type | |
| Provider | Any SMTP provider (SendGrid, Resend, Postmark, Mailgun, etc.) |
| Use | Notifications, OTPs |
Features:
- Works with any SMTP relay
- HTML/plain text support
- Template-based emails
Configuration:
{
"smtp_host": "smtp.example.com",
"smtp_port": 587,
"username": "your-username",
"password": "your-password",
"from_address": "[email protected]"
}SMS (Twilio)
| Property | Value |
|---|---|
| Type | sms |
| Provider | Twilio |
| Use | OTPs, urgent alerts |
Features:
- Worldwide coverage
- Character limits
- Delivery receipts
- Fallback support
Configuration:
{
"provider": "twilio",
"api_key_sid": "SK...",
"api_key_secret": "...",
"from_number": "+1234567890"
}WhatsApp (Meta Cloud API)
| Property | Value |
|---|---|
| Type | |
| Provider | Meta Cloud API |
| Use | Rich messages, OTPs |
Features:
- Rich formatting
- Media support
- Templates
- Lower cost than SMS internationally
Configuration:
{
"provider": "whatsapp",
"access_token": "...",
"phone_number_id": "...",
"business_account_id": "..."
}Telegram
| Property | Value |
|---|---|
| Type | telegram |
| Provider | Telegram Bot API |
| Use | Notifications, OTPs |
Features:
- No phone number needed
- Instant delivery
- Bot commands
- Webhook support
Configuration:
{
"provider": "telegram",
"bot_token": "1234567890:ABC..."
}Connector Manager
Responsibilities
- Load connectors from database
- Encrypt/decrypt configurations
- Route messages to appropriate provider
- Handle failures and fallbacks
Fallback Logic
func (m *Manager) SendWithFallback(ctx context.Context, msg *Message, priority []ConnectorType) error {
for _, ct := range priority {
conn := m.GetConnector(ct)
if conn != nil {
err := conn.Send(ctx, msg)
if err == nil {
return nil
}
// Log failure, try next
}
}
return ErrAllFailed
}OTP Delivery
Generation
func GenerateOTP() string {
code := make([]byte, 3)
rand.Read(code)
return fmt.Sprintf("%06d", binary.BigEndian.Uint32(code))[:6]
}Delivery Flow
- Generate 6-digit code
- Hash code for storage (Argon2)
- Send via connector
- Store session with expiry
- Verify input against hash
Rate Limiting
| Limit | Value |
|---|---|
| Max OTP requests | 5 per hour per survivor |
| Max verification attempts | 3 per OTP |
| OTP validity | 10 minutes |
Message Types
Notification Messages
Used for:
- Liveness checks
- Transfer initiated
- Access granted
- General alerts
OTP Messages
Used for:
- Survivor verification
- Host login (future)
- Account recovery (future)
Alert Messages
Used for:
- Urgent notifications
- Transfer cancellation
- Security alerts
Error Handling
Provider Errors
| Error | Handling |
|---|---|
| Invalid credentials | Mark connector as failed |
| Rate limited | Retry with backoff |
| Provider down | Fallback to next connector |
| Invalid destination | Return error to caller |
Retry Strategy
const maxRetries = 3
const retryDelay = time.Second * 2
for i := 0; i < maxRetries; i++ {
err := conn.Send(ctx, msg)
if err == nil {
return nil
}
if !isRetryable(err) {
return err
}
time.Sleep(retryDelay * time.Duration(i+1))
}Security
Credential Storage
- All credentials encrypted with master key
- Never logged or exposed in errors
- Encrypted at rest in database
Message Content
- No sensitive data in logs
- OTP content masked in some contexts
- TLS 1.3 for all external communication
Monitoring
Metrics
- Messages sent per connector
- Delivery success rate
- Average delivery time
- Error rates by type
Logging
- All send attempts logged
- Errors captured with context
- Performance metrics recorded
Adding New Connectors
Interface Implementation
type NewConnector struct {
config ConnectorConfig
}
func (c *NewConnector) Type() ConnectorType {
return "newconnector"
}
func (c *NewConnector) Send(ctx context.Context, msg *Message) error {
// Implementation
}
func (c *NewConnector) SendOTP(ctx context.Context, to, code string) error {
// Implementation
}
func (c *NewConnector) Test(ctx context.Context, destination string) error {
// Implementation
}Registration
func init() {
RegisterConnector("newconnector", func(cfg ConnectorConfig) Connector {
return &NewConnector{config: cfg}
})
}Next Steps
- Storage Providers — Storage backend abstraction
- Deployment - Connectors — Production connector setup