// App-owned SMS service: the guarded egress layer over the dumb SMS transport, // analogous to *mail.Mailer over the mail transport. Callers use intent methods // (SendMFACode, ...) and never touch the transport directly, and every message // crosses a single capability-checked chokepoint. package sms import ( "context" "fmt" "atlas9.dev/c/core/iam" "atlas9.dev/c/demo/lib/access" smstransport "atlas9.dev/c/sms" ) // CapSmsSend is the app's egress boundary for the SMS channel. SMS is not // tenant-scoped, so it is checked system-wide, mirroring CapMailSend. var CapSmsSend = iam.NewCap("Sms_Send") // Sender composes application text messages and enforces the egress cap before // handing them to the transport. type Sender struct { Transport smstransport.Sender Guard access.Guard } func (s *Sender) SendMFACode(ctx context.Context, phone, code string) error { return s.send(ctx, phone, fmt.Sprintf("Your verification code is %s", code)) } // send is the chokepoint: every message crosses the egress boundary here, so the // cap is checked exactly once, in one place. func (s *Sender) send(ctx context.Context, phone, message string) error { if err := s.Guard.System(ctx, CapSmsSend); err != nil { return err } return s.Transport.Send(ctx, phone, message) }