diff --git a/.ai/rules/website.md b/.ai/rules/website.md index 0c82ee5..f9658f0 100644 --- a/.ai/rules/website.md +++ b/.ai/rules/website.md @@ -6,4 +6,4 @@ paths: # Website ## website/ is the live site, uploaded by hand -website/ is a faithful copy of sealshare.nonameweb.ch (METANET hosting), hand-written HTML/CSS with no build step, uploaded wholesale when it changes. Colours in css/theme.css are copied from the indigo profile (the JSON's top-level light/dark) in resources/css/material-scheme.json — copy them again if indigo is regenerated differently; the site does not follow the admin's colour profile. The comparison tables are dated and every competitor value has a source from the product's own site, docs or repo; an unsourced value is "—", never a guess. Never call SealShare's encryption end-to-end (it encrypts at rest on the server). Nothing may load from another host except plausible.io. tests/Feature/WebsiteTest.php guards all of this. +website/ is a faithful copy of sealshare.nonameweb.ch (METANET hosting), hand-written HTML/CSS with no build step, uploaded wholesale when it changes. Colours in css/theme.css are copied from the indigo profile in resources/css/material-scheme.json: the standard light and dark values (profiles.indigo.light/dark) and the high-contrast light and dark values (profiles.indigo.contrast.high) — copy them again if indigo is regenerated differently; the site does not follow the admin's colour profile. The comparison tables are dated and every competitor value has a source from the product's own site, docs or repo; an unsourced value is "—", never a guess. Never call SealShare's encryption end-to-end (files are encrypted in the browser with a key the server issues, and the server decrypts them for downloads). Nothing may load from another host except plausible.io. tests/Feature/WebsiteTest.php guards all of this. diff --git a/README.md b/README.md index 1277bab..af69d5c 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ A simple, self-hosted file sharing solution built with Laravel. Upload files, ge |-------|-----------| | **Framework** | Laravel 13 | | **Application Server** | FrankenPHP (via Laravel Octane) | -| **Frontend** | Livewire 4, Tailwind CSS 4, [Livewire Material](https://gitea.nonameweb.ch/noNameWEB/livewire-material) (Material 3 Expressive) | +| **Frontend** | Livewire 4, [Livewire Material](https://gitea.nonameweb.ch/noNameWEB/livewire-material) (Material 3 Expressive) | | **Authentication** | Laravel Fortify | | **Encryption** | Chunked AES-256-GCM (WebCrypto in the browser), keys wrapped with Argon2id | | **ZIP Downloads** | [ZipStream-PHP](https://packagist.org/packages/maennchen/zipstream-php) | diff --git a/website/index.html b/website/index.html index 8ef853b..7748b0e 100644 --- a/website/index.html +++ b/website/index.html @@ -115,7 +115,7 @@ SealShare's upload page with files selected and share options + alt="SealShare's upload page with files selected and a generated password in the share options"> @@ -176,7 +176,7 @@
  • Upload

    -

    Drop files or whole folders. Add a password, an expiry and a download limit if you want.

    +

    Drop files or whole folders. Add a password, an expiry and a download limit if you want — SealShare can generate the password for you.

  • @@ -197,13 +197,13 @@
    Features

    Everything a file exchange with customers needs

    -

    And nothing that turns it into a project: SealShare runs from a single container and is set up in a few minutes. It follows your system's light or dark theme, on desktop and phone.

    +

    And nothing that turns it into a project: SealShare runs from a single container and is set up in a few minutes. It works on desktop and phone, in a Light, Dark or System theme, and in high contrast when your system asks for it.

    Files and folders

    -

    Drag & drop or browse; folders keep their structure, with live upload progress.

    +

    Drag & drop or browse; folders keep their structure. Files are encrypted in the browser and sent in chunks, with progress per file and a failed chunk retried on its own.

    @@ -218,12 +218,12 @@

    Password protection

    -

    Protect a share with a password, or generate one to copy; its encryption key is derived from it.

    +

    Type a password or generate one, as random characters or a passphrase. It is shown once more beside the new link, and it protects the share's own random key.

    Encrypted at rest

    -

    Encrypted at rest with AES-256-GCM; with a share password the key is never stored.

    +

    Encrypted in the browser with AES-256-GCM before upload, and stored only encrypted. With a share password the key is never stored as it is.

    @@ -243,7 +243,7 @@

    Automatic clean-up

    -

    Expired shares and their files are deleted every hour.

    +

    Expired shares and their files are deleted every hour, and unfinished uploads after 4 hours.

    @@ -252,8 +252,8 @@
    -

    Limits and quota

    -

    Maximum file and share size, files per share, storage quota, default expiry.

    +

    Limits and defaults

    +

    Maximum file and share size, files per share, storage quota, default expiry, and a password generator that is off, on request or prefilled.

    @@ -273,7 +273,7 @@

    One Docker image

    -

    FrankenPHP with Laravel Octane, optional automatic TLS and SQLite — no separate database.

    +

    FrankenPHP with Laravel Octane and SQLite — no separate database. Uploads need HTTPS: turn on automatic TLS, or use your reverse proxy.

    @@ -305,11 +305,11 @@ data-light-src="img/screenshots/desktop/light/01-upload-800.webp" data-light-srcset="img/screenshots/desktop/light/01-upload-1600.webp 1600w, img/screenshots/desktop/light/01-upload-800.webp 800w" data-dark-src="img/screenshots/desktop/dark/01-upload-800.webp" data-dark-srcset="img/screenshots/desktop/dark/01-upload-1600.webp 1600w, img/screenshots/desktop/dark/01-upload-800.webp 800w" width="1512" height="982" loading="lazy" decoding="async" - alt="The upload page with two files selected and the share options filled in"> + alt="The upload page with four files selected and a generated password in the share options"> -
    Upload files and foldersDrag & drop, then set a password, an expiry and a download limit.
    +
    Upload files and foldersDrag & drop, then generate a password and set an expiry and a download limit.
  • @@ -321,11 +321,11 @@ data-light-src="img/screenshots/desktop/light/02-share-created-800.webp" data-light-srcset="img/screenshots/desktop/light/02-share-created-1600.webp 1600w, img/screenshots/desktop/light/02-share-created-800.webp 800w" data-dark-src="img/screenshots/desktop/dark/02-share-created-800.webp" data-dark-srcset="img/screenshots/desktop/dark/02-share-created-1600.webp 1600w, img/screenshots/desktop/dark/02-share-created-800.webp 800w" width="1512" height="982" loading="lazy" decoding="async" - alt="The share created page with the share link, its details and the QR code button"> + alt="The share created page with the link, the password beside it, the share's details and the QR code button"> -
    Share the linkCopy it, show it as a QR code or send it from the share sheet.
    +
    Share the linkCopy it and its password, show it as a QR code or send it from the share sheet.
  • @@ -369,7 +369,7 @@ data-light-src="img/screenshots/desktop/light/05-admin-dashboard-800.webp" data-light-srcset="img/screenshots/desktop/light/05-admin-dashboard-1600.webp 1600w, img/screenshots/desktop/light/05-admin-dashboard-800.webp 800w" data-dark-src="img/screenshots/desktop/dark/05-admin-dashboard-800.webp" data-dark-srcset="img/screenshots/desktop/dark/05-admin-dashboard-1600.webp 1600w, img/screenshots/desktop/dark/05-admin-dashboard-800.webp 800w" width="1512" height="982" loading="lazy" decoding="async" - alt="The admin dashboard with share statistics, disk usage and the table of shares"> + alt="The admin dashboard with share and file counts, disk usage and the list of shares with Sort by"> @@ -385,11 +385,11 @@ data-light-src="img/screenshots/desktop/light/06-admin-settings-800.webp" data-light-srcset="img/screenshots/desktop/light/06-admin-settings-1600.webp 1600w, img/screenshots/desktop/light/06-admin-settings-800.webp 800w" data-dark-src="img/screenshots/desktop/dark/06-admin-settings-800.webp" data-dark-srcset="img/screenshots/desktop/dark/06-admin-settings-1600.webp 1600w, img/screenshots/desktop/dark/06-admin-settings-800.webp 800w" width="1512" height="982" loading="lazy" decoding="async" - alt="The admin settings page with branding and upload limits"> + alt="The admin settings page with the colour profile, branding and upload protection"> -
    Admin settingsUpload limits, storage quota, your logo and title.
    +
    Admin settingsColour profile, your logo and title, upload protection and limits.
  • @@ -480,7 +480,7 @@ End-to-end encryptionNoEnterprise add-onNoNoHigher Workspace editions Folder uploadYesNo, folders are flattenedYesYesYes Custom brandingYes: logo, title, descriptionUltimate plan and upThrough paid kDriveStandard plan and up— - Maximum file sizeSet by you (server limits)3 GB or 100 GB a month on Free150 GB per transfer2 GB on Basic (free)15 GB of free storage + Maximum file sizeSet by you (no server cap)3 GB or 100 GB a month on Free150 GB per transfer2 GB on Basic (free)15 GB of free storage Licence and costMIT, freeFree (non-commercial use); Starter USD 8/month2FreeFree; Plus EUR 11.99/month2Free; 100 GB CHF 2/month2 QR code for the linkYes———— @@ -505,14 +505,14 @@ End-to-end encryptionNoNoNoOptionalNo Folder uploadYesYesNoNo, files are listed singly3Yes, keeps structure Custom brandingYes: logo, title, descriptionYes: logo, name, colours, CSSYes, by editing templatesYes: logo, CSS, JavaScriptYes: logo, colours, themes - Maximum file sizeSet by you (server limits)Set by you (1 GB per share by default)Set by you (no limit by default)Set by you (100 GB by default)Set by you (2 GB per share by default) + Maximum file sizeSet by you (no server cap)Set by you (1 GB per share by default)Set by you (no limit by default)Set by you (100 GB by default)Set by you (2 GB per share by default) Licence and costMIT, freeBSD-2-Clause, freeBSD-2-Clause, freeAGPL-3.0, freeMIT, free QR code for the linkYesYesYesYesNo -

    SealShare encrypts files on the server as they arrive. With a share password the key is never stored; without one it is kept in SealShare's database. A value marked “—” could not be confirmed from the product's own sources. Checked on 13 September 2026.

    +

    SealShare encrypts files in the browser before they are uploaded. With a share password the key is never stored as it is; without one it is kept in SealShare's database. A value marked “—” could not be confirmed from the product's own sources. Other products checked on 13 September 2026.

    1 WeTransfer's pricing page and its help centre disagree. The pricing page lists password protection on every plan, expiry up to 7 days and up to 100 GB a month on Free; the help centre (May 2026) lists password protection under Ultimate, expiry up to 3 days, and up to 10 transfers or 3 GB in 30 days on Free.

    2 The cheapest paid plan, at the price shown in Switzerland on the date above; prices differ by country. WeTransfer's Free plan is for individual, non-commercial use. Dropbox Plus has no passwords or branding for transfers; Google One 100 GB adds storage, not sharing features.

    @@ -541,9 +541,10 @@
    Install

    Running in a few minutes

    -

    All you need is a server with Docker and a domain name.

    +

    All you need is a server with Docker, a domain name and HTTPS.

    @@ -558,7 +559,8 @@ cp docker-compose.example.yml docker-compose.yml # Generate an app key and paste it into docker-compose.yml docker run --rm gitea.nonameweb.ch/nonameweb/sealshare:latest php artisan key:generate --show -# Edit docker-compose.yml — set APP_KEY and APP_URL; AUTO_HTTPS and SERVER_NAME for automatic TLS +# Edit docker-compose.yml — set APP_KEY and APP_URL +# Uploads need HTTPS: set AUTO_HTTPS and SERVER_NAME, or use a reverse proxy # Then start: docker compose up -d
    @@ -575,7 +577,14 @@ docker compose up -d Is SealShare end-to-end encrypted?

    No. The uploader's browser encrypts each file in chunks with AES-256-GCM before sending it, and SealShare stores the files only in encrypted form. But the key comes from your server, which checks every chunk and decrypts the files again for downloads — so the server can read them, which is why it matters that the server is yours.

    -

    For a share without a password the key is kept in SealShare's database. With a share password the key is locked with that password and never stored as it is, so the files cannot be decrypted without it.

    +

    For a share without a password the key is kept in SealShare's database. With a share password the key is wrapped with a key derived from that password (Argon2id) and never stored as it is, so the files cannot be decrypted without it.

    +
    + +
    + Why do uploads need HTTPS? +
    +

    Files are encrypted in the uploader's browser with WebCrypto, and browsers only offer WebCrypto over HTTPS or on localhost. Over plain HTTP the upload page says so and takes no files; downloads keep working.

    +

    Set AUTO_HTTPS: "true" and SERVER_NAME to get a Let's Encrypt certificate, with ports 80 and 443 reachable from the internet. Or put a reverse proxy that terminates TLS in front of SealShare.

    @@ -587,7 +596,8 @@ docker compose up -d
    How big can files be?
    -

    As big as your server allows. The Docker image accepts up to 4 GB per file by default; within that, you set the maximum file size and share size in the admin settings (100 MB and 2 GB out of the box). Behind a reverse proxy, raise its request size and timeouts as well.

    +

    As big as you allow. You set the maximum file size, share size and storage quota in the admin settings (100 MB, 2 GB and 20 GB out of the box); PHP's upload limits do not cap them.

    +

    Files go up in chunks of 16 MB (UPLOAD_CHUNK_SIZE_MB), so a reverse proxy only needs a request body limit a little above one chunk. Unfinished uploads count towards the quota and are deleted after 4 hours.