- Config: auth, services, logging, queue and database only repeated the
framework's own files and are gone; the others keep only the keys that
differ (app version, cache serializable_classes, session cookie name,
Markdown mail theme, the shares disk, three Octane values, Livewire's
pagination theme and payload guards).
- Email verification is removed: User never implemented MustVerifyEmail,
so it was never enforced, and SealShare has a single admin and no
registration. CreateNewUser goes with it.
- FileEncryptionService::encryptFile() and generateSalt() were only used
by tests; tests build files with encryptTestFile() in tests/Pest.php.
- The expiration options are defined once, as Share::EXPIRATIONS. "30 Days"
now lasts 30 days instead of a calendar month, and Admin settings only
save a default expiration that is one of the options.
- One-caller helpers are inlined, the uploader reads chunk responses with
XHR's responseType, and starter-kit leftovers are removed.
- Docker: PHP reads the PHP_* limits from the environment itself
(${VAR:-default} in uploads.ini); both entrypoints stop writing the ini.
docker-compose.yml shares the app and scheduler variables through one
anchor. The dev image installs gd for the screenshot publisher and fake
test images.
- Development runs in Docker only: the composer dev script, concurrently,
laravel/pail, laravel/sail, autoprefixer and the shell-quote override
are gone.
- phpunit.xml forces the test environment with <server> entries, so tests
run in the dev container no longer use its real database.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Patch releases of laravel/boost, pestphp/pest, phpunit/phpunit and
filp/whoops, plus refreshed transitive npm packages. Boost's update
refreshed its laravel-best-practices skill.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docker-compose.dev.yml extends docker-compose.yml, so development runs the
scheduler too, and takes its settings from .env, which selects the file
through COMPOSE_FILE. The dev image is a stage of the Dockerfile and shares
the production image's PHP extensions; the app listens on port 80 as in
production.
A vite service runs the dev server with hot reload. No ports are published:
OrbStack serves https://app.sealshare.orb.local and
https://vite.sealshare.orb.local, with the ports pinned by label. Without
OrbStack, docker-compose.ports.yml publishes APP_PORT and VITE_PORT on
127.0.0.1. vite.config.js takes the dev server's address from
VITE_DEV_SERVER_URL and listens on IPv4 and IPv6, as OrbStack's proxy
connects over either.
Each start installs Composer packages, clears caches, migrates and links
storage.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The scheduler inherited the image's healthcheck, which asks the web server
that only the app container runs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Without a name, npm writes the folder's name into package-lock.json: "app" in
the dev container, "SealShare" on the host, so every install changed the file.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A quiet line under the shares card reads "SealShare 2.1.0 · Release
notes · Website · Made by noNameWEB": the release notes link goes to the
installed version's Gitea release, the others to sealshare.nonameweb.ch
and nonameweb.ch, each in a new tab. It is page chrome, not content, so
it is a footer line rather than a card.
The image has neither .git nor CHANGELOG.md, so the version is a
constant in config/app.php, bumped with each release. AppVersionTest
fails while it differs from the newest released heading in
CHANGELOG.md, so a forgotten bump stops CI before the tag's image is
built. CHANGELOG (Unreleased) notes it.
The dashboard screenshots are unchanged: the line sits below their fold.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Set pagination_theme explicitly now that the package would otherwise take
it over itself; boost:update refreshed the package's guideline and skill.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The recovery-codes pulse comment still explained itself against
Tailwind's animate-pulse, which left the stack in 2.0.0. The reason
stands on its own: the package keeps no keyframe utility for a pulse
loop, so the loading state animates on the effects spring instead.
config/livewire.php:237 stays on Livewire's own "tailwind" default:
SealShare still installs livewire-material ^2.0 from Gitea, whose
vendor copy has no `pagination::material` view yet, so pointing
pagination_theme at it now would break pagination. The package takes
the theme over itself once 2.2.0 is tagged and pulled.
DesignLanguageTest carried no Tailwind wording to update.
Full suite (322) and Pint green, unchanged — run against the old
vendor copy, so the guard's new undeclared-class check is not
exercised here yet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The sort select above the admin dashboard's shares list was capped at
20rem by the admin-shares-sort wrapper. The wrapper and its rule in
app.css are gone, so the select stretches with the card's stack like
the other fields. CHANGELOG (Unreleased) notes it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
With a download limit of 1, downloading one file of a share with
several files deleted the share and the files not yet downloaded.
ShareService::recordDownload() ran at the end of every download
request, one file or the ZIP alike, and deleted the share as soon as
download_count reached max_downloads. It has worked that way since
the first commit.
- One recipient's visit is one download. The first file or ZIP a
session downloads is counted when it starts, in one conditional
UPDATE that also checks the limit, so two recipients starting at
once can't both take the last download. The session remembers the
time, and for ShareService::DOWNLOAD_WINDOW_MINUTES (60) it may
start more downloads of the share without counting them, even once
the limit is reached. The claim happens in the controller before
streaming, because the session is saved before the body is sent,
and after the share key is resolved, so a request without the key
uses nothing.
- A share at its limit is closed to everyone else at once. The hourly
cleanup deletes it 24 hours after shares.last_downloaded_at (new
column), since a ZIP opens each file only when it reaches it and a
large download can outlast the hour.
- The download page of a limited share says how many downloads are
left, switches to "You have 1 hour" on the first press (Alpine, as
a download link does not render the page again), and shows the time
left on the next visit.
- The admin dashboard shows "2 of 3 downloads", marks shares at their
limit "Download limit reached" and leaves them out of Active Shares.
- Tests: the regression (3 files, limit 1: every file and the ZIP
download, counted once), another recipient, the end of the hour,
the last download going to one of two recipients, requests refused
before streaming, unlimited shares, the page notes in PHP and in
Chromium, the dashboard, and the cleanup at 23 and 25 hours. The
tests of recordDownload() and of the instant deletion are gone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Uploads on share.kadenpartner.ch failed with 409 on every chunk after
the update to 2.1. The example docker-compose.yml mounts the SQLite
volume over all of /app/database. Docker fills a volume from the image
only when it is created, so the container kept the 2.0 migrations and
never saw the 2.1 one. share_files.uploaded_chunks was never created,
so the chunk endpoint read null and answered 409. SQLite takes the
unknown "completed_at" in whereNull() as a string, so nothing failed
earlier.
- The image keeps a copy of its migrations in docker/migrations. On
startup the entrypoint adds the ones missing from database/migrations
before migrating, so installs with the old mount recover by pulling
the new image.
- docker-compose.example.yml and docker-compose.yml mount
sealshare_database at /app/database/sqlite and set DB_DATABASE to the
file in it. An existing volume can be moved there without losing
data: its database.sqlite lands at exactly that path.
- Tested with a locally built image: a volume created by 2.0.1 with the
old mount gets the migration once (not again on restart) and keeps
its settings; the same volume moved to the new path keeps its data;
a fresh volume with the new layout starts healthy.
- README and CHANGELOG (Unreleased) describe the new path and how to
switch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The site's tokens now carry the app's --md-sys-* names, with shape,
elevation, motion, state, type and spacing values copied from Livewire
Material 2.1 and the colours from the indigo profile, including its
high-contrast light and dark values for prefers-contrast: more. Cards
are outlined, the gallery uses a connected button group instead of
segmented buttons, buttons use M3 Expressive sizes, state layers and
pressed shapes, and layouts follow the M3 window classes with logical
properties. A Light/Dark/System toggle in the nav sets data-theme
before the first paint, and no text is dimmed with opacity.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The site now says files are encrypted in the browser and sent in chunks,
describes the password generator and its admin settings, explains why
uploads need HTTPS and how to serve it, and replaces the old PHP upload
cap with the admin's file size limits and the chunk size. The alt texts
and captions match the retaken screenshots. The README no longer lists
Tailwind CSS, and the website rules describe the encryption and the
copied colours accurately.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The desktop upload shot fills the password with Generate, and the share
created shot now comes from creating that share, so the password is
offered beside the link. The created share takes a fixed token, so its
link and QR code read the same on every run, and is deleted before the
dashboard shot, which no longer counts the unfinished upload in its disk
usage.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Everything stays within its existing constraint: Laravel 13.32, Livewire
4.4.5, Livewire Material 2.1.0, Pest 5.2, Boost 2.9, Pint 1.32.1, Vite
8.3 and autoprefixer 10.6. Boost's copy of the guidelines and skills
follows Livewire Material 2.1, which is plain CSS without utilities.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The pages were built five ways: two layouts, seven widths from 28 to
64rem and four heading styles. Every page now looks like the share
pages: a centred heading over one 40rem column of outlined cards, with
the floating toolbar below.
- <x-page> (components/page.blade.php) is the root of every page. It
draws the h1 and its line (`brand` takes the site's logo, title and
description from Admin settings), an optional `mark` and `navigation`
slot, then the content. It has no width prop: every page is the same
<x-pane width="narrow">.
- The sign-in, password reset, confirm, verify email, two-factor
challenge, setup and system password pages move onto layouts/app with
the brand heading and their form in a card titled with the task.
layouts/auth, auth-header and the settings heading partial are gone,
and so is the per-page width CSS.
- Settings put their section nav under the heading; the admin pages get
a description line each. FileUploader and ShareDownload no longer pass
the branding to their views.
- The admin dashboard's table needed about 49rem, so its shares are a
list: created above the token, which opens the share, then files,
size, downloads and expiry on two lines that wrap instead of clipping,
and one delete button. A "Sort by" select replaces the column headers
(newest, oldest, expiring soonest with never-expiring last, largest,
most downloads, most files) and resets the page. The stats stay two
by two. table.css and sort-header.css are no longer imported.
- Branding hints in Admin settings name every page the title shows on.
- Tests: PageTemplateTest renders every page once and checks one page
template, one h1 and the width, and the brand heading with its
fallbacks. FrameTest measures the page column instead of the auth
card and the 64rem main; dashboard tests follow the list and the sort
select, including expiry order. .ai/rules/views.md records <x-page>,
the CHANGELOG notes the change and the website screenshots are
regenerated.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A 6 GB upload kept a customer waiting long after its progress bar
reached 100%. The server wrote every upload three times: PHP's
temporary file, Livewire's copy of it ("Processing files...") and the
encrypted file ("Create Share Link"), each a full rewrite of a slow
disk. The unencrypted copy also stayed behind in livewire-tmp.
Now the uploader's browser encrypts each file in 16 MB chunks with
WebCrypto and PUTs them one at a time; the server checks each chunk in
memory and writes it once, already encrypted. Creating the share only
wraps its key and saves the options. A 200 MB upload through the
Docker image took 2.8 s, and its download matched byte for byte.
- SEALCHK2: a 19-byte header (chunk size, 7-byte nonce prefix), then
ciphertext and tag per chunk. Each nonce holds the chunk index and a
last-chunk flag (the STREAM construction), so cut or reordered files
fail to decrypt. SEALCHK1 and the single-block format still read.
- Envelope encryption: one random key per share. With a password it is
wrapped with Argon2id (sodium, libsodium's interactive limits) in
shares.wrapped_key, which names its parameters. Password shares from
before keep their PBKDF2-derived key.
- The upload page registers each selection with FileUploader into a
pending share of its own, lists the files with their progress, retries
a failed chunk after 1-16 s, then offers Retry; Remove and Cancel
abort. UploadChunkController only accepts chunks from the session that
started the share: a repeat is acknowledged, a skip gets 409 with the
count stored. Chunks go out as Blobs, which Chromium sends about eight
times faster than ArrayBuffers.
- Uploads need a secure context: over plain HTTP the page says HTTPS is
needed and takes no files. The Docker image gains AUTO_HTTPS, which
serves Let's Encrypt on 443 for SERVER_NAME and redirects 80; without
it the container stays on HTTP 80 behind a proxy. docker/Caddyfile was
never loaded and is gone; docker/healthcheck.sh covers both modes.
- "Download all" streams the ZIP with maennchen/zipstream-php (STORE,
ZIP64) instead of decrypting whole files into memory and writing the
archive unencrypted to /tmp.
- Pending shares count towards the quota, stay out of the admin
dashboard and 404 everywhere else. shares:cleanup deletes uploads idle
for 4 hours and Livewire temporary files older than that.
- PHP's upload limits no longer cap the admin's max file size and
default to 64M; LIVEWIRE_MAX_UPLOAD_TIME is gone and
UPLOAD_CHUNK_SIZE_MB is new.
- Tests cover the format, key wrapping, registration limits, the chunk
endpoint's answers, completing a share, the streamed ZIP, cleanup,
and in Chromium a real chunked upload and the HTTPS warning; the
selected-files overflow test runs again. README, website, CHANGELOG
and .ai/rules follow.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Uploaders no longer have to make up a share password. With "Password
protect" on, the upload page has Generate and Copy under the field, and
the page the upload leads to offers the password once more beside the
link: masked, with the same copy button at the end of the field as the
link's. The password also derives the share's encryption key and only
its hash is stored, so a lost one means files nobody can open.
- PasswordGeneratorService draws from Random\Randomizer's secure engine.
Characters are drawn uniformly and redrawn until every chosen set
appears; passphrases come from EFF's large word list (CC BY 3.0 US,
credited in the README), without its four hyphenated words.
- Admin settings gain a "Share Passwords" card: mode (off, on request,
prefilled as protection is switched on), kind (characters: length
12–64, the sets, look-alikes left out; passphrase: 4–10 words and a
separator), and an example with its estimated entropy that follows the
form before saving. Fields the chosen mode or kind hides are excluded
from validation and keep their saved value. The default is on
request, 20 letters and numbers without look-alikes.
- FileUploader flashes the password encrypted with the share's token;
ShareCreated shows it only when the token matches, so a reload or any
other visitor sees nothing. Crypt covers installs without
SESSION_ENCRYPT, which the Docker setup does not set.
- The symbol set leaves out what chat apps turn into formatting and
what breaks inside quotes, so a pasted password arrives unchanged.
- app.css imports group.css for <x-group>; .ai/rules/views.md records
that <x-group> drops data-test and other attributes.
- Tests cover the generator, the admin card's saving, validation and
example, prefill and generate on the upload page, the flash, and in
Chromium Generate and Copy on the upload page and the masked copy on
the share page. The admin settings page now has six headed sections.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Livewire Material 2.0.0 aligns every component with Material 3
Expressive and carries no Tailwind anywhere, so SealShare drops
tailwindcss and its Vite plugin and writes its views in the package's
vocabulary: layout components (<x-pane>, <x-stack>, <x-row>, <x-grid>,
<x-form>) with M3's spacing tokens, the md-type-*/md-ink-* text classes,
and --md-sys-* tokens in its own small stylesheet.
- resources/css/app.css opens with the package's layer order, imports
foundation.css and the stylesheet of each component the views render,
then the scheme, regenerated with the 2025 colour rules at M3's three
contrast levels. The app's own rules follow, one section per view,
on tokens and on M3's breakpoints (600/840/1200/1600px) only.
- Every view is rewritten in that vocabulary while SealShare keeps the
shape it had: the admin table, the admin settings, the recovery codes,
the share options and the download page are cards, and their fields
fill them rather than stopping at the 40rem bound a card already
bounds. The user settings pages became cards too, to match the
admin's, each with the sections that stand apart from its one subject
— deleting the account, the recovery codes — in a card beside it.
Material 3 decides how a component behaves, not whether a container
survives: buttons keep their label's width, a form's actions end it,
and each heading level keeps one type role.
- The layouts clear the floating toolbar by the --material-bottom-toolbar
the package publishes, and the snackbar clears it by itself.
- The two-factor setup QR code comes from QrCodeService, so it keeps a
white field and quiet zone in the dark theme and still scans.
- Browse Files is a real button that opens the file input, reachable
and visibly focused from the keyboard.
- Tests: the design test scans views, JS, CSS and app/ and checks the
CSS entry both ways (no missing, no unused import). New Chromium
suites cover the frame, settings and admin, the share flow, and every
page at M3's breakpoint edges (599/600, 839/840, 1199/1200, 1600px).
The package's renamed data-md-* hooks replace the 1.x ones.
- Boost's update brings the material-3 guideline and skill and drops
the Tailwind skill.
composer.json requires nonameweb/livewire-material ^2.0 from the Gitea
repository, resolved at the 2.0.0 tag. The CHANGELOG records the move as
2.1.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The entrypoint's php artisan view:cache failed on the package's showcase
views, whose components were only registered with the showcase enabled.
1.1.1 registers them always. ProductionBootTest caches every view with
the showcase off, as the entrypoint does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
QEMU 8.x crashes running x86_64 programs on an arm64 host (QEMU issue
2168), which the 8.1.5 pin walked into, and 10.2 segfaults on the runner
as well; 9.2.2 runs node, composer and install-php-extensions on the
runner's host. The Composer and npm stages now build on the build
machine's platform: their output is the same for every target, so a
multi-arch build runs them once and natively.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
Indigo (the default), Blue, Teal, Green, Amber, Rose and Violet in the
Vibrant style and Graphite in the Neutral style are generated from
config into the stylesheet. Admin settings opens with a colour profile
card: a swatch previews the profile on the page, and Save Settings
stores it as color_profile, which AppServiceProvider hands to the
package's resolver, so every page, mail and error page wears it. An
unknown profile is refused, and a saved one that disappears falls back
to indigo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
It brings colour profiles: several generated schemes under
<html data-scheme>, a resolver for the active one, and a picker. Boost's
copy of the package guideline and skill follows.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
The workflows live in .gitea/workflows. The Docker workflow logs in to
the Gitea container registry with REGISTRY_TOKEN (Gitea's job token cannot
publish packages yet), publishes gitea.nonameweb.ch/nonameweb/sealshare
with a registry build cache, and makes the release on Gitea with the
version's section of the changelog as its notes.
The README, the website's quick start, both compose files and the image
label point to Gitea; the changelog announces the new image name and
links Gitea. GiteaOnlyTest keeps GitHub and ghcr.io out.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
website/ is hand-written HTML and CSS, uploaded as it is, like the
MailifySMS site: SealShare as software a company installs for its own
upload platform, how it works, features, a desktop/phone and light/dark
gallery of the generated screenshots, a dated and sourced comparison with
hosted transfer services and self-hosted tools, the Docker quick start,
FAQ and a privacy page for Plausible. Colours come from the app's scheme,
Google Sans Flex is served locally, nothing else loads from other hosts.
The README shows three screenshots and no longer calls the encryption
end-to-end. WebsiteTest guards missing files, other hosts, the screenshot
set and the encryption wording.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
composer screenshots runs Pest browser tests in tests/Screenshots, outside
every test suite: fixed demo data under a frozen clock, desktop (MacBook
14, 2x) and phone (iPhone 15 Pro, 3x) in light and dark, each capture
published at once as WebP at two widths into website/img/screenshots.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
The share created page gets "Show QR code", a dialog with the link as a
QR code (black on white, full screen on a phone) that downloads as a PNG
drawn in the browser, with a password reminder for protected shares; and
"Share…", which opens the device's share sheet where there is one. Both
carry only the link.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
Bacon QR Code stays at the v3.1.1 Fortify already installed; SealShare
now draws QR codes with it too. Livewire Material 1.0.1 compiles its
components to the same view on every machine and keeps a full-screen
dialog's subtitle on a phone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
The checkout is mounted at /app, so views compiled in the container and
on the host landed in one storage/framework/views, and each side read the
other's absolute paths.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
The top app bar repeated the site's name, which already heads the upload
and download pages, and spread its actions to the window's far edge. A
floating toolbar centred above the bottom edge, as wide as its buttons,
now holds them: Upload and the admin pages with the current one filled,
and the account menu; guests get Upload, the theme toggle and Log in,
without tooltips. On a phone the sign-in card no longer stretches to the
full height, and the admin dashboard's empty state sits outside the
scrolling table.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
Upload drop zone, copying a share link, unlocking a download on a phone,
sorting and deleting on the admin dashboard, and the system theme with
the Appearance picker.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
Replaces maryUI and daisyUI with nonameweb/livewire-material: the Vibrant
indigo scheme, a system/light/dark theme under sealshare-theme, one top
app bar with the account menu, the upload drop zone and link-ready
moments, M3 fields, dialogs instead of wire:confirm, snackbars instead of
flashed messages, a sortable admin table, and the starter-kit cleanup.
Docker builds assets after Composer; CI drops the Flux step.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
SealShare leaves maryUI and daisyUI for nonameweb/livewire-material, a
shared Material 3 Expressive component package, once it reaches 1.0.0.
This plan holds SealShare's decisions and its adoption steps; the
package's own plan lives in the package repository.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
Pest 5.1 brings PHPUnit 13.3; phpunit.xml and the tests needed no
changes. Everything else stays within its existing constraint: Laravel
13.31, Livewire 4.4.4, Octane 2.19.1, Fortify 1.39, Mary 2.9.10, Boost
2.8 and Pint 1.32. Guzzle moves to 8.2 as a transitive dependency.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XYnWFt9pJEwvAmNFN38XD
Livewire's temporary upload rule was hard-coded to max:4194304 (4 GB),
so /livewire/upload-file rejected any larger file no matter how high
PHP_UPLOAD_MAX_FILESIZE or the admin's max file size were set.
_uploadErrored() then replaced Livewire's actual message with "file
exceeds the maximum size of N MB", quoting the admin limit the file was
under.
The cap is removed: PHP's upload_max_filesize is the hard limit and the
admin setting is still enforced in updatedFiles() and createShare(). A
rejected upload now logs the real validation errors and tells the user
the server could not accept the file.
max_upload_time is configurable via LIVEWIRE_MAX_UPLOAD_TIME, and the
README documents every limit large uploads depend on.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XYnWFt9pJEwvAmNFN38XD
Documents 1.0.0, 1.0.1 and the upcoming 1.1.0 in Keep a Changelog format.
The tech stack table still claimed Laravel 12 and listed
maennchen/zipstream-php, which was dropped in 1.0.1 when ZIP downloads
moved to native ZipArchive.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>