An anonymous person may keep 100 MB of images and sounds, an identified one
500 MB, and one person more by a file_allowance row the operator adds; all
of them together 15 GB, since every file is in the database and in each dump
the server keeps (decided 2026-10-02). Each comes from the environment
(FILE_LIMIT_*_MB), with these defaults, documented in deploy/.env.example.
The server checks when an upload starts, counting unfinished uploads so they
can't fill the disk unnoticed, and again when it finishes, counting what is
kept, so uploads started side by side can't overshoot; a refusal is a
FileLimitExceededException, and an upload refused as it finishes is deleted.
deletePerson deletes the allowance with the person. Migration: the new
table file_allowance, only added.
The app keeps a refused file waiting on the device and stops that round of
uploads; the Anki import warns when a deck's files may not fit in the room
left (asset.usage()).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
The asset endpoint (login required) takes a file into the caller's own space
by its SHA-256 and serves an asset row's file only to its space's owner and
members; anyone else, and a deleted row, gets NotFoundException as for a
missing file. startUpload hands out Serverpod's upload description, or null
when the file is there already (claiming it anew); finishUpload keeps the
upload only if its content has the announced hash. Files are stored in the
private database storage at spaces/<space>/<sha256>, at most 20 MB.
deletePerson deletes the person's files in its transaction, and a daily
future call deletes the files no live asset row refers to, and uploads never
finished, 7 days after they were last claimed (ADR 0004, rule 5).
The tests run on the sync engine's database, as the server does, without
rollback: its writes take transactions of their own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>