Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Modules Licensing Laravel Package

ozankurt/laravel-modules-licensing

A Laravel package for licensing modular applications: validate license keys, activate or deactivate modules, and gate premium features with a configurable licensing layer designed to integrate smoothly with Laravel Modules.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the licensing layer for ozankurt/laravel-modules-licensing — it bolts a license server, Ed25519 offline key verification, and optional Filament admin resources onto ozankurt/laravel-modules-core. v2.0.0 is a compatibility generation, not a feature release: no public API changed, so anything you learn here transfers verbatim from v1.1.0 docs.

Environment preconditions (do these before composer require):

  1. PHP ^8.4 with ext-sodium compiled in — not just enabled in php.ini. This is the single most common install failure in Docker images and CI runners. Verify with php -m | grep sodium and php -r 'var_dump(function_exists("sodium_crypto_sign_verify_detached"));'.
  2. Laravel ^13.0 and ozankurt/laravel-modules-core ^2.0. Both are hard floors; there is no graceful degradation on Laravel 12.
  3. composer require ozankurt/laravel-modules-licensing:^2.0

Where to look first, in order:

  • The package config after php artisan vendor:publish --tag=... — this is where key material, storage driver, and endpoint paths live.
  • The migration set — the license/activation tables tell you more about the domain model than the README does.
  • The route registration — the HTTP endpoints are the contract between the licensing server and the client SDK, so read them before writing any client code.

First use case to implement: the offline verification path, not the admin UI. Generate an Ed25519 keypair, sign a license payload on the server, ship the public key inside your product, and verify the signature locally without a network call. That single flow is the value proposition of the package — the Filament resources are convenience on top of it. Once that works end-to-end, adding seat activation and the online check is incremental.

Skip Filament entirely on the first pass. It's optional and its version range (3, 4, or 5) is the most likely source of dependency-resolution friction in a fresh install. Get the non-UI paths green first.

Implementation Patterns

Two deployment roles, one package. Treat the licensing server (your app, with the DB and Filament resources) and the client SDK (embedded in the code you ship to customers) as separate integrations even though they come from the same composer package. Server-side you're modelling licenses, seats, and activations; client-side you're doing one signature check and caching the result.

Offline-first verification, online as a supplement. The Ed25519 verification should be the primary gate — it works on airgapped boxes and survives your licensing server being down. Layer the online endpoint on top for revocation and seat counting, but design the client so a network failure degrades to "signature valid, revocation status stale" rather than a hard lockout. If your first implementation blocks users when your server 502s, you've built an outage generator.

Key handling. Generate the keypair once, on infrastructure you control, and store the private key outside the repo and outside the database — env or a secrets manager. The public key is not a secret: it ships inside the client SDK, in the published config. Plan for rotation on day one even if you never use it, because a leaked private key with no rotation path means reissuing every license you've ever sold. Version the keypair (a key_id in the signed payload) so old licenses still verify against the old public key.

Activation/seat workflows. Model seats explicitly rather than counting rows ad hoc. The pattern that holds up: activation creates a record bound to a machine fingerprint with an issued timestamp; deactivation frees the seat; a periodic heartbeat updates "last seen" so you can reclaim seats from abandoned installs via a scheduled command. Do the reclamation as a queued job, not in the request path.

Integrating with laravel-modules-core. The licensing check belongs in the same gate as your module enable/disable logic — one middleware or service that answers "is this module both installed and licensed?" Keep it a single choke point rather than sprinkling license checks through controllers, otherwise every new module becomes a small archaeology project.

Filament resources. Useful as-is for support staff: look up a customer, view activations, revoke. Resist customising them heavily — the resources are part of the "no public API changed" surface, and the version range across Filament 3/4/5 means deep customisation is where you'll feel an upgrade bump hardest.

Testing. Sign test payloads in your test suite with a fixture keypair rather than hitting the real server. Keep one integration test that exercises the actual HTTP endpoint so route/serialisation regressions surface, and unit tests for the verification logic with deliberately corrupted signatures (wrong key, truncated payload, flipped byte) — those are the cases that silently pass when you're not looking.

Gotchas and Tips

ext-sodium is the top failure mode. It's easy for a build to pass on a dev machine and fail in a slim production image. Add ext-sodium to composer.json's require block explicitly so composer install fails loudly at build time instead of at first license check in production. Also check that your CI matrix image actually ships it.

spatie/laravel-package-tools ^1.93 is a hard pin in the wrong direction. 1.92.x caps at Laravel 12, so if anything in your tree constrains package-tools downward — another package with an old pin, a lockfile that won't update — composer will resolve you back into Laravel 12 territory and refuse the licensing upgrade. Run composer why spatie/laravel-package-tools before upgrading and resolve that constraint first.

The upgrade is environmental, not code. No public API changed, so the migration is: PHP 8.4, Laravel 13, modules-core ^2.0, then composer require ...:^2.0. If your application code needs edits, the breakage is from Laravel 13 or modules-core 2.0, not from this package — look there first. Sequence the upgrade so modules-core goes to ^2.0 in the same commit; the licensing package won't resolve against core 1.x.

Filament 3/4/5 variance is a resolution problem, not a runtime one. Pin your Filament major in the root composer.json rather than letting the licensing package's range decide for you. Two apps on different Filament majors will get different transitive sets from the same licensing version — useful in a monorepo, surprising if you assume parity.

Offline means you can't revoke. Verification is cryptographic and local, so a revoked license stays working on a machine that never phones home. Don't promise customers revocation enforcement you can't deliver. If revocation matters, make the heartbeat mandatory for the features that need it, and say so in the contract.

Clock skew and expiry. Offline licenses carry an expiry that's checked against the client's system clock. Users can and do move clocks backwards. Decide the policy — grace period, monotonic clock storage, or signed short-lived renewals — before a customer discovers it for you.

Cache the verification result. Ed25519 verify is cheap but not free, and running it on every request in a busy app is waste. Cache the validated license in the application cache keyed on the license id and a hash of the payload, with an explicit invalidation path on key rotation or license update.

Extension points. The client SDK being Core-free is deliberate — it lets you embed verification in a distributed product without dragging your module framework along. If you need custom license metadata, put it in the signed payload rather than in a side table the client can't see; anything outside the signature is unverifiable and therefore decorative.

Debugging verification failures. Log the key_id and the payload hash, never the signature check's intermediate values in production. Nine times out of ten a "valid license rejected" report is a trailing-newline or whitespace change in the payload between signing and verification — canonicalise the payload bytes (JSON encode with sorted keys, no pretty printing) on both sides, and verify against the exact byte string, not a re-serialised object.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console