Vulcan is a Ruby on Rails application that powers the Maryland Accessible Telecommunications (MAT) program. The platform bridges the communication gap for Maryland residents who have difficulty using standard telephones by connecting them with accessible telecommunications equipment and program support services.
At its core, Vulcan manages a comprehensive application workflow to verify constituent eligibility. Approved applicants can move through voucher or equipment-fulfillment workflows, redeem vouchers through authorized vendors, and receive supplementary services such as training and evaluation.
To ensure participants maintain access to modern technology as their needs evolve, the MAT program operates on a three-year lifecycle. Once an application cycle concludes, constituents may reapply to qualify for a new voucher, equipment support, and further training services.
- Features
- Technical Stack
- Architecture
- Documentation
- Prerequisites
- Installation
- Configuration
- Database and Seeding
- Running the App
- Default Development Users
- Testing
- Deployment
- Maintenance Tasks
- Contributing
- Constituent portal applications with autosave, inline validation, proof upload, and status tracking.
- Admin paper-intake workflow guarded by
Current.paper_context, with paper-specific validation and side-effect behavior. - Application statuses for draft, in-progress, proof collection, disability certification, approval, rejection, and archival.
- Explicit status transition history through
ApplicationStatusChangeandAuditEventService. - Eligibility rules for income proof, residency proof, ID proof, disability certification, and voucher/equipment fulfillment.
- Policy-driven three-year service window and training-session limits.
- Many-to-many guardian/dependent relationships through
GuardianRelationship. - Managing guardian assignment for dependent applications.
- Communication routing to the managing guardian when a dependent should not receive direct messages.
- Authorization scopes for viewing, editing, and managing applications across constituent, guardian, and admin contexts.
- Income, residency, and ID proof uploads with independent status tracking.
ProofAttachmentServiceas the shared attachment entry point across portal, admin, paper, and secure-form submissions.- Admin review through
ProofReview,Applications::ProofReviewer, and proof-specific rejection reasons. - Secure proof resubmission links for rejected or missing proof, including first rejection and re-rejection paths.
- Rate limits, validation, audit logging, and admin visibility for proof submission and review activity.
Secure public forms are tokenized, unauthenticated, time-boxed forms for a specific task. They are not portal sessions.
- Provider-info collection through
SecureRequestForm. - Proof resubmission through
SecureProofFormsController. - Disability certification upload through
MedicalProviderSecureRequestForm. - Vendor W9 resubmission through
VendorSecureRequestForm. - Token digests are stored instead of raw bearer tokens.
- Revoked, expired, submitted, and invalid links render neutral public responses.
- Expiration activity is recorded by
RecordSecureFormExpirationsJob.
- Disability certification status tracking from request through receipt, approval, or rejection.
- DocuSeal digital signing through
DocumentSigning::SubmissionServiceand the DocuSeal webhook. - Secure certification upload links for provider corrections or fallback upload.
- Staff-managed fax, mail, and admin upload workflows.
- Additional certification artifacts can be retained for review when a later DocuSeal or upload result arrives after a primary artifact.
- Snapshot fields on applications for fulfillment type and income-proof requirement.
- Voucher issuance for eligible voucher-fulfillment applications.
- Policy-driven voucher values by disability type and voucher validity period.
- Voucher status tracking for issued, active, redeemed, expired, and cancelled vouchers.
- Vendor redemption workflow with transaction records and audit history.
- Equipment-fulfillment paths with evaluation/training support where applicable.
- Vendor portal for voucher verification, redemption, transaction history, and authenticated W9 uploads.
- Admin W9 review and rejection workflow.
- Secure W9 resubmission links for rejected W9s.
- Vendor invoices generated from completed voucher transactions.
- Vendor-facing notifications for W9, invoice, payment, and voucher events.
- Trainer assignment, scheduling, completion, cancellation, and follow-up handling.
- Trainer dashboard and trainer session history.
- Evaluator assignment, scheduling, rescheduling, completion, and report submission.
- Evaluator dashboard with status filters.
- Activity history on training and evaluation records, including schedule and completion events.
- Training request queues driven by
applications.training_requested_at.
- Admin application dashboard with filters, search, proof queues, provider-info queues, training queues, and status views.
- Application detail pages for proof review, disability certification, secure request forms, training/evaluation status, vouchers, notes, and audit history.
- User management for constituents, guardians, administrators, trainers, evaluators, and vendors.
- Vendor management, W9 review, invoice review, and voucher administration.
- Policy and feature-flag management.
- Print queue support for paper correspondence.
- Draft pain-point analysis for application drop-off review.
- Database-backed email templates with English and Spanish seed data.
- Email delivery through Postmark.
- SMS and fax integrations through Twilio where configured.
- Paper letters through
PrintQueueItem. NotificationServicefor delivery records and notification workflows.AuditEventServicefor domain audit events.Applications::EventDeduplicationServicefor readable application timelines.- Application, proof, certification, secure-form, voucher, training, evaluation, vendor, and W9 history views.
- Session-based authentication with secure password handling.
- Two-factor authentication by WebAuthn, TOTP, and SMS.
- Account recovery workflow with admin review.
- PII filtering and Active Record encryption for sensitive fields.
- Request-scoped public secure forms that do not create user sessions or expose unrelated account data.
- Voucher redemption controls and audit trails.
- Solid Queue-backed jobs for email status updates, voucher expiration, vendor invoices, proof metrics, admin notifications, and secure form expiration events.
- Solid Cache and Solid Cable are configured through Rails 8 database-backed infrastructure.
- Ruby 4.0.2
- Rails 8.1.3
- PostgreSQL 17+
- Tailwind CSS
- Propshaft
- Stimulus and Turbo
- Solid Queue, Solid Cache, and Solid Cable
- Postmark for outbound email
- Twilio for SMS and fax status integrations
- DocuSeal for document signing
- Active Storage with local disk in development/test and S3-compatible storage in production
- Minitest with FactoryBot
- Service-oriented business logic with
BaseServiceand structured result objects. - STI user model for authenticating roles:
Users::Constituent,Users::Administrator,Users::Vendor,Users::Evaluator, andUsers::Trainer. - Request-scoped state through
Current.userandCurrent.paper_context. - Explicit lifecycle transitions through
Application#transition_status!and workflow reconciliation helpers. - Separate delivery records, audit records, and status-change records.
- Secure public request models for unauthenticated, bounded collection tasks.
- Stimulus controllers and Turbo for progressive frontend behavior.
- Minitest test coverage for models, services, controllers, jobs, mailers, and system flows.
The links below point to tracked repository documentation intended to be available on GitHub.
- Testing and Debugging Guide
- Service Architecture
- JavaScript Architecture
- Guardian Relationship System
- Paper Application Architecture
- User Management Features
- DocuSeal Integration Guide
- Application Workflow Guide
- Proof Review Process Guide
- Notification System
- Audit and Event Tracking
- Pain Point Tracking
- Email System
- Active Storage S3 Setup
- Authentication System
- PII Encryption
- Voucher Security Controls
- Required Reports and Audits
- Ruby 4.0.2
- Bundler
- PostgreSQL 17 or newer
- Node.js 24.x
- Yarn
- A Rails master key for shared credentials, or permission to generate local credentials for development
-
Clone the repository:
git clone https://github.com/mdod-tam/vulcan.git cd vulcan -
Install Ruby dependencies:
bundle install
-
Install JavaScript dependencies:
yarn install
-
Prepare credentials:
If you have the team master key, place it in
config/master.key.For an isolated local setup, generate local credentials:
EDITOR="vim" bin/rails credentials:edit bin/rails db:encryption:init EDITOR="vim" bin/rails credentials:edit
Add the generated Active Record encryption keys under
active_record_encryption. Do not commitconfig/master.key.
The default development database expects PostgreSQL on localhost with username postgres. Override with environment variables when needed:
DATABASE_USERNAME=postgres
DATABASE_PASSWORD=your_passwordDevelopment and test use local Active Storage by default.
Set these in production:
RAILS_MASTER_KEY=...
DATABASE_URL=postgres://...
APPLICATION_HOST=your-host.exampleOptional production database URLs:
QUEUE_DATABASE_URL=postgres://...
CACHE_DATABASE_URL=postgres://...
CABLE_DATABASE_URL=postgres://...Optional runtime settings:
RAILS_MAX_THREADS=10
SOLID_QUEUE_POOL_SIZE=10
WEB_CONCURRENCY=2
SOLID_QUEUE_IN_PUMA=true
WEBAUTHN_RP_ID=your-host.exampleConfigure these through Rails credentials or environment variables, depending on the integration:
-
Postmark API token for outbound email.
-
DocuSeal API key and optional base URL.
-
Twilio account settings for SMS and fax status integrations.
-
Webhook secret for signed webhooks.
-
S3-compatible storage:
S3_ACCESS_KEY_ID=... S3_SECRET_ACCESS_KEY=... S3_REGION=us-east-1 S3_BUCKET=...
Bucketeer-compatible alternatives are also supported:
BUCKETEER_AWS_ACCESS_KEY_ID=... BUCKETEER_AWS_SECRET_ACCESS_KEY=... BUCKETEER_AWS_REGION=... BUCKETEER_BUCKET_NAME=...
Create, migrate, and seed a local development database:
bin/rails db:create
bin/rails db:migrate
bin/rails db:seeddb:seed is for local development data. It clears existing local records, uses FactoryBot, loads products from test/fixtures/products.yml, creates demo users/applications/invoices, seeds policies, feature flags, email templates, rejection reasons, and attaches sample files. In production it intentionally skips the seed body.
Targeted seed tasks:
| Task | Use it for | Notes |
|---|---|---|
bin/rails db:seed_policies |
Policy rows for FPL, proof limits, waiting period, training limits, secure form timing, and voucher values. | Production-safe. Updates existing rows when values differ. |
bin/rails db:seed_feature_flags |
Default feature flags. | Currently seeds vouchers_enabled as disabled when the row is missing. |
bin/rails db:seed_manual_email_templates |
Database-backed email templates from db/seeds/email_templates/. |
Deletes existing EmailTemplate rows first, then reloads text/HTML templates. |
bin/rails db:seed_rejection_reasons |
Rejection reasons for proof and disability certification review. | Uses find_or_create_by!; safe to rerun for missing rows. |
There is no standalone product seed task. Products are loaded by the development seed from fixtures; production product data should be entered through the product admin workflow or a purpose-built import if one is added.
Do not use bin/rails db:seed for production setup. It is a development/demo seed and is written to avoid overwriting production data.
For a new production environment, run the targeted seeds below. Re-run db:seed_manual_email_templates only when you mean to replace the current template rows with the seeded versions.
bin/rails db:seed_policies
bin/rails db:seed_feature_flags
bin/rails db:seed_manual_email_templates
bin/rails db:seed_rejection_reasonsCreate the first admin user through the Rails console:
Users::Administrator.create!(
email: 'admin@example.org',
password: 'replace-with-a-secure-password',
first_name: 'Admin',
last_name: 'User',
email_verified: true
)Start the full development stack:
./bin/devThis starts the Rails web process, JavaScript build watcher, Tailwind build watcher, and Solid Queue worker through Procfile.dev.
To run Rails only:
bin/rails serverThe default local URL is:
http://localhost:3000
After bin/rails db:seed, these development users are available:
| Role | Password | |
|---|---|---|
| Admin | admin@example.com |
password123 |
| Constituent | user@example.com |
password123 |
| Constituent | user2@example.com |
password123 |
| Trainer | trainer@example.com |
password123 |
| Evaluator | evaluator@example.com |
password123 |
| Vendor | ray@testemail.com |
password123 |
| Vendor | teltex@testemail.com |
password123 |
| Legacy medical-provider fixture | medical@example.com |
password123 |
Providers do not need portal accounts for the current secure certification upload workflow; the medical-provider seed is retained as fixture data.
Run the full test suite:
bin/rails testRun system tests:
bin/rails test:systemRun all configured tests:
bin/rails test:allRun a focused test file:
bin/rails test test/models/application_test.rbRun a focused test line:
bin/rails test test/models/application_test.rb:42Run RuboCop on touched Ruby files:
bin/rubocop app/models/application.rbRun pre-deploy checks:
ruby bin/pre-deploy-checks-
Set required configuration:
heroku config:set RAILS_MASTER_KEY=... --app your-app-name heroku config:set APPLICATION_HOST=your-app-name.herokuapp.com --app your-app-name
-
Configure production database, Postmark, S3-compatible storage, DocuSeal, Twilio, and webhook secrets.
-
Deploy and migrate:
git push heroku main heroku run bin/rails db:migrate --app your-app-name
-
Seed baseline records:
heroku run bin/rails db:seed_policies --app your-app-name heroku run bin/rails db:seed_feature_flags --app your-app-name heroku run bin/rails db:seed_manual_email_templates --app your-app-name heroku run bin/rails db:seed_rejection_reasons --app your-app-name
-
Create an admin user through
heroku run bin/rails console. -
Ensure a worker process is running for Solid Queue, or set
SOLID_QUEUE_IN_PUMA=truewhen intentionally running jobs in Puma.
The repository includes config/deploy.yml for Kamal-based deployment. Update these before use:
serviceimageserversproxy.host- registry credentials
- production secrets, especially
RAILS_MASTER_KEY - database, storage, and integration settings
Then deploy with:
bin/kamal deployUse the configured aliases for console, logs, shell, and database console:
bin/kamal console
bin/kamal logs
bin/kamal shell
bin/kamal dbcThese are the custom tasks in this app. Run bin/rails -T when you need the full Rails task list.
bin/rails db:seed_policies
bin/rails db:seed_feature_flags
bin/rails db:seed_manual_email_templates
bin/rails db:seed_rejection_reasonsbin/rails features:list
bin/rails 'features:enable[vouchers_enabled]'
bin/rails 'features:disable[vouchers_enabled]'bin/rails data_integrity:find_orphaned_applications
bin/rails 'data_integrity:fix_orphaned_applications[report]'
bin/rails 'data_integrity:fix_orphaned_applications[assign,admin@example.org]'
bin/rails 'data_integrity:fix_orphaned_applications[delete]'
bin/rails data:update_status_names
bin/rails 'maintenance:void_duplicate_phone[443-653-1927]'Use the delete orphan cleanup only after reviewing the report. The phone cleanup keeps the lowest-ID user for the supplied phone number and clears that phone number from the rest.
bin/rails email_templates:audit
bin/rails letters:check_consistency
bin/rails letters:check_email_letter_consistency
bin/rails notification_tracking:check_all
bin/rails 'notification_tracking:analyze[123]'
bin/rails 'notification_tracking:fix_duplicates[123]'
DRY_RUN=true bin/rails notification_tracking:backfill
bin/rails 'notification_tracking:backfill[true,123]'notification_tracking:backfill works on disability certification request notifications. Pass DRY_RUN=true to inspect duplicate candidates without writing changes.
bin/rails vouchers:report_missingRecurring work is configured in config/recurring.yml and processed by Solid Queue.
- Create a feature branch.
- Make the smallest coherent change.
- Run focused tests for the changed behavior.
- Run RuboCop on touched Ruby files.
- Update documentation when behavior or setup changes.
- Open a pull request with the behavior change, verification performed, and any remaining risks.
- Maryland Accessible Telecommunications Program
- Contributors and maintainers