ninjadd / anima
Reactive webhook interceptor and synthetic request replay studio for Laravel.
Requires
- php: ^8.2
- illuminate/contracts: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
README
Introduction
Anima is a developer-centric, reactive webhook interceptor and synthetic request replay studio for Laravel applications. It embeds an interactive split-pane Workbench powered by Vue 3 and Monaco Editor directly into your web browser, giving developers an instant interface to capture incoming webhooks, inspect payload trees, mutate headers and JSON payloads, and replay synthetic requests directly through Laravel's HTTP Kernel without network latency.
Designed for modern API development, Anima includes built-in cryptographic signature bypassing, swappable storage drivers (Database, Isolated SQLite, and Redis), and non-recursive replay loop prevention.
Table of Contents
- Features and Workbench Capabilities
- Supported Storage Drivers
- Installation
- Configuration
- Usage Guide
- Security Considerations & Environment Guards
- Quickstart and Local Verification
- Multi-Driver Docker Compose Environment
- Running Automated Tests
- License
Features and Workbench Capabilities
1. Transparent Interception Middleware
- Non-Blocking Capture: Seamlessly intercepts incoming HTTP requests, recording raw request bodies, HTTP headers, response status codes, execution durations, and provider tags without altering upstream response flows.
- Tagged Webhook Categorization: Add provider tags directly to route middleware (
anima.capture:stripe,billing) to filter and segment events in the workbench. - Infinite Loop Guard: Automatically detects internal synthetic replays (
X-Anima-Replay: true) to prevent recursive capture loops.
2. In-Memory Kernel Request Synthesizer
- Zero-Network In-Memory Replays: Executes synthetic HTTP requests directly through Laravel's
Illuminate\Contracts\Http\Kernelin memory without making outbound cURL or socket calls. - Precise Duration Metrics: Captures real execution duration in milliseconds (
ms), HTTP response status codes, and formatted response bodies.
3. Cryptographic Signature Bypassing
- Environment-Aware Trait: The
BypassesReplaySignaturestrait enables seamless replay testing of modified payloads without failing external HMAC signature verification (e.g., Stripe, GitHub). - Production Guard: Hardcoded to reject signature bypassing in
productionenvironments to eliminate header spoofing vulnerabilities.
4. Swappable Polymorphic Storage Drivers
- Database Driver (
database): Stores webhook events in your primary database connection with configurable table names and timestamp indexing. - Isolated SQLite Driver (
sqlite): Automatically provisions an isolated SQLite database file and schema without modifying host application migrations. - Redis Driver (
redis): High-speed temporal storage utilizing Redis Hashes and Sorted Sets with automatic TTL key expiration.
5. Embedded Vue 3 & Monaco Editor Workbench
- Split-Pane Architecture: Left-pane scrollable event feed paired with a right-pane request/response inspection studio.
- Monaco JSON Editor: Full VS Code editing experience with syntax highlighting, automatic JSON formatting, and reactive document synchronization.
- Replay Result Slide-Over: Real-time modal detailing synthetic response status codes, execution durations, and formatted payload viewers with copy-to-clipboard actions.
6. Request Context & Header Inspector
- Dynamic Header Manipulation: Add, edit, remove, and reset request headers before triggering synthetic replays.
- URI & Method Customization: Test altered HTTP verbs (
POST,GET,PUT,PATCH,DELETE) and custom relative or absolute endpoints.
7. Light and Dark Theme Support
- Dual Visual Modes: Fully styled for both high-contrast Dark and modern Light modes.
- Monaco Theme Sync: Monaco Editor automatically synchronizes between
vs-darkandvswith persistentlocalStoragetheme preference.
Supported Storage Drivers
| Storage Driver | Schema Isolation | TTL Expiration | Query Filtering | Zero Migration Setup |
|---|---|---|---|---|
Database (database) |
Primary DB | Configurable via Purge | Supported | Requires Migration Stub |
SQLite (sqlite) |
Isolated File | Configurable via Purge | Supported | Auto-Provisioning |
Redis (redis) |
In-Memory | Native Key TTL | Supported | Auto-Provisioning |
Installation
Install the package via Composer:
composer require ninjadd/anima
Publish the package configuration file:
php artisan vendor:publish --tag=anima-config
Publish and run the database migration (required only when using the database driver):
php artisan vendor:publish --tag=anima-migrations php artisan migrate
Optionally publish compiled frontend assets and Blade views:
php artisan vendor:publish --tag=anima-assets --force php artisan vendor:publish --tag=anima-views
Configuration
In config/anima.php:
return [ /* |-------------------------------------------------------------------------- | Master Switch |-------------------------------------------------------------------------- | Enable or disable webhook interception and workbench access. */ 'enabled' => env('ANIMA_ENABLED', true), /* |-------------------------------------------------------------------------- | Route Path |-------------------------------------------------------------------------- | The base URI path where the Anima Workbench is accessible. */ 'path' => env('ANIMA_PATH', 'anima'), /* |-------------------------------------------------------------------------- | Route Middleware |-------------------------------------------------------------------------- */ 'middleware' => [ 'web', ], /* |-------------------------------------------------------------------------- | Storage Driver Configuration |-------------------------------------------------------------------------- | Supported drivers: "database", "sqlite", "redis" */ 'storage' => [ 'driver' => env('ANIMA_STORAGE_DRIVER', 'database'), 'database' => [ 'connection' => env('ANIMA_DB_CONNECTION', null), 'table' => env('ANIMA_DB_TABLE', 'anima_entries'), ], 'sqlite' => [ 'database' => env('ANIMA_SQLITE_PATH', storage_path('anima/anima.sqlite')), 'table' => env('ANIMA_SQLITE_TABLE', 'anima_entries'), ], 'redis' => [ 'connection' => env('ANIMA_REDIS_CONNECTION', 'default'), 'prefix' => env('ANIMA_REDIS_PREFIX', 'anima:entries'), 'ttl' => env('ANIMA_REDIS_TTL', 86400), ], ], ];
Usage Guide
Attaching Interception Middleware
Attach the anima.capture middleware alias to any webhook route. You can pass optional comma-separated tags to categorize entries:
use Illuminate\Support\Facades\Route; // Standard capture Route::post('/webhooks/payment', [PaymentWebhookController::class, 'handle']) ->middleware('anima.capture'); // Capture with provider tags Route::post('/webhooks/stripe', [StripeWebhookController::class, 'handle']) ->middleware('anima.capture:stripe,billing'); Route::post('/webhooks/github', [GitHubWebhookController::class, 'handle']) ->middleware('anima.capture:github,vcs');
Integrating Signature Bypass Trait
When replaying altered payloads, external HMAC signatures will naturally fail validation. Use the BypassesReplaySignatures trait inside your webhook verification middleware:
namespace App\Http\Middleware; use Anima\Traits\BypassesReplaySignatures; use Closure; use Illuminate\Http\Request; class VerifyStripeSignature { use BypassesReplaySignatures; public function handle(Request $request, Closure $next) { // Bypass signature verification during local/testing synthetic replays if ($this->isValidReplay($request)) { return $next($request); } $signature = $request->header('Stripe-Signature'); $payload = $request->getContent(); $secret = config('services.stripe.webhook_secret'); if (! $this->verifySignature($payload, $signature, $secret)) { return response()->json(['error' => 'Invalid signature'], 401); } return $next($request); } }
Security Considerations & Environment Guards
Caution
ENVIRONMENT SECURITY GUARD
The BypassesReplaySignatures trait is strictly locked to local and testing environments (app()->environment('local', 'testing')). In production environments, isValidReplay() is hardcoded to return false, preventing header spoofing attacks.
Quickstart and Local Verification
- Start your local development server:
php artisan serve
# or with Orchestra Testbench
./vendor/bin/testbench serve --port=8000
- Open your browser and navigate to:
http://localhost:8000/anima
- Send a test webhook from your terminal:
curl -X POST http://localhost:8000/api/webhooks/stripe \ -H "Content-Type: application/json" \ -H "Stripe-Signature: t=123,v1=test_sig" \ -d '{"id": "evt_123", "type": "payment_intent.succeeded", "data": {"object": {"amount": 4900, "currency": "usd"}}}'
- The webhook event will appear in the Captured Events feed. Select it, modify the JSON payload in Monaco Editor, adjust headers, and click Synthesize & Replay.
Multi-Driver Docker Compose Environment
A multi-driver Docker Compose stack is bundled for testing across storage backends:
# Start Redis, PostgreSQL, MySQL, and MariaDB containers docker compose up -d # Stop containers docker compose down
Services included:
- Redis 7 on port
6379 - PostgreSQL 16 on port
5432 - MySQL 8.0 on port
3306 - MariaDB 11 on port
3307
Running Automated Tests
Run the PHPUnit test suite:
./vendor/bin/phpunit
All 34 tests (195 assertions) verify:
- Storage drivers (
DatabaseStorageDriver,SqliteStorageDriver,RedisStorageDriver) andStorageManager. - Webhook capture middleware with route tags and synthetic replay loop prevention.
- Kernel request synthesizer in-memory dispatch and metric tracking.
- Signature bypassing safety checks and environment guards.
- Workbench HTTP controllers, Blade templates, asset streaming, and REST API endpoints.
License
This package is open-sourced software licensed under the MIT License.