- Share files using a link
- Unlimited file size (restricted only by disk space)
- Set an expiration date for shares
- Secure shares with visitor limits, passwords, and IP-based access restrictions
- Email recipients
- Reverse shares
- OIDC and LDAP authentication
- Integration with ClamAV for security scans
- Different file providers: local storage and S3
- Download the
docker-compose.ymlfile - Run
docker compose up -d
The website is now listening on http://localhost:3000, have fun with Better Pingvin Share 🐧!
Tip
Checkout Pocket ID, a user-friendly OIDC provider that lets you easily log in to services like Better Pingvin Share using Passkeys.
TBD.
npm run qualityruns lint, type checks, production builds, and the fast unit/integration layernpm run test:fastruns backend unit/integration tests plus frontend Vitest suitescd backend && npm run test:systemruns the PR-safe backend API smoke suitecd backend && npm run test:system:full-regressionruns the full Newman + scripted backend regression suitenpm run test:allruns the fast layer and the full backend black-box regression suitenpm run test:e2eboots a temporary backend + frontend stack on dynamic ports and runs the Playwright browser smoke suite ine2e/
Test artifacts are written to test-results/, and test-specific runtime files are isolated under tmp/test-runtime/ or backend/tmp/. Backend black-box runs emit Newman JSON/JUnit reports, HTML summaries, and per-request snapshots under test-results/backend/system/<suite>/.
.github/workflows/ci.ymlrunsBackend,Frontend,Docs,API smoke, andBrowser E2Ein parallel on pull requests,main, andv*tags.- The README hero badges show the
mainbranch CI status, test pass snapshot, current app version, and the latest backend/frontend line coverage snapshots published by CI. - The recommended branch protection gate is
CI / Required checks; the per-area jobs stay stable for drill-down and artifact inspection. - GHCR publication is no longer an independent push trigger:
build-docker-image.ymlis invoked by CI only after the required checks pass onmainor release tags.
Important
Anonymous browser uploads now generate a dedicated edit link for the uploader. Treat that link as a secret because it grants owner-level access to the share.
Better Pingvin Share now includes an automation-focused API under /api/v1.
- Authentication for
/api/v1uses bearer tokens, not the browseraccess_tokencookie - Bearer tokens can be created from the account page and are shown only once
- Share owners can update metadata, expiration, recipients, and security rules with
PATCH /api/v1/shares/:id - Small uploads can use
multipart/form-data; large or resumable uploads can keep using chunkedapplication/octet-stream - Browser-based cross-origin access to
/api/v1is disabled by default and can be enabled withapi.corsAllowedOrigins
Example small upload:
curl -X POST \
-H "Authorization: Bearer $PINGVIN_API_TOKEN" \
-F "file=@artifact.zip" \
http://localhost:3000/api/v1/shares/my-share/files/multipartExample chunk upload:
curl -X POST \
-H "Authorization: Bearer $PINGVIN_API_TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary @chunk.bin \
"http://localhost:3000/api/v1/shares/my-share/files?name=artifact.zip&chunkIndex=0&totalChunks=1"Example ZIP bundle download:
curl -L \
-H "Authorization: Bearer $PINGVIN_API_TOKEN" \
-o my-share.zip \
http://localhost:3000/api/v1/shares/my-share/files/zipIn development mode, Swagger documents both the legacy routes and the new automation endpoints at /api/swagger.
Administrators can configure share.expiredEditablePeriod to keep expired shares editable by their owner and by administrators before the files are physically deleted by share.fileRetentionPeriod. Expired shares remain unavailable through public links and downloads until their expiration is extended.
Every public share exposes a JSON file list at <share-url>/files.json (for example http://localhost:3000/s/my-share/files.json) and a compact plain-text list at <share-url>/files.txt.
files.jsonusesapplication/jsonand includes share metadata, per-file metadata, and direct download URLs for every filefiles.txtusestext/plain, starts with a short plain-text overview, and then lists one file per line as filename, type, size, download URL, and web-view URL when available; file links use the same stable file-id based URLs asfiles.json- Returned URLs stay stable and do not embed a
token=query parameter - Admins can opt in to tokenized URLs for password-protected shares with
share.filesJsonPasswordProtectedLinksIncludeToken - Admins can opt in to per-file web-view links for supported text-like files, images, audio, video, and PDFs with
share.filesJsonWebViewLinksEnabled; these render inline without extra page chrome - Password-protected shares still require a valid share token before the JSON listing can be fetched
- Shares can optionally restrict access to a fixed IP allow list or the first
Nunique client IPs that reach the share - Clients that access protected or view-limited shares through these lists should preserve the
Set-Cookieheader returned by that response
