redberry / mailbox-for-laravel
This is my package mailbox-for-laravel
Package info
github.com/RedberryProducts/mailbox-for-laravel
pkg:composer/redberry/mailbox-for-laravel
Fund package maintenance!
Requires
- php: ^8.3
- illuminate/contracts: ^10.0||^11.0||^12.0
- inertiajs/inertia-laravel: ^2.0
- spatie/laravel-data: ^4.18
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^2.9||^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1||^7.10.0
- orchestra/testbench: ^10.0.0||^9.0.0||^8.22.0
- pestphp/pest: ^2.0||^3.0
- pestphp/pest-plugin-arch: ^2.5||^3.0
- pestphp/pest-plugin-laravel: ^2.0||^3.0
- phpstan/extension-installer: ^1.3
- phpstan/phpstan-deprecation-rules: ^1.1||^2.0
- phpstan/phpstan-phpunit: ^1.3||^2.0
- spatie/laravel-ray: ^1.35
- dev-main
- v1.2.0
- v1.1.5
- v1.1.4
- v1.1.3
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- v0.0.5
- v0.0.4
- v0.0.3
- v0.0.2
- v0.0.1
- dev-dependabot/composer/inertiajs/inertia-laravel-tw-3.0
- dev-dependabot/github_actions/dependabot/fetch-metadata-3.0.0
- dev-dependabot/github_actions/ramsey/composer-install-4
This package is auto-updated.
Last update: 2026-03-30 08:30:32 UTC
README
A zero-configuration local email inbox for Laravel. Capture, inspect, and test emails without external services—like Mailtrap, but self-contained within your application.
Table of Contents
- Features
- Requirements
- Installation
- Configuration
- Usage
- Frontend Integration
- Storage Drivers
- Deploying on Staging / VPS / Docker
- Authorization & Security
- Testing
- Development
- Changelog
- Security Vulnerabilities
- Credits
- License
Features
✨ Core Features:
- Local Mailtrap-style inbox — Capture all outgoing emails in a beautiful web interface
- Zero external dependencies — No Mailtrap, Mailhog, or third-party services required
- Self-contained Inertia.js dashboard — Vue 3-powered UI that's completely isolated from your host application
- Works with any frontend stack — Compatible with Blade-only, Vue, React, Livewire, or existing Inertia apps
- Multiple storage drivers — Database (SQLite, MySQL, PostgreSQL) or file-based storage
- Message normalization — Structured capture of headers, recipients, attachments, HTML/text bodies
- Automatic retention policies — Configure message pruning to prevent disk bloat
- Authorization middleware — Gate-based access control for production safety
- Attachment support — View and download email attachments
- Mark as read/unread — Track which messages you've reviewed
- Delete functionality — Clear entire inbox or delete individual messages with confirmation dialogs
- Responsive UI — Beautiful TailwindCSS-based interface with dark mode support
- Developer-friendly — Auto-enabled in non-production environments
- Test helpers — Send test emails directly from the dashboard
Requirements
- PHP:
^8.3 - Laravel:
^10.0|^11.0|^12.0 - Node.js & NPM: Required only if rebuilding frontend assets (pre-built assets included)
Installation
1. Install via Composer
For development environments (recommended):
composer require redberry/mailbox-for-laravel --dev
For production (if you need to capture emails in production, NOT RECOMMENDED):
composer require redberry/mailbox-for-laravel
2. Run the Installation Command
php artisan mailbox:install
This command will:
- Publish frontend assets to
public/vendor/mailbox/ - Run database migrations (creates
mailbox_messagestable by default) - Set up the necessary configuration
Available Flags:
--dev— Link assets for development (watches for file changes)--force— Force overwrite existing published assets--refresh— Runmigrate:refreshinstead ofmigrate(⚠️ drops tables)
Examples:
# Standard installation php artisan mailbox:install # Force reinstall (overwrites existing assets) php artisan mailbox:install --force # Development mode with linked assets php artisan mailbox:install --dev # Fresh install with database reset php artisan mailbox:install --refresh
3. Configure Your Mail Driver Required:
Set your mail driver to mailbox to capture outgoing emails.
Add to your .env:
MAIL_MAILER=mailbox
4. Access the Dashboard
Visit the dashboard at:
http://localhost/mailbox
Or your configured route (see Configuration).
Note: The package is auto-discovered by Laravel. No manual service provider registration needed.
Configuration
After installation, the configuration file is available at config/mailbox.php.
Configuration Reference
<?php return [ /* |-------------------------------------------------------------------------- | Enable Mailbox |-------------------------------------------------------------------------- | | By default, the mailbox is enabled in all environments except production. | Set MAILBOX_ENABLED=true in production to capture emails. | */ 'enabled' => env('MAILBOX_ENABLED', env('APP_ENV') !== 'production'), /* |-------------------------------------------------------------------------- | Storage Configuration |-------------------------------------------------------------------------- | | Configure where captured emails are stored. Available drivers: | - database: Store in a dedicated database connection (SQLite by default) | - file: Store as JSON files on disk | */ 'store' => [ 'driver' => env('MAILBOX_STORE_DRIVER', 'database'), // Custom storage driver resolvers 'resolvers' => [ // 'custom' => fn() => new \App\CustomMessageStore, ], // File storage options 'file' => [ 'path' => env('MAILBOX_FILE_PATH', storage_path('app/mailbox')), ], // Database storage options 'database' => [ 'connection' => env('MAILBOX_DB_CONNECTION', 'mailbox'), 'table' => env('MAILBOX_DB_TABLE', 'mailbox_messages'), ], ], /* |-------------------------------------------------------------------------- | Message Retention |-------------------------------------------------------------------------- | | Automatically purge messages older than the specified number of seconds. | Default: 24 hours (86400 seconds). | */ 'retention' => [ 'seconds' => (int) env('MAILBOX_RETENTION', 60 * 60 * 24), ], /* |-------------------------------------------------------------------------- | Authorization Gate |-------------------------------------------------------------------------- | | Define which Laravel Gate ability is checked before allowing access | to the mailbox dashboard. Default: 'viewMailbox' | | Define in AuthServiceProvider: | Gate::define('viewMailbox', fn ($user) => $user->isAdmin()); | */ 'gate' => env('MAILBOX_GATE', 'viewMailbox'), /* |-------------------------------------------------------------------------- | Unauthorized Redirect |-------------------------------------------------------------------------- | | Where to redirect users who fail authorization. | Default: null (shows Laravel's 403 page) | */ 'unauthorized_redirect' => env('MAILBOX_REDIRECT', null), /* |-------------------------------------------------------------------------- | Dashboard Route |-------------------------------------------------------------------------- | | The URI prefix where the mailbox dashboard is accessible. | Default: 'mailbox' | */ 'route' => env('MAILBOX_DASHBOARD_ROUTE', 'mailbox'), /* |-------------------------------------------------------------------------- | Middleware Stack |-------------------------------------------------------------------------- | | Middleware applied to all mailbox routes. | Default: ['web'] | */ 'middleware' => ['web'], ];
Environment Variables
Add these to your .env file to customize behavior:
# Enable in production (default: auto-enabled in non-production) MAILBOX_ENABLED=true # Storage driver (database or file) MAILBOX_STORE_DRIVER=database # Database connection (for database driver) MAILBOX_DB_CONNECTION=mailbox MAILBOX_DB_TABLE=mailbox_messages # File storage path (for file driver) MAILBOX_FILE_PATH=/path/to/storage/mailbox # Message retention (in seconds, default: 24 hours) MAILBOX_RETENTION=86400 # Authorization gate MAILBOX_GATE=viewMailbox # Redirect on unauthorized access MAILBOX_REDIRECT=/login # Dashboard route prefix MAILBOX_DASHBOARD_ROUTE=mailbox
Database Configuration
By default, the package creates a separate SQLite database at storage/app/mailbox/mailbox.sqlite to avoid
cluttering your main database. This default connection is only created when no connection with the configured name
already exists in your config/database.php.
To use your own database connection, define a connection named mailbox (or any name) in config/database.php and
the package will use it instead of creating its own:
// config/database.php 'connections' => [ 'mailbox' => [ 'driver' => 'mysql', 'host' => env('MAILBOX_DB_HOST', '127.0.0.1'), 'database' => env('MAILBOX_DB_DATABASE', 'mailbox'), 'username' => env('MAILBOX_DB_USERNAME', 'root'), 'password' => env('MAILBOX_DB_PASSWORD', ''), ], ]
Or point to an existing connection (e.g., your app's main database):
MAILBOX_DB_CONNECTION=mysql # or pgsql, sqlsrv, etc.
This is particularly useful in containerized or VPS environments where SQLite may not be ideal (e.g., shared volumes, multi-container setups, or read-only filesystems).
Note: When using a non-SQLite connection, make sure to run
php artisan mailbox:installto create themailbox_messagesandmailbox_attachmentstables in that database.
Attachment Storage
Attachments are stored on a dedicated mailbox filesystem disk (defaults to storage/app/mailbox/). Like the database
connection, the default local disk is only created when no disk with the configured name already exists in your
config/filesystems.php.
To use remote storage (e.g., S3), define the disk in config/filesystems.php:
// config/filesystems.php 'disks' => [ 'mailbox' => [ 'driver' => 's3', 'bucket' => env('MAILBOX_S3_BUCKET'), 'region' => env('MAILBOX_S3_REGION', 'us-east-1'), // ... ], ]
Or point to a different disk name:
MAILBOX_ATTACHMENTS_DISK=s3
Usage
Dashboard Overview
The mailbox dashboard provides a modern email client interface with:
- Message list — All captured emails sorted newest-first
- Preview pane — View email content (HTML, plain text, raw source)
- Recipient filtering — Filter by To, Cc, Bcc recipients
- Read/Unread tracking — Mark messages as seen
- Attachment viewer — Download email attachments
- Test email sender — Send sample emails for testing
- Clear inbox — Remove all captured messages with confirmation
- Delete messages — Remove individual messages with confirmation
Accessing the Dashboard
Navigate to your configured route (default: /mailbox):
http://localhost/mailbox
http://yourapp.test/mailbox
https://staging.yourapp.com/mailbox
Sending Test Emails
Use the "Send Test Email" button in the dashboard, or programmatically:
use Illuminate\Support\Facades\Mail; use Illuminate\Mail\Message; Mail::raw('This is a test email', function (Message $message) { $message->to('recipient@example.com') ->subject('Test Email') ->from('sender@example.com'); });
Message Capture Flow
- Your application sends an email using Laravel's
Mailfacade - Mailbox Transport intercepts the outgoing message
- Message is normalized into a structured format (headers, body, attachments)
- Stored in configured driver (database or file system)
- Displayed in dashboard with full content and metadata
Behind the scenes:
// MailboxTransport::doSend() $payload = MessageNormalizer::normalize($original, $envelope, $raw, true); $key = $this->mailbox->store($payload);
Stored payload structure:
{
"from": "sender@example.com",
"to": [
"recipient@example.com"
],
"cc": [],
"bcc": [],
"subject": "Test Email",
"date": "2025-11-19T10:30:00+00:00",
"text": "Plain text body",
"html": "<html>HTML body</html>",
"attachments": [],
"raw": "Full RFC 822 message",
"timestamp": 1732017000,
"saved_at": "2025-11-19T10:30:00.000000Z",
"seen_at": null
}
Attachments
Email attachments are captured and stored alongside the message. Access them via:
- Dashboard UI: Click "View" on the attachment
- Direct download:
/mailbox/messages/{id}/attachments/{index}
Attachment metadata:
{
"filename": "document.pdf",
"content_type": "application/pdf",
"size": 102400,
"content": "base64-encoded-data"
}
Clearing the Inbox
From Dashboard:
- Click the "Clear Inbox" button in the filter bar (with trash icon)
- Confirm the action in the dialog that appears
- All messages will be permanently deleted
From Message Detail:
- Click the trash icon button in the top-right corner of the message preview
- Confirm the deletion in the dialog
- The specific message will be permanently deleted
Programmatically:
use Redberry\MailboxForLaravel\Facades\Mailbox; // Clear all messages Mailbox::clearAll(); // Delete a specific message Mailbox::delete($messageId);
Via Artisan (future feature):
php artisan mailbox:clear
Note: Both clear and delete operations show confirmation dialogs in the UI to prevent accidental data loss.
Frontend Integration
Architecture Overview
The mailbox uses Inertia.js + Vue 3 for its dashboard, but operates in complete isolation from your host application's frontend stack.
Key isolation mechanisms:
- Namespaced components — All Inertia renders use the
mailbox::prefix - Dedicated middleware —
mailbox.inertiamiddleware handles Inertia responses separately - Scoped assets — Built to
public/vendor/mailbox/with independent manifest - Separate Vue instance — Creates its own app, doesn't mount to your app's root
Compatibility
✅ Works alongside your existing frontend:
- Blade-only apps — No conflicts, package bundles its own JS
- Vue without Inertia — Separate Vue instances, no shared state
- React — No conflicts with React or other frameworks
- Existing Inertia apps — Uses different middleware and namespaces
- Livewire — Fully compatible
Storage Drivers
Database Driver (Default)
Stores messages in a dedicated SQLite database (database/mailbox.sqlite).
Pros:
- Fast queries and filtering
- ACID compliance
- Supports complex queries
- Easy to inspect with database tools
Configuration:
MAILBOX_STORE_DRIVER=database MAILBOX_DB_CONNECTION=mailbox MAILBOX_DB_TABLE=mailbox_messages
File Driver
Stores each message as a JSON file in storage/app/mailbox/.
Pros:
- No database required
- Easy to inspect/debug
- Portable (copy files between environments)
Configuration:
MAILBOX_STORE_DRIVER=file MAILBOX_FILE_PATH=/path/to/storage/mailbox
Custom Drivers
Implement the MessageStore contract:
namespace App\Storage; use Redberry\MailboxForLaravel\Contracts\MessageStore; class RedisMessageStore implements MessageStore { public function store(array $payload): string|int { // Implementation } public function get(string|int $key): ?array { // Implementation } // ... other methods }
Register in config/mailbox.php:
'store' => [ 'resolvers' => [ 'redis' => fn() => new \App\Storage\RedisMessageStore, ], ],
Use via .env:
MAILBOX_STORE_DRIVER=redis
Deploying on Staging / VPS / Docker
The package works out of the box on local development, but containerized or VPS environments may need additional configuration depending on your infrastructure.
Minimal Staging Setup
For a typical staging server, just set the mail driver and you're done:
MAIL_MAILER=mailbox MAILBOX_ENABLED=true # Already true by default when APP_ENV != production
Using MySQL/PostgreSQL Instead of SQLite
SQLite doesn't work well with shared volumes or multi-container setups. Point the mailbox at your existing database:
MAILBOX_DB_CONNECTION=mysql
Or define a dedicated connection in config/database.php (see Database Configuration).
Docker Setup
Dockerfile example:
# Build stage (or entrypoint script) RUN php artisan mailbox:install --force
docker-compose.yml example:
services: app: environment: - APP_ENV=staging - MAIL_MAILER=mailbox - MAILBOX_DB_CONNECTION=mysql # Use the app's MySQL instead of SQLite
If your container has a read-only filesystem for public/, publish assets during the Docker image build rather than
at runtime.
Environment Variables Reference
| Variable | Default | Description |
|---|---|---|
MAILBOX_ENABLED |
true (non-production) |
Master on/off switch |
MAILBOX_DB_CONNECTION |
mailbox (auto-created SQLite) |
Database connection name |
MAILBOX_STORE_DRIVER |
database |
Storage driver (database or file) |
MAILBOX_ATTACHMENTS_DISK |
mailbox (auto-created local) |
Filesystem disk for attachments |
MAILBOX_DASHBOARD_ROUTE |
mailbox |
URL prefix for the dashboard |
MAILBOX_GATE |
viewMailbox |
Gate ability for authorization |
MAILBOX_RETENTION |
86400 |
Message retention in seconds (24h) |
MAILBOX_REDIRECT |
null |
Redirect URL on unauthorized access |
Key point: When you define your own
mailboxdatabase connection or filesystem disk in your Laravel config, the package respects it and will not overwrite it with defaults.
Authorization & Security
Gate-Based Authorization
Access is controlled via Laravel's Gate system using the viewMailbox ability.
Default behavior: The package registers a default gate that allows access in local environments or when mailbox.enabled is true. If you define your own viewMailbox gate in your AuthServiceProvider, the package will not overwrite it.
Define a custom gate in AuthServiceProvider:
use Illuminate\Support\Facades\Gate; public function boot() { Gate::define('viewMailbox', function ($user) { return $user->isAdmin(); }); }
Or use a Policy:
Gate::define('viewMailbox', [MailboxPolicy::class, 'view']);
Staging / Non-Production Access
On staging or development servers, the default gate allows access automatically because MAILBOX_ENABLED defaults to
true in non-production environments. No additional configuration is needed.
For authenticated-only access on staging, define your own gate:
Gate::define('viewMailbox', function ($user) { return $user->hasRole('developer'); });
Production Considerations
⚠️ Security warnings:
- Captured emails may contain sensitive data (passwords, tokens, etc.)
- Always require authentication in production
- Consider IP whitelisting for staging environments
- Use
MAILBOX_ENABLED=falsein production unless necessary
Recommended production config:
# Disable by default MAILBOX_ENABLED=false # Enable only for admins MAILBOX_GATE=viewMailbox # Redirect unauthorized users MAILBOX_REDIRECT=/login
Testing
Running Tests
The package includes comprehensive test coverage using Pest.
# Run all tests composer test # Run with coverage report composer test-coverage # Run only unit tests ./vendor/bin/pest --filter=Unit # Run only feature tests ./vendor/bin/pest --filter=Feature
Test Coverage
Target coverage: 90%+ lines, 80%+ branches
# Generate HTML coverage report ./vendor/bin/pest --coverage --coverage-html=coverage # Fail if coverage drops below threshold ./vendor/bin/pest --coverage --min=90
Static Analysis
Run PHPStan for type safety:
composer analyse
# Or directly
./vendor/bin/phpstan analyse
Code Style
Format code with Laravel Pint:
composer format
# Or directly
./vendor/bin/pint
Writing Tests
Tests follow the repository structure:
tests/
├── Architecture/ # Arch tests for architectural rules
├── Feature/ # Integration tests for controllers, commands
└── Unit/ # Unit tests for services, transport, storage
Example test:
use Redberry\MailboxForLaravel\CaptureService; it('stores a message and returns a key', function () { $service = app(CaptureService::class); $key = $service->store([ 'from' => 'sender@example.com', 'subject' => 'Test', 'raw' => 'Full message', ]); expect($key)->toBeString(); expect($service->get($key))->toBeArray(); });
Development
Setting Up a Development Environment
The package uses Orchestra Testbench Workbench for local development.
1. Clone the repository:
git clone https://github.com/RedberryProducts/mailbox-for-laravel.git
cd mailbox-for-laravel
2. Install dependencies:
composer install npm install
3. Set up the database:
php artisan mailbox:install --dev
4. Start the development server:
# Terminal 1: Laravel dev server php artisan serve # Terminal 2: Vite dev server (hot reload) npm run dev
5. Visit the dashboard:
http://localhost:8000/mailbox
Workbench
The package includes a Workbench app for testing integration:
# Run Workbench php artisan serve # Access at http://localhost:8000
Workbench configuration:
workbench/
├── app/ # Test application code
├── bootstrap/ # Workbench bootstrap
├── config/ # Test config files
├── database/ # Test migrations/seeders
└── routes/ # Test routes
Building Frontend Assets
Development mode (with hot reload):
npm run dev
Production build:
npm run build
Link assets for development:
php artisan mailbox:install --dev
This creates symlinks instead of copying files, allowing hot module replacement.
Contribution Guidelines
We welcome contributions! Please follow these guidelines:
Code Standards:
- Follow Laravel coding style (PSR-12)
- Run
composer formatbefore committing - Ensure PHPStan passes:
composer analyse - Write tests for new features (90%+ coverage required)
Pull Request Process:
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make changes with tests and documentation
- Run tests:
composer test && composer analyse - Format code:
composer format - Commit with conventional commit messages:
feat: add message filtering - Push and open a Pull Request
Commit Convention:
feat:— New featuresfix:— Bug fixesdocs:— Documentation changestest:— Test additions/changesrefactor:— Code refactoringchore:— Maintenance tasks
Branch Naming:
feature/description— New featuresfix/description— Bug fixesdocs/description— Documentation updates
Changelog
All notable changes to this project are documented in CHANGELOG.md.
Security Vulnerabilities
If you discover a security vulnerability within this package, please email security@redberry.ge instead of using the issue tracker. All security vulnerabilities will be promptly addressed.
Credits
- Nika Jorjoliani — Creator & Maintainer
- Redberry — Development Agency
- All Contributors
License
The MIT License (MIT). Please see LICENSE.md for more information.