redberry / mailbox-for-laravel
This is my package mailbox-for-laravel
Fund package maintenance!
Redberry
Installs: 4 138
Dependents: 0
Suggesters: 0
Security: 0
Stars: 7
Watchers: 1
Forks: 1
Open Issues: 0
pkg:composer/redberry/mailbox-for-laravel
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.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-chore/update-readme
- dev-fix/minor-issues
- dev-copilot/add-full-attachment-support
- dev-feat/isolated-mail-into-iframe
- dev-copilot/add-clear-inbox-and-delete-message
- dev-copilot/add-polling-infinite-scroll
- dev-copilot/create-high-quality-readme
- dev-feat/support-db-driver
- dev-copilot/sub-pr-40
- dev-copilot/audit-fix-expand-test-suite
- dev-copilot/fix-failing-tests-and-cleanup
- dev-feat/restyling-inbox
- dev-copilot/implement-message-seen-functionality
This package is auto-updated.
Last update: 2025-11-21 09:51:46 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
- 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):
composer require redberry/mailbox-for-laravel
2. Run the Install 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
Configure you new mailer, that will use mailbox transport config/mail.php:
'mailers' => [ 'mailbox' => [ 'transport' => 'mailbox', ], ],
Note: Without this configuration, emails will be sent normally but won't be captured by the 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
The package uses a separate SQLite database by default to avoid cluttering your main database. This is configured in config/mailbox.php:
'store' => [ 'driver' => env('MAILBOX_STORE_DRIVER', 'database'), // ... 'database' => [ 'connection' => env('MAILBOX_DB_CONNECTION', 'mailbox'), 'table' => env('MAILBOX_DB_TABLE', 'mailbox_messages'), ], // ... ],
and then we inject 'mailbox' database connection into the config array, like so:
config([ 'database.connections.mailbox' => [ 'driver' => 'sqlite', 'database' => storage_path('app/mailbox/mailbox.sqlite'), 'prefix' => '', 'foreign_key_constraints' => true, ], ]);
If you want to override the default connection, you can add a new connection or use an existing one. All you need to do is add a new connection into your config/database.php:
'connections' => [ 'custom_connection' => [ 'driver' => 'sqlite', 'url' => env('MAILBOX_DB_URL'), 'database' => env('MAILBOX_DB_DATABASE', database_path('mailbox.sqlite')), 'prefix' => '', 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), ], ]
Then set the new custom connection in .env
MAILBOX_DB_CONNECTION=custom_connection
Or use your main database connection:
MAILBOX_DB_CONNECTION=mysql # or pgsql, sqlsrv, etc.
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
API Endpoints
The package registers the following HTTP endpoints:
| Method | Endpoint | Description |
|---|---|---|
GET |
/mailbox |
Load the dashboard (Inertia page) |
DELETE |
/mailbox/messages |
Delete all captured messages |
DELETE |
/mailbox/messages/{id} |
Delete a specific message |
POST |
/mailbox/test-email |
Send a test email |
POST |
/mailbox/messages/{id}/seen |
Mark a message as read/unread |
Example: Mark Message as Read
// From frontend (Inertia) router.post(`/mailbox/messages/${messageId}/seen`, { seen: true }) // Response: Updated message object { "id": "msg_123", "seen_at": "2025-11-19T10:30:00.000000Z", ... }
Example: Delete a Specific Message
// From frontend (Inertia) router.delete(`/mailbox/messages/${messageId}`) // Response: { "status": "deleted" }
Example: Clear All Messages
// From frontend (Inertia) router.delete('/mailbox/messages') // Response: Empty JSON {}
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
How It Works
Backend (Controller):
use Inertia\Inertia; return Inertia::render('mailbox::Dashboard', [ 'messages' => $messages, 'title' => 'Mailbox for Laravel', ]);
Frontend (Entry Point):
// resources/js/dashboard.js import { createInertiaApp } from '@inertiajs/vue3' createInertiaApp({ resolve: (name) => { const pageName = name.replace(/^mailbox::/, '') const pages = import.meta.glob('./Pages/**/*.vue', { eager: true }) return pages[`./Pages/${pageName}.vue`] }, setup({ el, App, props, plugin }) { createApp({ render: () => h(App, props) }) .use(plugin) .mount(el) }, })
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
Asset Building
Pre-built assets are included, but you can rebuild if needed:
# Install dependencies npm install # Build for production npm run build # Watch for changes (development) npm run dev
Built assets output to:
public/vendor/mailbox/manifest.jsonpublic/vendor/mailbox/assets/dashboard-[hash].jspublic/vendor/mailbox/assets/dashboard-[hash].css
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
Authorization & Security
Gate-Based Authorization
By default, access is controlled via Laravel's Gate system using the viewMailbox ability.
Define 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']);
Public Access (Development Only)
To disable authorization (e.g., for local development):
// In AuthServiceProvider::boot() Gate::define('viewMailbox', fn () => true);
Or create a custom gate in config:
MAILBOX_GATE=alwaysAllow
Gate::define('alwaysAllow', fn () => true);
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.