d2e8a135c51aa40532f6a2c5ab542150b6f5f305
The `uploading` flag was only cleared by `x-init="uploading = false"` on the file list, which Alpine runs when the element is initialized. Adding a second batch of files to the same share only patches that existing element, so the flag stayed true forever: the spinner never went away, the drop zone stayed disabled and the submit button stayed disabled, making the share impossible to create. `updatedFiles()` now dispatches `files-processed`, which runs for every batch, and the form resets its upload state on that event instead. Also fixes two adjacent bugs in the drag & drop path: `uploadMultiple()` was called without callbacks so dropped files showed no progress at all, and `relativePaths` was replaced instead of appended, shifting every earlier file's path onto the wrong file on a second drop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SealShare
A simple, self-hosted file sharing solution built with Laravel. Upload files, get a shareable link, done. All files are encrypted at rest with AES-256-GCM.
Features
- File Uploading — Drag & drop or browse to upload single/multiple files and folders with real-time progress
- Shareable Links — Each upload generates a unique link for recipients
- End-to-End Encryption — All files encrypted at rest using AES-256-GCM (chunked, streaming)
- Password Protection — Optionally protect shares with a password
- Expiration — Shares auto-expire after a configurable duration (1 hour to 30 days)
- Download Limits — Set a maximum number of downloads per share
- ZIP Downloads — Download all files in a share as a single ZIP archive
- Auto-Cleanup — Expired shares and files are automatically deleted (hourly)
- Admin Dashboard — View, manage, and delete all shares
- Admin Settings — Configure upload limits, storage quotas, branding, and more
- Site Branding — Custom logo, title, and description
- System Password — Optional global password gate to restrict upload access
- User Authentication — Login, registration, password reset, email verification
- Two-Factor Authentication — TOTP-based 2FA via Laravel Fortify
- Dark Mode — Dark themed UI with DaisyUI components
- Setup Wizard — First-run wizard to create the initial admin account
Tech Stack
| Layer | Technology |
|---|---|
| Framework | Laravel 12 |
| Application Server | FrankenPHP (via Laravel Octane) |
| Frontend | Livewire 4, Alpine.js, Tailwind CSS 4, DaisyUI 5, Mary UI |
| Authentication | Laravel Fortify |
| Encryption | Chunked AES-256-GCM with PBKDF2-SHA256 key derivation |
| ZIP Streaming | maennchen/zipstream-php |
| Testing | Pest 4 |
| Code Style | Laravel Pint |
| Build Tool | Vite |
Installation — Development
Docker (recommended)
# Build and start the dev container
docker compose -f docker-compose.dev.yml up -d --build
# View logs (including Vite output)
docker compose -f docker-compose.dev.yml logs -f
The app is available at http://localhost:8000 with Vite HMR on port 5173.
Installation — Production
Docker (recommended)
mkdir sealshare && cd sealshare
curl -O https://raw.githubusercontent.com/surtic86/SealShare/main/docker-compose.example.yml
cp docker-compose.example.yml docker-compose.yml
# Generate an app key and paste it into docker-compose.yml
docker run --rm ghcr.io/surtic86/sealshare:latest php artisan key:generate --show
# Edit docker-compose.yml — set APP_KEY, APP_URL, and SERVER_NAME
# Then start:
docker compose up -d
Migrations run automatically on startup. Open your configured domain — the Setup Wizard will create the first admin account.
Key environment variables:
| Variable | Required | Description |
|---|---|---|
APP_KEY |
Yes | Laravel encryption key |
APP_URL |
Yes | Full URL (e.g. https://share.example.com) |
SERVER_NAME |
Yes | Domain for auto-TLS (e.g. share.example.com) |
Volumes:
| Volume | Path | Purpose |
|---|---|---|
sealshare_storage |
/app/storage/app |
Encrypted uploaded files |
sealshare_database |
/app/database |
SQLite database |
caddy_data |
/data |
TLS certificates |
caddy_config |
/config |
Caddy configuration |
Manual (without Docker)
git clone https://github.com/surtic86/SealShare.git
cd SealShare
composer install --no-dev --optimize-autoloader
npm install && npm run build
cp .env.example .env
php artisan key:generate
# Edit .env — set APP_ENV=production, APP_DEBUG=false, APP_URL=https://your-domain.com
touch database/database.sqlite
php artisan migrate --force
php artisan storage:link
php artisan config:cache
php artisan route:cache
php artisan view:cache
Start with Octane:
php artisan octane:frankenphp --host=0.0.0.0 --port=80
Or point your web server (Nginx/Apache) to the public/ directory for a traditional PHP-FPM setup.
Add the scheduler to your crontab:
* * * * * cd /path-to-sealshare && php artisan schedule:run >> /dev/null 2>&1
License
This project is open-source software licensed under the MIT License.