Skip to main content

File Sharing and Storage

Alpha Feature

File Sharing and Storage is currently in alpha. Functionality may change, and some features are incomplete. Use in production at your own risk.

Stirling PDF can store files on the server and let users share them with each other. Files can be shared directly with specific users or via shareable links. Admins can set storage quotas to control disk usage.

Basic local-disk storage and sharing need no license - just turn on security.enableLogin and storage.enabled. Only the database and s3 storage providers require a Pro/Enterprise license. See Modes for what each deploy mode includes; self-hosted instances never use credits.

Already set up?

If your admin has turned storage on and you just want to use it, skip ahead to The My Files Page and Sharing Files. The setup sections in between (storage providers, S3, quotas) are for whoever runs the server.


What You Can Do

  • Store files server-side -- upload PDFs and other files that persist across sessions
  • Share with specific users -- grant other registered users access to your files with configurable permissions
  • Share via link -- generate a shareable link that any logged-in user with the link can access
  • Control access levels -- assign Editor, Commenter, or Viewer roles to shared users
  • Set storage quotas -- limit storage per user, per file, or system-wide
  • Audit access -- see who accessed your shared links and when
  • Automatic cleanup -- expired share links and orphaned files are cleaned up daily

Prerequisites

  • Authentication must be enabled (security.enableLogin: true)
  • For share links: system.frontendUrl must be set to your instance URL
  • For email notifications when sharing: mail.enabled: true with valid SMTP configuration

Enabling File Storage

storage:
enabled: true
provider: local # 'local', 'database', or 's3'
local:
basePath: './storage' # Filesystem path (local provider only)

Storage Providers

ProviderConfig ValueLicenseDescriptionBest For
Local FilesystemlocalFreeFiles stored on disk under basePathMost deployments, large files
DatabasedatabasePro/EnterpriseFiles stored as BLOBs in the databaseSimple setups where you want everything in one place
S3-Compatibles3Pro/EnterpriseFiles stored in an S3-compatible object storeMulti-node clusters, cloud object storage
tip

The local provider is recommended for most deployments and requires no license. It handles large files well and keeps database size manageable. The database provider is convenient but uses more memory with large files. The s3 provider is for object-storage and multi-node deployments. The database and s3 providers require a Pro/Enterprise license.

Docker Volume Mount

When using the local provider with Docker, mount the storage directory so files persist across container restarts:

volumes:
- ./stirling-storage:/storage

S3-Compatible Object Storage

Pro/Enterprise

The s3 storage provider requires a valid Pro or Enterprise license.

Set storage.provider: s3 to store user uploads in any S3-compatible object store. The same storage.s3.* block is also used by the cluster artifact store (see below).

storage:
enabled: true
provider: s3
s3:
endpoint: "" # blank = AWS regional default; otherwise full URL incl. https://
bucket: my-bucket # required
region: us-east-1
accessKey: "" # blank = fall back to AWS DefaultCredentialsProvider (env / profile / IMDS)
secretKey: ""
pathStyleAccess: false # true for MinIO and Supabase; false for AWS/R2/most CDNs
allowPrivateEndpoints: false # SSRF guard - see below
requestChecksumCalculation: WHEN_SUPPORTED # WHEN_SUPPORTED | WHEN_REQUIRED | DISABLED
responseChecksumValidation: WHEN_SUPPORTED # WHEN_SUPPORTED | WHEN_REQUIRED | DISABLED

S3 Configuration Keys

KeyDefaultDescription
endpoint(blank)Blank uses the AWS regional default. For other vendors, the full URL including https://.
bucket(blank)Required. The bucket that holds the stored files.
regionus-east-1Region of the bucket.
accessKey(blank)Static access key. Blank falls back to the AWS DefaultCredentialsProvider (env vars, profile, or IMDS).
secretKey(blank)Static secret key. Used together with accessKey.
pathStyleAccessfalseUse path-style (endpoint/bucket/key) instead of virtual-host addressing. true for MinIO and Supabase.
allowPrivateEndpointsfalseSSRF guard. When false, an endpoint that resolves to a loopback, link-local, or private (RFC1918) address is rejected at startup. Set true to opt in (for example in-cluster MinIO). Leave false for any internet-facing vendor.
requestChecksumCalculationWHEN_SUPPORTEDWHEN_SUPPORTED, WHEN_REQUIRED, or DISABLED. Set WHEN_REQUIRED if your vendor rejects auto-added x-amz-checksum-* headers (older Backblaze B2, some R2 corner cases).
responseChecksumValidationWHEN_SUPPORTEDWHEN_SUPPORTED, WHEN_REQUIRED, or DISABLED. Set WHEN_REQUIRED if you see false-positive checksum-mismatch errors on GET from a vendor that never returns checksum headers.
SSRF guard

allowPrivateEndpoints defaults to false. The server resolves the configured endpoint host and refuses to start if it points at a loopback, link-local, or private IP. This blocks an admin-supplied endpoint from being pointed at the cloud metadata service (for example http://169.254.169.254/) to exfiltrate instance-role credentials. Only set it to true for a trusted in-cluster store such as MinIO.

Per-Vendor Cheat Sheet

VendorendpointregionpathStyleAccessNotes
AWS S3(blank)your regionfalseUses the AWS regional default.
MinIO (in-cluster)http://minio:9000us-east-1trueAlso set allowPrivateEndpoints: true.
Cloudflare R2https://<acct>.r2.cloudflarestorage.comautofalseIf uploads fail with unsupported header x-amz-checksum-*, set requestChecksumCalculation: WHEN_REQUIRED.
Supabase Storagehttps://<project>.supabase.co/storage/v1/s3your project regiontrueNon-ASCII display filenames are fine - the storage key is opaque.
Backblaze B2https://s3.<region>.backblazeb2.comyour regionfalseOn B2 deployments older than July 2025, if uploads return Unsupported header x-amz-checksum-crc32, set requestChecksumCalculation: WHEN_REQUIRED.
DigitalOcean Spaceshttps://<region>.digitaloceanspaces.comyour regionfalse5 GB per-object cap (regardless of multipart).

Sharing Credentials with the Cluster Artifact Store

The storage.s3.* block is used in two places: the s3 storage provider (persistent user uploads) and the cluster artifact store when cluster.artifactStore: s3 (transient multi-node job artifacts). When both use S3 they reuse the same credentials and bucket. The cluster store writes under a separate key prefix (cluster.s3.keyPrefix, default transient/) so a single bucket can host both persistent uploads and transient artifacts without collisions. Multi-node deployments must set cluster.artifactStore: s3.


Enabling File Sharing

storage:
enabled: true
sharing:
enabled: true # Master switch for all sharing
linkEnabled: true # Enable shareable links
emailEnabled: true # Send email notifications when sharing
linkExpirationDays: 3 # Days until share links expire

Feature Dependencies

FeatureWhat It Needs
File Storagesecurity.enableLogin: true
File SharingStorage enabled
Shareable LinksSharing enabled + system.frontendUrl set
Email NotificationsSharing enabled + mail.enabled: true
Shared SigningStorage enabled + storage.signing.enabled: true

Storage Quotas

Control how much storage space is available.

storage:
quotas:
maxStorageMbPerUser: -1 # Per-user cap in MB (-1 = unlimited)
maxStorageMbTotal: -1 # Total system cap in MB (-1 = unlimited)
maxFileMb: -1 # Max size per upload in MB (-1 = unlimited)

Quotas are checked before a file is stored. When replacing an existing file, only the size difference counts against the quota.


Access Roles

When sharing a file, you choose what level of access to grant:

RoleCan View/DownloadCan Replace FileIn Signing Workflows
EditorYesYesCan sign
CommenterYesNoCan sign
ViewerYesNoRead-only

The file owner always has full access. The default role is Editor.

The difference between Commenter and Viewer only matters in Shared Signing workflows -- both are read-only for regular file sharing.


The My Files Page

When your admin has turned storage on, you get a My Files page (find it at /files once you are logged in). It is your personal space on the server for keeping documents that stick around between sessions, instead of living only in your browser tab.

On the My Files page you can:

  • Upload documents to keep them on the server
  • Organize them into folders, including folders inside folders, and drag files between them
  • Preview a stored file right in the browser without downloading it
  • Rename, move, and delete files and folders
  • Jump around quickly using the folder sidebar on the left
  • Personalize folders with colors and thumbnails so they are easy to spot
  • See where each file lives - every item shows a small badge telling you whether it is in your current browser session or saved on the server

Your folders are private to you. Folders themselves are not shared; you share individual files instead (see Sharing Files below).


Sharing Files

You can share a file from the My Files page, or from the Share button in the top bar of the editor workbench while a file is open.

Share with a Specific User

From the file manager, select a file and share it with another user by their username or email address. You can choose the access role when sharing.

If you enter an email address for someone who doesn't have an account, the system will create a share link and email it to them (if email notifications are enabled).

Generate a shareable link for any file you own. Anyone who is logged in and has the link can access the file. Links expire automatically based on your linkExpirationDays setting (default: 3 days).

You can revoke a share link at any time, which immediately removes access and deletes all access records for that link.

The share link URL follows the format:

https://your-stirling-instance.com/share/{token}
info

Share links require the recipient to be logged in. There is no anonymous or public access -- the link is an additional credential on top of authentication.

Access History

For any share link you've created, you can view who accessed it, whether they viewed or downloaded the file, and when.


Security

  • All endpoints require authentication -- there is no anonymous file access
  • Owner-only controls -- only the file owner can update, delete, or manage sharing
  • Random tokens -- share link tokens are cryptographically random UUIDs
  • Automatic expiration -- expired links return an error and are cleaned up daily
  • Revocation -- owners can revoke any share link immediately
  • Access auditing -- every share link access is recorded with user, action type, and timestamp

Known Limitations

  • Share links require system.frontendUrl to be configured
  • Share links require the user to be logged in -- there is no public/anonymous access
  • The database storage provider uses more memory with large files (use the local provider for large deployments)

Troubleshooting

"Storage is disabled"

  • Verify storage.enabled: true in your settings
  • Verify security.enableLogin: true
  • Verify storage.sharing.linkEnabled: true
  • Verify system.frontendUrl is set to your instance URL

"Email sharing is disabled"

  • Verify storage.sharing.emailEnabled: true
  • Verify mail.enabled: true with valid SMTP settings
  • The link has expired or does not exist. Expired and missing links both return 404 Not Found. The file owner needs to create a new one.

Quota exceeded (HTTP 413)

  • A file or upload that exceeds a configured quota is rejected with 413 Payload Too Large (per-file, per-user, and total-storage caps all use this status)
  • Increase maxStorageMbPerUser, maxStorageMbTotal, or maxFileMb, or delete unused files to free up space

Developer Reference: Storage API

This section is for developers and admins automating storage outside the web app. If you just want to upload, organize, and share files, everything above is done from the My Files page - you do not need any of this.

The full storage and sharing endpoints are listed below. See API Documentation for authentication and general usage.

MethodEndpointDescription
POST/api/v1/storage/filesUpload file
PUT/api/v1/storage/files/{id}Update file (owner only)
GET/api/v1/storage/filesList accessible files
GET/api/v1/storage/files/{id}Get file metadata
GET/api/v1/storage/files/{id}/downloadDownload file
DELETE/api/v1/storage/files/{id}Delete file (owner only)
POST/api/v1/storage/files/{id}/shares/usersShare with user
DELETE/api/v1/storage/files/{id}/shares/users/{username}Revoke user share
DELETE/api/v1/storage/files/{id}/shares/selfLeave shared file
POST/api/v1/storage/files/{id}/shares/linksCreate share link
DELETE/api/v1/storage/files/{id}/shares/links/{token}Revoke share link
GET/api/v1/storage/share-links/{token}Access via share link
GET/api/v1/storage/share-links/{token}/metadataGet share link info
GET/api/v1/storage/share-links/accessedList your accessed links
GET/api/v1/storage/files/{id}/shares/links/{token}/accessesAccess history (owner only)

Folder Endpoints

These are the endpoints behind the folders on the My Files page. All operations are scoped to the authenticated user.

MethodEndpointDescription
GET/api/v1/storage/foldersList your folders
POST/api/v1/storage/foldersCreate a folder
PATCH/api/v1/storage/folders/{folderId}Update a folder (name, appearance)
DELETE/api/v1/storage/folders/{folderId}Delete a folder
PATCH/api/v1/storage/files/{fileId}/folderMove a single file to a folder (or to root when folderId is null)
PATCH/api/v1/storage/files/folderBulk-move files to a folder (up to 1000 per request)

x