ifabula / sevola-interceptor-helper
Laravel database interceptor for automatic field encryption/decryption using Sevola SDK
Package info
gitlab.com/kasfi.tamiya/sevola-php-interceptor-helper
pkg:composer/ifabula/sevola-interceptor-helper
Requires
- php: ^8.2
- ifabula/sevola-sdk-php: ^1.0
- illuminate/database: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^9.0
- phpunit/phpunit: ^10.0
README
Zero-integration encryption/decryption for Laravel applications powered by the Sevola SDK.
Configure once in Sevola CMS, install this package, and every Eloquent model automatically honours the encryption rulesโno traits, no code changes.
Table of Contents
- Why the Helper Exists
- Key Features
- Requirements
- Installation
- Configuring the Helper
- Quick Start Checklist
- How the Helper Works
- Available Services
- Usage Examples
- Advanced Topics
- Troubleshooting
- FAQ
- Contributing & Support
Why the Helper Exists
Enterprises often need to encrypt Personally Identifiable Information (PII) without rewriting existing Laravel codebases. The Sevola PHP Interceptor Helper fetches field-level encryption rules from the Sevola Central Dashboard API, transparently encrypts data before it is written, and decrypts it again when readโso developers keep using Eloquent exactly as before.
Key Features
- ๐ True zero-integration โ encryption happens automatically for every Eloquent model; no traits or manual hooks required.
- ๐ง Configuration-driven โ encryption rules live in Sevola CMS, fetched securely via the Central Dashboard API.
- โก Global interception โ the service provider attaches framework-level listeners to
savingandretrievedevents. - ๐ฏ Column-level control โ pick exactly which columns are encrypted, per-table, per-database.
- ๐งฎ Algorithm aware โ supports FF1 format-preserving encryption and AES transit encryption.
- ๐ Encrypted API transport โ all communication with the Central Dashboard uses AES-256-GCM + HMAC-SHA256; no config is ever sent or received in plaintext.
- ๐๏ธ Two-layer caching โ encryption config (from the dashboard) and FF1 keys (from the Sevola API) are both cached, keeping response times fast even for large result sets.
- ๐ชต Rich logging โ optional structured logging for auditing and debugging.
- ๐ ๏ธ Manual APIs still available โ access
ConfigService,EncryptionService, andModelEncryptionManagerfor one-off jobs or raw queries. - ๐๏ธ Per-request ciphertext toggle โ opt in to encrypted responses using
SevolaContext.
Requirements
| Component | Minimum |
|---|---|
| PHP | 8.2 |
| Laravel | 11.x (works with L10 via manual provider registration) |
| Sevola SDK | ifabula/sevola-sdk-php (installed automatically) |
| Central Dashboard | Sevola Central Management API reachable from your server |
Installation
Require the package
composer require ifabula/sevola-interceptor-helper(Optional) Publish the configuration file
php artisan vendor:publish --tag=sevola-configThis creates
config/sevola.php. Publishing is recommended when you need to customise defaults (cache TTL, log channel, etc.).Laravel service provider
- Laravel 11+: auto-discovery registers the provider automatically.
- Laravel โค 10: add
Ifabula\SevolaInterceptor\SevolaServiceProvider::classto theprovidersarray inconfig/app.php.
Configuring the Helper
Environment variables
Add these entries to .env (or your secrets manager):
# โโ Required โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Sevola API โ used for FF1 / AES encryption operations
SEVOLA_API_KEY=your-api-key
SEVOLA_BASE_URL=https://your-sevola-api-url/
# Central Dashboard โ encryption config is fetched from here
CENTRAL_DASHBOARD_URL=https://your-central-dashboard-url/
# Shared AES-256 key for dashboard API transport (64 hex chars = 32 bytes)
# Must match DEALER_ENCRYPTION_SALT on the Central Dashboard server
DEALER_ENCRYPTION_SALT=your-64-char-hex-string
# โโ Optional โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# How long to cache the encryption config fetched from the dashboard (seconds)
SEVOLA_CACHE_TTL=300
# Cache the FF1/REST key from the Sevola API (strongly recommended for large result sets)
SEVOLA_ENABLE_SDK_CACHE=true
SEVOLA_SDK_CACHE_TTL=300
# Logging
SEVOLA_LOGGING=false
SEVOLA_LOG_CHANNEL=stack
SEVOLA_DEBUG=false
# Experimental raw-query interceptor (disabled by default; Eloquent listeners are sufficient)
SEVOLA_ENABLE_QUERY_LISTENER=false
# Feature control โ which interceptor directions are active (default: none / dormant)
SEVOLA_INTERCEPTOR_RUNNING_FEATURES=encrypt,decrypt
SEVOLA_ENABLE_SDK_CACHE=trueis strongly recommended for production. Without it, every column decryption makes a separate HTTP call to the Sevola API to fetch the FF1 key. With caching enabled, the key is fetched once and reused for the entire TTL window โ making decryption of large result sets orders of magnitude faster.
Sevola CMS configuration
Encryption rules are managed in Sevola CMS. For each table you want to protect:
- Open Encryption Config inside Sevola CMS.
- Create an entry specifying the database, table, columns to encrypt, and the algorithm.
- Set the entry status to ACTIVE.
The helper fetches this config from the Central Dashboard API on demand (cached). Once active, every Eloquent query touching that table automatically encrypts/decrypts the listed columns.
No code changes are needed in the Laravel project. As soon as the config is active and the helper is installed, the model data is protected.
Quick Start Checklist
- โ
composer require ifabula/sevola-interceptor-helper - โ
Add the four required env vars (
SEVOLA_API_KEY,SEVOLA_BASE_URL,CENTRAL_DASHBOARD_URL,DEALER_ENCRYPTION_SALT). - โ
Set
SEVOLA_ENABLE_SDK_CACHE=truefor production workloads. - โ
Ensure the Sevola CMS entry exists for each table (status =
ACTIVE). - โ (Laravel โค 10) Register the service provider manually.
- โ
Set
SEVOLA_INTERCEPTOR_RUNNING_FEATURES=encrypt,decryptto activate both directions (orencrypt/decryptalone for partial mode). - โถ๏ธ Use your Eloquent models exactly as before โ encrypted columns are handled automatically.
To verify quickly, enable logging (SEVOLA_LOGGING=true, SEVOLA_DEBUG=true) and inspect storage/logs/laravel.log. You should see [Sevola] entries confirming encryption/decryption activity for configured tables.
How the Helper Works
- Bootstrapping โ
SevolaServiceProviderregisters services and attaches listeners to global Eloquent events. - Config fetch โ when a model is first accessed,
ConfigServicecalls the Central Dashboard API (POST /dealer/encryption-config) with an AES-256-GCM encrypted + HMAC-signed payload. The response (also encrypted) is verified, decrypted, and cached. - On Save โ
Model::savingfires; the helper encrypts dirty columns listed in the config and writes the ciphertext back onto the model before the INSERT/UPDATE. - On Retrieve โ
Model::retrievedfires; the helper decrypts the relevant attributes and callssyncOriginal()so Laravel treats the decrypted values as the original state. - Key caching โ the FF1 key fetched from the Sevola API is cached in memory for the SDK cache TTL, so large result sets (hundreds of rows) only pay one key-fetch round-trip.
The helper does nothing for tables that have no active CMS config โ safe to deploy broadly without code toggles.
Available Services
| Service | Class | Responsibilities |
|---|---|---|
| Configuration | Ifabula\SevolaInterceptor\Services\ConfigService | Fetch and cache encryption config from the Central Dashboard. |
| Encryption | Ifabula\SevolaInterceptor\Services\EncryptionService | Encrypt/decrypt associative arrays via the Sevola SDK. |
| Model Manager | Ifabula\SevolaInterceptor\Services\ModelEncryptionManager | Core logic for Eloquent lifecycle event handling. |
| Query Interceptor (experimental) | Ifabula\SevolaInterceptor\Interceptors\QueryInterceptor | Low-level listener for raw query events. Disabled by default. |
All services are container singletons โ resolve with app(ConfigService::class), etc.
Usage Examples
1. Plain Eloquent usage (no code changes)
$user = App\Models\User::create([
'name' => 'Jane Doe',
'email' => 'jane@example.com', // stored encrypted in DB
'phone' => '555-1234', // stored encrypted in DB
]);
$user = App\Models\User::find($user->id);
$user->email; // "jane@example.com" (decrypted automatically)
$user->phone; // "555-1234"
2. Manual encryption/decryption for raw arrays
use Ifabula\SevolaInterceptor\Services\ConfigService;
use Ifabula\SevolaInterceptor\Services\EncryptionService;
$config = app(ConfigService::class)->getConfig('my_database', 'users');
if ($config?->isActive()) {
$payload = ['email' => 'john@example.com', 'phone' => '555-9999'];
$encrypted = app(EncryptionService::class)->encryptData($payload, $config);
$decrypted = app(EncryptionService::class)->decryptData($encrypted, $config);
}
3. Clearing configuration cache
app(ConfigService::class)->clearCache(); // all tables
app(ConfigService::class)->clearCache('my_database', 'users'); // specific table
4. Returning ciphertext for a specific query
use Ifabula\SevolaInterceptor\Support\SevolaContext;
// Default: decrypted
$records = MyModel::all();
// Encrypted values for this call only
$encrypted = SevolaContext::withReturnEncrypted(fn () => MyModel::all());
// Manual push/pop
SevolaContext::pushReturnEncrypted();
try {
$cipherRecords = MyModel::all();
} finally {
SevolaContext::pop();
}
Advanced Topics
Logging & Observability
SEVOLA_LOGGING=trueenables informational logs (provider loaded, dashboard requests, etc.).SEVOLA_DEBUG=trueadds granular debug logs (cache hits, attribute lists, payloads).- Use
SEVOLA_LOG_CHANNELto route logs to any Laravel channel (daily file, Slack, etc.).
Cache Strategy
- Config cache (
SEVOLA_CACHE_TTL) โ caches the column list fetched from the Central Dashboard per(database, table)pair. CallConfigService::clearCache()after changing CMS entries to invalidate immediately. - SDK key cache (
SEVOLA_ENABLE_SDK_CACHE,SEVOLA_SDK_CACHE_TTL) โ caches the FF1/REST key from the Sevola API. Without this, every column decryption makes a separate HTTP round-trip. Always enable in production.
Query Listener (experimental)
Flip SEVOLA_ENABLE_QUERY_LISTENER=true to activate QueryInterceptor. Intended for low-level query event hooks; the global Eloquent listeners are sufficient for most use cases.
Working with Query Builder / Raw SQL
Eloquent models auto-decrypt attributes. For direct DB::select() calls, decrypt arrays manually via EncryptionService. Bulk CSV imports can pre-encrypt using the same service before calling DB::table()->insert().
Troubleshooting
| Symptom | Possible Cause | Fix |
|---|---|---|
| No encryption or decryption at all | SEVOLA_INTERCEPTOR_RUNNING_FEATURES not set | Set to encrypt,decrypt (or the desired subset); default is dormant |
| Values remain encrypted on retrieve | CMS entry missing or INACTIVE | Set status to ACTIVE in Sevola CMS; clear config cache |
| No encryption on save | CMS entry missing; listeners not running | Enable debug logging; ensure provider is registered |
SEVOLA_API_KEY is not configured | .env missing key | Add key; run php artisan config:clear |
CENTRAL_DASHBOARD_URL is not configured | .env missing key | Add CENTRAL_DASHBOARD_URL |
DEALER_ENCRYPTION_SALT is not configured | .env missing key | Add 64-char hex salt matching the dashboard server |
Response signature verification failed | Salt mismatch between app and dashboard | Verify DEALER_ENCRYPTION_SALT matches the server value |
Decryption failed: ERROR: Request failed | Sevola API unreachable | Verify SEVOLA_BASE_URL and network connectivity |
| Slow responses on large result sets | SDK key cache disabled | Set SEVOLA_ENABLE_SDK_CACHE=true |
| Stale config after CMS change | Config still cached | Call app(ConfigService::class)->clearCache() |
Enable debug logging and watch storage/logs/laravel.log to diagnose issues. You will see entries such as:
[Sevola] Fetching config from central dashboard {"database":"my_db","table":"users"}
[Sevola] Config loaded from central dashboard {"columns":["email","phone"]}
[Sevola] Decrypting attributes during retrieved {"model":"App\\Models\\User"}
FAQ
Do I need to modify my Eloquent models?
No. The helper intercepts Eloquent lifecycle events globally. Keep your models exactly as they are.
How do I disable encryption temporarily?
Mark the CMS entry as INACTIVE and clear the helper cache. Existing ciphertext stays encrypted in the database.
Can I encrypt new tables without deploying code?
Yes. Add the table to Sevola CMS, set it ACTIVE, and the helper picks it up on the next cache miss.
What about queue workers or scheduled jobs?
As long as the job boots Laravel normally, the service provider runs and encryption stays active.
Does this helper change database schemas?
No. The helper only transforms values going into/out of the database. Schema migrations are unchanged.
Why is DEALER_ENCRYPTION_SALT needed?
All requests to the Central Dashboard are encrypted with AES-256-GCM and signed with HMAC-SHA256 using this salt. It must match the value configured on the Central Dashboard server. This ensures no encryption config is ever transmitted in plaintext.
Contributing & Support
- ๐ฆ Packagist: https://packagist.org/packages/ifabula/sevola-interceptor-helper
- ๐งโ๐ป Source: https://gitlab.com/kasfi.tamiya/sevola-php-interceptor-helper
- ๐งฐ Related SDK: https://packagist.org/packages/ifabula/sevola-sdk-php
Issues and feature requests are welcome on GitLab. For commercial support, please contact Ifabula directly.
Made with โค๏ธ by Ifabula.