edrisaturay/filament-starter-minimal

Minimal Filament v5 starter plugin: essential panel plugins only (Shield-first, extensible registry).

Maintainers

Package info

github.com/edrisaturay/filament-starter-minimal

pkg:composer/edrisaturay/filament-starter-minimal

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v2.0.0 2026-05-05 04:18 UTC

This package is auto-updated.

Last update: 2026-05-05 04:20:20 UTC


README

A Laravel package that ships a small, Filament v5-compatible “starter” layer: a panel plugin (FilamentStarterMinimalPlugin), a plugin registry (Shield-first), database-backed overrides, cached plugin state, and Filament resources for platform administration (plugin management, panel snapshots, audit log).

It is intentionally minimal compared to edrisaturay/filament-starter: fewer bundled plugins, no multi-plugin publish wizard, and a smaller doctor surface—but it still ships Artisan commands in the same spirit as the full starter (install, update, doctor, safe-mode).

Artisan commands

These mirror the full starter commands with a minimal-starter: prefix and lighter behavior.

Full starter (edrisaturay/filament-starter) This package
php artisan starter:install php artisan minimal-starter:install
php artisan starter:update php artisan minimal-starter:update
php artisan starter:doctor php artisan minimal-starter:doctor
php artisan starter:safe-mode on|off php artisan minimal-starter:safe-mode on|off

minimal-starter:install

  • Optionally publishes config (--publish-config, or interactive confirm when not --no-interaction).
  • Runs php artisan migrate --force.
  • Calls PanelSnapshotManager::snapshot() (snapshots + plugin override sync).
php artisan minimal-starter:install
php artisan minimal-starter:install --publish-config --no-interaction

minimal-starter:update

Runs migrate --force, then PanelSnapshotManager::snapshot(). Use after deploy or dependency updates.

php artisan minimal-starter:update

minimal-starter:doctor

Runs health checks: database connectivity, minimal migrations present, Filament / Shield classes, snapshot row count, each managed_panels id registered with Filament, and that each registry plugin class exists.

Exit codes:

  • 0 — every check passed or only warning-level findings
  • 1 — at least one critical finding (DB unreachable, Filament missing, registered plugin's class not autoloadable, etc.)

warning-level findings (e.g. a managed panel id that isn't registered with Filament yet, zero snapshots) are reported but do not fail the command — operators can wire CI on doctor || exit 1 without false negatives.

php artisan minimal-starter:doctor

minimal-starter:safe-mode

When on, plugins marked dangerous_to_disable in the registry are forced enabled in resolved state (same idea as the full starter). Implemented via cache key starter_minimal_safe_mode_active, optional env STARTER_MINIMAL_SAFE_MODE, or config filament-starter-minimal.safe_mode. Toggling clears plugin state cache for all Filament panels.

php artisan minimal-starter:safe-mode on
php artisan minimal-starter:safe-mode off

Note: In testing env, safe mode reads as inactive (matches full starter behavior for tests).

Requirements

  • PHP ^8.3
  • Laravel 12 (illuminate/support ^12)
  • Filament ^5 (filament/filament)
  • Filament Shield ^4 (bezhansalleh/filament-shield)

Your app should use a Spatie-style role on the authenticated user (e.g. hasRole('super_admin')) for the built-in Platform resources.

Installation

Composer

composer require edrisaturay/filament-starter-minimal

Or add a path repository while developing:

{
    "repositories": [
        {
            "type": "path",
            "url": "packages/edrisaturay/filament-starter-minimal",
            "options": { "symlink": true }
        }
    ],
    "require": {
        "edrisaturay/filament-starter-minimal": "dev-main"
    }
}

The package auto-registers FilamentStarterMinimalServiceProvider via Composer’s extra.laravel.providers.

Configuration (optional)

Publish the config file to customize panel IDs, defaults, or the super-admin role:

php artisan vendor:publish --tag=filament-starter-minimal-config

Migrations

Migrations are loaded from the package automatically (loadMigrationsFrom). Run:

php artisan migrate

If you prefer copies of migration files in your app (e.g. for customization), publish them:

php artisan vendor:publish --tag=filament-starter-minimal-migrations
php artisan migrate

Quick start

1. Register the panel plugin

In your PanelProvider (or wherever you configure the panel), add one plugin entry. Do not also register FilamentShieldPlugin::make() separately unless you intend to load Shield twice.

use EdrisaTuray\FilamentStarterMinimal\Filament\FilamentStarterMinimalPlugin;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugins([
            FilamentStarterMinimalPlugin::make(),
        ]);
}

2. Align config with your panel ID

Edit config/filament-starter-minimal.php (or the merged defaults):

  • Set managed_panels to every Filament panel id that should use the registry and receive registry-driven plugins (e.g. ['admin', 'staff']).
  • Under plugin_defaults, add a block for each of those panel IDs and each plugin key returned by PluginRegistry::getPlugins() (see Extending the registry).

If a panel’s id is not in managed_panels, PlatformPanelFactory will not attach registry plugins to that panel (but the Filament resources are still registered on whichever panels load FilamentStarterMinimalPlugin—restrict access with policies / canViewAny() as needed).

3. Seed snapshots and plugin override rows

The Plugin Management UI expects panel snapshots (and sync populates overrides). After your panels are registered, call:

use EdrisaTuray\FilamentStarterMinimal\Support\PanelSnapshotManager;

PanelSnapshotManager::snapshot();

Typical places: AppServiceProvider::boot() (after Filament is ready), a dedicated Artisan command, or your deployment hook once per environment.

4. Filament Shield

Follow Shield’s own docs: run shield:install, configure config/filament-shield.php, generate permissions, and assign the super admin role configured in filament-starter-minimal.superadmin.role (default super_admin) to users who should access Platform resources.

What this package provides

Area Description
FilamentStarterMinimalPlugin Filament Plugin (getId(): 'filament-starter-minimal'). Registers Platform resources and runs PlatformPanelFactory::build() for registry plugins.
PluginRegistry Declares plugins (key, label, installer closure, defaults, dangerous_to_disable, Composer package name, plugin class).
PluginStateResolver Merges config defaults with DB overrides, caches result per panel (starter_minimal_plugins_{panelId}).
PluginSyncManager Ensures every registry key has a row per managed panel; removes stale rows; writes audit entries; clears cache.
PanelSnapshotManager Stores panel metadata JSON; triggers PluginSyncManager::sync().
Filament resources Plugin Management, Panel Snapshots, Audit Log (super-admin gated).

Configuration reference

File: config/filament-starter-minimal.php (merged as filament-starter-minimal.*).

Key Purpose
safe_mode When true (or env STARTER_MINIMAL_SAFE_MODE=true), dangerous registry plugins are forced on. Usually toggled via minimal-starter:safe-mode (cache).
superadmin.role Role name checked by Platform resources (hasRole()). Default: super_admin.
managed_panels Panel IDs that receive registry installers via PlatformPanelFactory.
plugin_defaults Nested: panel_idplugin_keyenabled, options. Used before DB overrides.

Example for two panels:

'managed_panels' => ['admin', 'staff'],

'plugin_defaults' => [
    'admin' => [
        'filament-shield' => [
            'enabled' => true,
            'options' => [],
        ],
    ],
    'staff' => [
        'filament-shield' => [
            'enabled' => true,
            'options' => [],
        ],
    ],
],

Database schema

All tables use the starter_minimal_ prefix so this package can coexist with edrisaturay/filament-starter (which uses starter_* without _minimal_).

Table Purpose
starter_minimal_panel_plugin_overrides Per-panel, per-plugin enabled, is_dangerous, options. Also options_version (currently always 1, reserved for future schema migrations) and updated_by_user_id (reserved; not yet auto-set by sync — populate manually if needed). Unique on (panel_id, plugin_key).
starter_minimal_panel_snapshots Latest known Filament panel metadata (path, domains, middleware, and the consumer panel's hasTenancy() flag — informational only; this package itself is not multi-tenant aware).
starter_minimal_audit_logs Append-only log for plugin sync and override changes. Immutable — update/delete throw at the model layer. actor_user_id is auto-injected from auth()->id() and is not mass-assignable.

Dangerous plugins: If is_dangerous is true, the PanelPluginOverride model forces enabled to stay true (same behavior as the full starter).

Filament resources (navigation: Platform)

Resource Access Notes
Plugin Management Super admin only CRUD overrides; Sync from Registry; Clear Plugin Cache.
Panel Snapshots Super admin only Read-only meta; Refresh snapshots header action.
Audit Log Super admin only Read-only list, newest first.

canViewAny() / edit / create / delete use auth()->user()->hasRole(config('filament-starter-minimal.superadmin.role')).

Bundled plugin catalog

The default catalog is registered for you in Registry\DefaultPluginCatalog. Every entry is disabled by default (except filament-shield) and every installer is class_exists-guarded — so the package can list a plugin without forcing you to composer require it. Toggle plugins on per-panel from Plugin Management in the admin UI, or pre-configure in config/filament-starter-minimal.php.

Key Package (composer require ...) Notes
filament-shield bezhansalleh/filament-shield RBAC. dangerous_to_disable — safe-mode forces it on.
filament-panel-switch bezhansalleh/filament-panel-switch Configures globally via PanelSwitch::configureUsing().
filament-exceptions bezhansalleh/filament-exceptions Adds an Exceptions resource.
filament-language-switch bezhansalleh/filament-language-switch Configures globally. Pass options.locales to set available locales.
filament-breezy jeffgreco13/filament-breezy Profile + 2FA flows.
filament-logger z3d0x/filament-logger Activity log UI.
filament-connection-badge rawand201/filament-connection-badge DB connection indicator.
filament-media-manager tomatophp/filament-media-manager Folder/file media manager.
filament-menu-builder datlechin/filament-menu-builder Drag-and-drop navigation builder.
filament-api-service rupadana/filament-api-service Generates REST endpoints from Resources.
filament-resource-lock kenepa/resource-lock Prevents concurrent edits on a resource.
filament-spatie-health shuvroroy/filament-spatie-laravel-health UI for spatie/laravel-health.
filament-spatie-backup shuvroroy/filament-spatie-laravel-backup UI for spatie/laravel-backup.
filament-global-search-modal charrafimed/global-search-modal Richer global search UI.
filament-spotlight pxlrbt/filament-spotlight ⌘K spotlight launcher.
filament-quick-create awcodes/filament-quick-create Topbar quick-create dropdown. Options: excludes, includes, sort_by.

Run php artisan minimal-starter:doctor after composer require to confirm the class autoloads — if a vendor renames their plugin class between versions, override the entry via withPlugin() (next section).

Companion packages (not panel plugins)

These packages don't implement Filament's Plugin contract — they ship form components, traits, or settings pages you compose into your own Resources. They're not in the registry; install them as needed:

  • filament/spatie-laravel-media-library-plugin — media library form fields
  • filament/spatie-laravel-settings-plugin — Spatie-Settings-backed pages
  • filament/spatie-laravel-tags-plugin — tag form components
  • riodwanto/filament-ace-editor — Ace code editor form field
  • ralphjsmit/laravel-filament-components — assorted helper components
  • stechstudio/filament-impersonate — table/page actions for impersonation
  • ysfkaya/filament-phone-input — international phone-number form field
  • owen-it/laravel-auditing — model trait, not Filament-specific

Extending the registry

The plugin registry is bound to the container as PluginRegistryContract. Register additional plugins (or override package-shipped ones with the same key) via the fluent withPlugin() / withPlugins() API on the panel plugin:

use EdrisaTuray\FilamentStarterMinimal\Filament\FilamentStarterMinimalPlugin;
use EdrisaTuray\FilamentStarterMinimal\Registry\PluginDefinition;
use Filament\Panel;
use Vendor\MyPlugin\MyPlugin;

$panel->plugins([
    FilamentStarterMinimalPlugin::make()
        ->withPlugin(new PluginDefinition(
            key: 'my-plugin',
            label: 'My Plugin',
            installer: fn (Panel $p, array $options): Panel => $p->plugin(MyPlugin::make()),
            defaultEnabled: false,
            dangerousToDisable: false,
            defaultOptions: [],
            class: MyPlugin::class,
            package: 'vendor/my-plugin',
        )),
]);

You can also resolve the contract directly anywhere in the app (e.g. in another service provider's boot()):

use EdrisaTuray\FilamentStarterMinimal\Contracts\PluginRegistryContract;

app(PluginRegistryContract::class)->register(new PluginDefinition(...));

After registering new definitions, run Sync from Registry in the admin UI or call PluginSyncManager::sync() so override rows are created.

Protected dangerous flag

If the package's default catalog ships a definition with dangerousToDisable: true (e.g. filament-shield), PluginRegistryContract::register() will throw when a same-key registration tries to silently demote it to false. To intentionally override this, pass the explicit opt-in:

app(PluginRegistryContract::class)->register($definition, allowDangerousOverride: true);

This blocks the chained-takeover path where a third-party plugin could redefine filament-shield with dangerousToDisable=false and then disable RBAC.

isDangerous() semantics

PluginRegistry::isDangerous(string $key, ?string $panelId = null) returns true only if the registry definition for $key has dangerousToDisable=true. The $panelId parameter is accepted for API compatibility but ignored — the registry is the single source of truth. The is_dangerous DB column is a denormalized cache maintained by the model's saving hook; reading it is unnecessary and risks drift.

Caching and invalidation

  • Resolved state is cached forever per panel under key starter_minimal_plugins_{panelId}_v{registry-signature}. The signature changes whenever a PluginDefinition is added, removed, or has its defaultEnabled/dangerousToDisable modified — so registering a plugin via withPlugin() automatically misses the old cache.
  • Saving or deleting a PanelPluginOverride calls PluginStateResolver::invalidate() for that panel.
  • The Clear Plugin Cache action in Plugin Management clears the resolver cache for all registered Filament panels.
  • When safe mode is active, resolved state forces dangerous_to_disable plugins on before the result is cached; use minimal-starter:safe-mode on|off so caches are invalidated automatically.

How sync works

PluginSyncManager::sync() is the write path that reconciles your code-level registry against the per-panel override rows in the database. Triggered from the Sync from Registry button, the install/update commands, or programmatically.

For every (managed_panel × registry_plugin) pair it:

  1. Acquires a 60-second Cache::lock('starter_minimal_sync') (other concurrent calls block up to 10 seconds — prevents racing inserts during a deploy).
  2. Inside a single DB::transaction(), calls PanelPluginOverride::updateOrCreate() for each pair so model events (audit log + cache invalidation) fire.
  3. Deletes any override row whose plugin_key is no longer in the registry or whose panel_id is no longer in managed_panels. Each deletion writes an audit row.
  4. After commit, invalidates PluginStateResolver caches for every registered panel.

Calling sync() twice in a row is a no-op on the second call — updateOrCreate only writes when fields differ from their defaults.

Shipped defaults

Out of the box, with zero published config:

  • managed_panels is ['admin'] — only a panel with id admin receives registry plugins from PlatformPanelFactory.
  • superadmin.role is super_admin — the role name checked by every Platform resource. Your User model must have spatie/laravel-permission's HasRoles trait (or another implementation that exposes a hasRole(string $role) method), and a role with that name must exist and be assigned. If neither is true, the resources are simply invisible to all users (closed-default).
  • safe_mode is false.
  • This package is single-tenant. The override / snapshot / audit tables key by panel_id only. The consumer's individual Filament panels may use Filament tenancy themselves — that's orthogonal.
  • The registry seeds filament-shield (enabled = true, dangerous_to_disable = true); every other plugin in the catalog is registered as enabled = false. Toggle them on per-panel via Plugin Management or via plugin_defaults in the published config.

Troubleshooting

Symptom Likely cause / fix
Platform resources invisible to all users The User model is missing the role trait, or no user has the configured super_admin role. Doctor cannot detect this — check $user->hasRole('super_admin') directly.
Plugin Management UI is empty No snapshots yet. Run php artisan minimal-starter:install (or :update) to populate starter_minimal_panel_snapshots and the override table.
Toggling a plugin in the UI does nothing Check that the panel id is in managed_panels. PlatformPanelFactory short-circuits non-managed panels even though the resources still appear.
Cache::rememberForever returns stale state in production The cache driver must be persistent (redis, database, file). The resolver caches forever; with array driver the cache is recreated cold every request.
Doctor reports a plugin's class missing after composer require The vendor renamed its plugin class. Override the entry via FilamentStarterMinimalPlugin::make()->withPlugin(new PluginDefinition(...)) with the correct FQCN, then run Sync from Registry.
Shield resources appearing twice You registered both FilamentStarterMinimalPlugin::make() and FilamentShieldPlugin::make() on the same panel. Drop the bare Shield registration; this starter installs Shield via the registry.

Commands reference

Command Purpose Exits non-zero on
minimal-starter:install Publish config (optional), migrate --force, snapshot + sync migrate non-zero exit OR any Throwable from snapshot/sync
minimal-starter:update migrate --force, snapshot + sync. Re-runnable. Same
minimal-starter:doctor Health checks (see above) Any critical finding
minimal-starter:safe-mode {on|off} Toggle the safe-mode cache flag, invalidate plugin caches Invalid argument

All commands accept --no-interaction and respect Laravel's standard verbosity flags.

Coexistence with edrisaturay/filament-starter

  • Different DB tables (starter_minimal_* vs starter_*).
  • Different Filament plugin id (filament-starter-minimal vs filament-starter).
  • Different config file (filament-starter-minimal.php vs filament-starter.php).

You should not load both starter panel plugins on the same panel if they both register Shield or duplicate resources—pick one package per panel.

Package layout

config/
  filament-starter-minimal.php
database/
  migrations/
    *_create_starter_minimal_tables.php
src/
  FilamentStarterMinimalServiceProvider.php
  Contracts/
    PluginRegistryContract.php
  Registry/
    DefaultPluginCatalog.php
    PluginDefinition.php
  Filament/
    FilamentStarterMinimalPlugin.php
    PlatformPanelFactory.php
    Resources/
      AuditLogResource.php
      PanelPluginOverrideResource.php
      PanelSnapshotResource.php
      ...
  Models/
    AuditLog.php
    PanelPluginOverride.php
    PanelSnapshot.php
  Support/
    MinimalDoctor.php
    MinimalSafeMode.php
    PanelSnapshotManager.php
    PluginRegistry.php
    PluginStateResolver.php
    PluginSyncManager.php

Testing (in a consuming app)

The reference application includes Pest tests under tests/Unit/FilamentStarterMinimalTest.php that assert registry shape, config resolution, DB merge behavior (with a targeted migrate --path when needed), and plugin id stability.

License

MIT. See the package composer.json for author metadata.

Support

Issues and source: see the monorepo or package repository you publish from. For Filament or Shield behavior, refer to the official Filament v5 and Filament Shield documentation.