Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/HypathStack/model-scribe/llms.txt

Use this file to discover all available pages before exploring further.

ModelScribe’s driver system is open for extension: any class that implements DriverInterface can act as a first-class audit driver. Whether you need to stream events to a webhook endpoint, push records into Elasticsearch, or fan out to a proprietary data warehouse, you can plug your implementation in without touching the package internals. Registration happens through DriverManager::extend(), which you call once from a service provider during application boot.

The DriverInterface contract

Every driver — built-in or custom — must implement two methods:
<?php

namespace HypathBel\ModelScribe\Contracts;

use HypathBel\ModelScribe\DTOs\LogEntry;

interface DriverInterface
{
    /**
     * Persist or dispatch a log entry.
     */
    public function log(LogEntry $entry): void;

    /**
     * Prune old entries according to the driver's retention policy.
     * Implementations that don't support pruning can be a no-op.
     */
    public function prune(): int;
}
  • log(LogEntry $entry): void — called once for every auditable event. Receive the fully-populated LogEntry value object and send it wherever your driver needs it.
  • prune(): int — called when model-scribe:prune runs. Return the number of records deleted. Drivers that do not manage their own storage should return 0.

Building a custom driver

The example below implements a webhook driver that POSTs every audit entry to a configurable URL. The constructor receives the driver’s config array directly, so any keys you define in config/model-scribe.php are available at $this->config.
<?php

use HypathBel\ModelScribe\Contracts\DriverInterface;
use HypathBel\ModelScribe\DTOs\LogEntry;
use Illuminate\Support\Facades\Http;

class WebhookDriver implements DriverInterface
{
    public function __construct(protected array $config) {}

    public function log(LogEntry $entry): void
    {
        Http::post($this->config['url'], [
            'event'      => $entry->event->value,
            'log_name'   => $entry->logName,
            'subject'    => $entry->subject?->getKey(),
            'properties' => $entry->properties,
        ]);
    }

    public function prune(): int
    {
        return 0; // Webhook driver doesn't manage storage
    }
}

Registering the driver

Call DriverManager::extend() inside the boot() method of any service provider. The closure receives the driver’s config array and the DriverManager instance, and must return a DriverInterface implementation.
<?php

use HypathBel\ModelScribe\DriverManager;
use HypathBel\ModelScribe\Facades\ModelScribe;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        ModelScribe::getDriverManager()->extend('webhook', function (array $config, DriverManager $manager) {
            return new WebhookDriver($config);
        });
    }
}
extend() returns the DriverManager instance (static), so multiple drivers can be registered fluently:
ModelScribe::getDriverManager()
    ->extend('webhook', fn (array $config, DriverManager $manager) => new WebhookDriver($config))
    ->extend('bigquery', fn (array $config, DriverManager $manager) => new BigQueryDriver($config));

Adding the driver to your config

Once registered, the driver is available by the name you passed to extend(). Add a matching entry to the drivers map in config/model-scribe.php:
// config/model-scribe.php

'drivers' => [
    'webhook' => [
        'driver' => 'webhook',
        'url'    => env('AUDIT_WEBHOOK_URL'),
    ],
],

'default' => 'webhook',
Any keys you add alongside driver are forwarded verbatim to your constructor’s $config array, so you can expose as many options as your implementation needs.

Flushing resolved driver instances

DriverManager caches resolved driver instances so they are only constructed once per request. In tests that register drivers or swap config values between cases, call flush() to clear the cache and force fresh construction:
use HypathBel\ModelScribe\Facades\ModelScribe;

ModelScribe::getDriverManager()->flush();
This is particularly useful in feature tests that assert different routing outcomes — for example, verifying that changing MODEL_SCRIBE_DRIVER routes entries to the correct backend.
extend() returns static (the DriverManager instance), enabling method chaining when you need to register several custom drivers in one call. The registration is stored immediately; you do not need to call any further methods to activate the driver.
You do not have to choose between your custom driver and the built-in database driver. Use the stack driver to fan every audit entry out to both simultaneously — your webhook gets real-time notifications while the database retains a queryable history:
'drivers' => [
    'webhook' => [
        'driver' => 'webhook',
        'url'    => env('AUDIT_WEBHOOK_URL'),
    ],
    'stack' => [
        'driver'  => 'stack',
        'drivers' => ['database', 'webhook'],
    ],
],
'default' => 'stack',

Build docs developers (and LLMs) love