v1.54.0 - Okab
[1.54.0] - 2026-06-10 — Okab
Theme: Real extension seams — overridable container defaults, the entitlement contract, and a storage provider registry. A coordinated release in three movements: (1) a container-precedence fix that makes every "core default + extension override" seam actually overridable (extension definitions previously lost silently to core bindings); (2) the
Glueful\Entitlementscore seam — a contract-only extension point for commercial capability gates, consumed by the forthcomingglueful/subscriptions; and (3) a storage driver registry with thes3/gcs/azurefactories extracted to first-party provider packs (breaking — lean core, same playbook as 1.52). Staying in 1.x per the pre-public breaking-changes policy — see Upgrade Notes.
Breaking Changes
- Core ships only the
local/memorystorage drivers. Thes3,gcs, andazuredriver factories (and the embedded S3 presign logic) have been removed from core and extracted to first-party packs.glueful/storage-s3ships alongside this release (it also covers R2 / MinIO / Spaces / Wasabi via presets);glueful/storage-gcsandglueful/storage-azurefollow shortly. A disk using one of those drivers now fails fast withUnsupportedStorageDriverExceptionnaming the package to install (e.g.composer require glueful/storage-s3). Thes3stub disk inconfig/storage.phpis now commented out (core's default config only declares disks core can create). Upgrade: after updating the framework,composer require glueful/storage-s3(or the gcs/azure pack once published) for whichever driver your app uses. Apps onlocal/memoryonly are unaffected.
Added
- Entitlement seam (
Glueful\Entitlements). New core extension point:EntitlementCheckerInterface(allows()/limit(), explicit tenant uuid) with an absent-allowNullEntitlementCheckerdefault bound inCoreProvider. Lets extensions and app code gate commercial capabilities without depending on any specific subscriptions package. Core ships the contract only -- no consumer, no tenant/plan awareness.glueful/subscriptionsprovides the real checker. (Override of the default relies on the container precedence fix in the same release.) - Storage driver registry + provider seam. New
Glueful\Storage\Contracts\StorageDriverFactoryInterface(driver identity, construction,available(),features()),StorageDriverRegistryInterface(+StorageDriverRegistry::withBuiltIns()), and optional capability contractsNativeSignedUrlProviderInterface/StorageHealthCheckInterface.StorageManagernow resolves every disk through the registry (last-registered-wins per driver), accepts a nullable registry (defaults to built-ins --new StorageManager($config, $pathGuard)is unchanged), drivesputStream()throughfeatures()['supports_atomic_move'](default true), and exposes the registry viadrivers(). Extensions register factories taggedstorage.driver_factory;StorageProvidercollects them after thelocal/memorybuilt-ins and reverses the tagged iterator so higher tag priority wins same-driver collisions.FlysystemStoragedelegates writes toStorageManager::putStream()and resolves native signed URLs viaNativeSignedUrlProviderInterface(falling back to the app URL onnullor provider errors). storage:test [disk]command. Read-only by default (reports driver registration, adapteravailable(), and a non-mutating liveness probe via the health-check capability);--writeopts into a write/read/delete smoke test. Never prints secrets.- Optional
native_urlin the blob API. Additive, default-off, per-disk, visibility-scoped (config('uploads.native_urls')):publicblobs may serve a direct provider URL;privateblobs only with a bounded TTL. The app-signed/blobs/{uuid}URL stays the always-available, access-controlled path.
Fixed
- Container precedence: extension definitions now override core defaults.
ContainerFactorymerged extension service definitions with+=, which kept the core binding on key collision and silently dropped extension overrides -- making core default bindings (UserProviderInterface -> NullUserProvider, and every "core default + extension override" seam) un-overridable through the normal provider path. Now merged witharray_replace(extension-over-core);ApplicationContextis re-pinned post-merge so a framework-managed key cannot be clobbered. Within-extension precedence is unchanged. - Blob uploads now persist the actual effective storage disk.
FileUploadernow prefers its explicitstorageDriverwhen recordingblobs.storage_type, so per-request or manually constructed uploaders no longer store the configureduploads.diskwhen the file was written to a different disk.
Upgrade Notes
- Cloud storage drivers need their provider pack. If any disk uses
driver: s3(including R2/MinIO/Spaces/Wasabi setups),composer require glueful/storage-s3after updating — its presets cover the S3-compatible providers.gcs/azureusers should hold this upgrade untilglueful/storage-gcs/glueful/storage-azurepublish (following shortly). Apps onlocal/memoryneed nothing. - Refresh the command manifest on deploy. This release adds
storage:test; astorage/cache/glueful_commands_manifest.phpgenerated before the upgrade will not know it. Runphp glueful commands:cacheas part of the deploy (cache:cleardoes not refresh the command manifest). - Regenerate the precompiled container on deploy. The extension-over-core precedence fix only takes effect once the compiled container artifact is rebuilt — a container compiled before 1.54.0 still encodes the old (core-wins) merge. Run
php glueful di:container:compile --force(or delete the cachedglueful_compiled_container.phpartifact) as part of the deploy. - Extension authors: your container definitions now actually override core defaults. Binding the same service id as a core provider previously lost silently; it now wins (last-layer-wins). This is what makes
UserProviderInterface,EntitlementCheckerInterface, the storage factories, and every other "core default + extension override" seam real. Audit any extension that unintentionally reuses a core service id. - Optional env:
UPLOADS_NATIVE_MAX_PRIVATE_TTL(default900) caps the TTL of native provider URLs forprivateblobs. The wholenative_urlfeature is default-off per disk — no action unless you opt a disk in viauploads.native_urls.disks. - No core migrations; no required env changes.