blackpig-creatif/sceau
Sceau is a Laravel package for cryptographically sealing and signing application data, helping you create tamper-evident tokens, protect payloads, and verify integrity with a simple, expressive API.
Sceau is a Filament plugin that registers named "tomes" (Grimoire entries) into a global registry, then surfaces them inside your panels. The mental model to hold onto: tomes are application-wide singletons, panels are just views onto them. Almost every confusion in this package comes from mixing those two scopes up.
Install and publish:
composer require <vendor>/sceau
php artisan vendor:publish --tag="sceau-config"
php artisan vendor:publish --tag="sceau-migrations"
php artisan migrate
Register the plugin on each panel that should expose it — app/Providers/Filament/AdminPanelProvider.php (and AppPanelProvider, etc.):
use Vendor\Sceau\SceauPlugin;
public function panel(Panel $panel): Panel
{
return $panel
->plugin(SceauPlugin::make());
}
Register your tomes once, in a service provider — not in the panel provider:
use Vendor\Sceau\SceauServiceProvider as BaseSceauProvider;
// or your own AppServiceProvider::booted(), guarded with a has() check
Grimoire::register(Tome::make('sceau')->...);
Where to look first: the config file (config/sceau.php) for defaults and the bundled tomes, then SceauServiceProvider::packageBooted() in the vendor tree — that method is the authoritative example of correct registration scope, and it's the pattern you should copy for your own tomes.
First use case: the built-in sceau tome on a single panel. Get that rendering before you touch multiple panels or custom tomes; it isolates install problems from registration-scope problems.
Multi-panel, one plugin class. The idiomatic setup is a shared plugin registration in every panel provider, with per-panel behaviour expressed through the plugin's fluent config rather than through duplicate registration:
SceauPlugin::make()
->tomes(['sceau', 'cachet']) // narrow what this panel sees
->navigationGroup('Administration'),
If you need genuinely different behaviour per panel (different access rules, different navigation), derive it from the current panel rather than branching at registration time:
SceauPlugin::make()
->visible(fn (): bool => Filament::getCurrentPanel()?->getId() === 'admin'),
Custom tomes. Implement the tome contract and register it in a provider's booted() (or register() if the registry is container-bound and not yet resolved). Keep the registration in one place — a dedicated SceauTomeServiceProvider scales far better than scattering Grimoire::register() calls across feature providers:
public function booted(): void
{
Grimoire::register(
Tome::make('facture')
->label('Factures')
->schema(fn (Tome $tome) => [...])
);
}
Config-driven registries. For apps with many tomes, drive registration from config so deployments can toggle modules without code changes:
foreach (config('sceau.tomes', []) as $id => $class) {
Grimoire::register($class::make($id));
}
This also makes the registry trivially testable — mutate config in the test, assert the resulting Grimoire contents.
Testing. Because tomes live in a global registry, test isolation matters. Assert against the registry after boot rather than assuming a clean slate:
it('registers the sceau tome once', function () {
expect(Grimoire::all())->toHaveCount(1);
});
If your test suite boots the application repeatedly in one process (Pest with RefreshDatabase is usually fine; custom bootstrapping may not be), add an explicit Grimoire::flush() in tearDown().
Integration tip: treat Sceau resources like any other Filament resource for authorization — policies on the underlying model still apply. Don't build a parallel permission layer inside the plugin config; you'll fight it.
The v2.0.1 fix — upgrade if you're multi-panel. In v2.0.0, SceauPlugin::register() ran once per panel, so two panels each with SceauPlugin produced:
InvalidArgumentException: A Tome with id 'sceau' is already registered
v2.0.1 moves registration to SceauServiceProvider::packageBooted() (once per application boot). If you pinned 2.0.0 and worked around it, remove the workaround — leaving a has() guard or a conditional register() in place is now dead code that will mask future registration bugs.
Don't re-register tomes in SceauPlugin::register(). Even on 2.0.1, that method still runs per panel. Any custom tome you registered there will throw the exact same exception the moment a second panel uses the plugin. Move it to a provider.
Idempotency is your safety net. If you're unsure whether something double-registers, Grimoire::has($id) before register() is a cheap guard — but use it as a diagnostic, not a permanent crutch. A guard that's always true hides the scope bug rather than fixing it.
Long-lived processes. Under Octane or in queue workers, the application boots once and lives. Registry mutation at runtime (registering a tome conditionally based on request state) will persist across requests. Keep registration deterministic and boot-time-only.
Config caching. php artisan config:cache freezes config('sceau.*') at deploy. If you drive tome registration from config, remember to re-cache on deploy — otherwise new tomes silently fail to appear in production while working fine locally.
Extension points worth knowing: the plugin's fluent setters for navigational/visibility concerns, the tome contract for custom entries, and SceauServiceProvider::packageBooted() as the documented hook for app-level registration. When in doubt about scope, ask "once per app or once per panel?" — the vendor's own service provider answers it.
How can I help you explore Laravel packages today?