12 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
MySMSAPio is an SMS Gateway Backend API - a Rails 8.0 REST API and WebSocket server for managing SMS messaging through Android gateway devices. It provides programmatic SMS capabilities with OTP management, webhooks, and real-time WebSocket communication.
The project includes a web-based admin interface for managing API keys, monitoring SMS logs, and viewing gateway status. See ADMIN_INTERFACE.md for detailed documentation.
Tech Stack
- Framework: Rails 8.0.3 (API-only mode)
- Ruby: 3.4.7
- Database: PostgreSQL 14+
- Cache/Queue: Redis 7+
- Background Jobs: Sidekiq 7 with sidekiq-cron
- WebSocket: Action Cable (Redis adapter)
- Authentication: API Keys with SHA256 hashing
- Server: Puma with Thruster for production
- Deployment: Kamal (Docker-based deployment)
- Code Quality: RuboCop with rails-omakase configuration, Brakeman for security
SMS Gateway Dependencies
- redis: WebSocket, caching, background jobs
- sidekiq: Background job processing
- sidekiq-cron: Scheduled jobs (health checks, cleanup)
- jwt: API authentication tokens
- rack-cors: Cross-origin resource sharing
- phonelib: Phone number validation
- rotp: OTP generation
- httparty: Webhook HTTP requests
- pagy: Pagination
Development Commands
Initial Setup
bin/setup
This will:
- Install dependencies
- Prepare the database
- Clear logs and temp files
- Start the development server
To skip auto-starting the server:
bin/setup --skip-server
Running the Application
bin/dev
# or
bin/rails server
Database
Create and migrate databases:
bin/rails db:create
bin/rails db:migrate
Prepare database (create + migrate + seed):
bin/rails db:prepare
Reset database:
bin/rails db:reset
Database console:
bin/rails dbconsole
# or via Kamal:
bin/kamal dbc
Testing
Run all tests:
bin/rails test
Run specific test file:
bin/rails test test/models/your_model_test.rb
Run specific test by line number:
bin/rails test test/models/your_model_test.rb:42
System tests:
bin/rails test:system
Tests run in parallel using all processor cores by default.
Code Quality
Run RuboCop:
bin/rubocop
Auto-correct RuboCop violations:
bin/rubocop -a
Run Brakeman security scanner:
bin/brakeman
Rails Console
bin/rails console
# or via Kamal:
bin/kamal console
Background Jobs (Sidekiq)
Start Sidekiq:
bundle exec sidekiq
View scheduled jobs:
bundle exec sidekiq-cron
Scheduled jobs run automatically:
CheckGatewayHealthJob: Every minuteCleanupExpiredOtpsJob: Every 15 minutesResetDailyCountersJob: Daily at midnight
Asset Management
Precompile assets:
bin/rails assets:precompile
Clear compiled assets:
bin/rails assets:clobber
Deployment (Kamal)
Deploy application:
bin/kamal deploy
View logs:
bin/kamal logs
Access remote shell:
bin/kamal shell
View configuration:
bin/kamal config
The deployment configuration is in config/deploy.yml. Production uses Docker containers with SSL enabled via Let's Encrypt.
Database Architecture
Development/Test
Single PostgreSQL database per environment:
- Development:
my_smsa_pio_development - Test:
my_smsa_pio_test
Production
Multi-database setup for performance isolation:
- Primary: Main application data
- Cache: Database-backed cache via Solid Cache (migrations in
db/cache_migrate/) - Queue: Job queue via Solid Queue (migrations in
db/queue_migrate/) - Cable: WebSocket connections via Solid Cable (migrations in
db/cable_migrate/)
Each database shares connection pooling configuration but maintains separate migration paths.
Application Structure
The application follows standard Rails 8 conventions with the modern "Omakase" stack:
- app/: Standard Rails application code (models, controllers, views, jobs, mailers, helpers)
- config/: Configuration files including multi-database setup
- db/: Database schemas with separate migration directories for each database role
- lib/: Custom library code (autoloaded via
config.autoload_lib) - test/: Minitest-based test suite with system tests using Capybara + Selenium
Key Configuration Details
Module Name
The Rails application module is MySmsaPio (defined in config/application.rb).
Environment Variables
Required:
DATABASE_URL: PostgreSQL connection stringREDIS_URL: Redis connection string (used for Action Cable, Sidekiq, caching)SECRET_KEY_BASE: Rails secret keyRAILS_ENV: Environment (development, test, production)
Optional:
ALLOWED_ORIGINS: CORS allowed origins (default:*)DEFAULT_COUNTRY_CODE: Default country for phone validation (default:US)RAILS_LOG_LEVEL: Logging level
Docker & Production
The application is containerized using a multi-stage Dockerfile optimized for production:
- Base Ruby 3.4.7 slim image
- Uses Thruster as the web server (listens on port 80)
- Non-root user (rails:rails, uid/gid 1000)
- Entrypoint handles database preparation via
bin/docker-entrypoint - Assets precompiled during build
Code Style
Follow RuboCop Rails Omakase conventions. Configuration is minimal, inheriting from rubocop-rails-omakase gem.
Health Checks
The application includes a health check endpoint:
GET /up
Returns 200 if app boots successfully, 500 otherwise. Used by load balancers and monitoring.
SMS Gateway Specific Information
Database Models
Gateway (app/models/gateway.rb):
- Represents Android SMS gateway devices
- Tracks connection status, heartbeat, message counts
- Generates and stores API keys (hashed with SHA256)
SmsMessage (app/models/sms_message.rb):
- Stores all SMS messages (inbound and outbound)
- Auto-generates unique message IDs
- Validates phone numbers using Phonelib
- Triggers SendSmsJob on creation for outbound messages
OtpCode (app/models/otp_code.rb):
- Generates 6-digit OTP codes
- Enforces rate limiting (3 per phone per hour)
- Auto-expires after 5 minutes
- Tracks verification attempts (max 3)
WebhookConfig (app/models/webhook_config.rb):
- Configures webhooks for SMS events
- Signs payloads with HMAC-SHA256
- Supports retry logic
ApiKey (app/models/api_key.rb):
- Client API keys for application access
- Permissions-based access control
- Tracks usage and expiration
API Controllers
Gateway APIs (app/controllers/api/v1/gateway/):
RegistrationsController: Register new gateway devicesHeartbeatsController: Keep-alive from gatewaysSmsController: Report received SMS and delivery status
Client APIs (app/controllers/api/v1/):
SmsController: Send/receive SMS, check statusOtpController: Generate and verify OTP codesAdmin::GatewaysController: Manage gateway devicesAdmin::StatsController: System statistics
WebSocket Communication
GatewayChannel (app/channels/gateway_channel.rb):
- Real-time bidirectional communication with gateway devices
- Authenticated via API key digest
- Handles:
- Gateway connection/disconnection
- Heartbeat messages
- Delivery reports
- Inbound SMS notifications
- Outbound SMS commands
Connection (app/channels/application_cable/connection.rb):
- Authenticates WebSocket connections
- Verifies gateway API keys
Background Jobs
Processing Jobs:
SendSmsJob: Routes outbound SMS to available gateways via WebSocketProcessInboundSmsJob: Triggers webhooks for received SMSRetryFailedSmsJob: Retries failed messages with exponential backoffTriggerWebhookJob: Executes webhook HTTP requests
Scheduled Jobs (config/sidekiq_cron.yml):
CheckGatewayHealthJob: Marks offline gateways (every minute)CleanupExpiredOtpsJob: Deletes expired OTP codes (every 15 minutes)ResetDailyCountersJob: Resets daily message counters (daily at midnight)
Admin Web Interface
Admin Authentication (app/models/admin.rb, app/controllers/admin/):
- Session-based authentication with bcrypt password hashing
- Access URL:
/admin/login - Default credentials (development): admin@example.com / password123
Admin Features:
- Dashboard (
/admin/dashboard): Real-time statistics and recent activity - API Keys Management (
/admin/api_keys): Create, view, and revoke API keys - SMS Logs (
/admin/logs): Monitor messages with advanced filtering - Gateway Management (
/admin/gateways): View and manage gateway devices
Admin Controllers:
Admin::BaseController: Base controller with authenticationAdmin::SessionsController: Login/logoutAdmin::DashboardController: Dashboard with statsAdmin::ApiKeysController: API key CRUD operationsAdmin::LogsController: SMS logs with filteringAdmin::GatewaysController: Gateway management
See ADMIN_INTERFACE.md for complete documentation.
Authentication & Security
API Key Types:
- Gateway Keys (
gw_live_...): For Android gateway devices - Client Keys (
api_live_...): For application APIs - Admin Access: Session-based web authentication
Authentication Flow:
- API keys passed in
Authorization: Bearer <key>header - Keys are hashed with SHA256 before storage
- Admin passwords hashed with bcrypt
- Concerns:
ApiAuthenticatable,RateLimitable
Rate Limiting:
- Implemented via Redis caching
- OTP: Max 3 per phone per hour
- SMS Send: 100 per minute per API key
- Customizable per endpoint
Key Files
Models: app/models/{gateway,sms_message,otp_code,webhook_config,api_key}.rb
Controllers: app/controllers/api/v1/**/*_controller.rb
Jobs: app/jobs/{send_sms_job,process_inbound_sms_job,retry_failed_sms_job,trigger_webhook_job,check_gateway_health_job,cleanup_expired_otps_job,reset_daily_counters_job}.rb
Channels: app/channels/{gateway_channel,application_cable/connection}.rb
Concerns: app/controllers/concerns/{api_authenticatable,rate_limitable}.rb, app/models/concerns/metrics.rb
Config:
config/routes.rb: API routesconfig/cable.yml: Action Cable (Redis)config/sidekiq_cron.yml: Scheduled jobsconfig/initializers/{cors,sidekiq,phonelib,pagy}.rb
Common Development Tasks
Generate new API key:
# In Rails console
result = ApiKey.generate!(name: "My App", permissions: { send_sms: true, receive_sms: true })
puts result[:raw_key] # Save this immediately!
Register gateway manually:
# In Rails console
gateway = Gateway.new(device_id: "my-device-001", name: "My Gateway")
api_key = gateway.generate_api_key!
puts api_key # Save this immediately!
Check gateway status:
# In Rails console
Gateway.online.each { |g| puts "#{g.name}: #{g.status}" }
View pending messages:
# In Rails console
SmsMessage.pending.count
SmsMessage.failed.each { |msg| puts "#{msg.message_id}: #{msg.error_message}" }
Test WebSocket connection:
# Use wscat or similar WebSocket client
wscat -c "ws://localhost:3000/cable?api_key=gw_live_your_key_here"
Important Notes
- Redis is required for Action Cable, Sidekiq, and caching to work
- All phone numbers are validated and normalized using Phonelib
- Gateway devices must send heartbeats every 2 minutes or they'll be marked offline
- Outbound SMS messages are queued and sent asynchronously via Sidekiq
- Failed messages retry automatically up to 3 times with exponential backoff
- OTP codes expire after 5 minutes and allow max 3 verification attempts
- All API keys are SHA256 hashed - raw keys are only shown once during creation