Two rules governed version retention: the newest 50 per note, and a sweep of anything past 90 days. The second was wrong. The count already bounds what a note can accumulate, so age only ever deleted history nothing had replaced — and it bit hardest exactly where history is most wanted, a note left alone for a quarter of a year losing its whole past for having done nothing. It is off unless VERSION_RETENTION_DAYS is set. How many versions to keep is now a notebook's own setting, falling back to the server default when left empty. A reference notebook can keep fifty of everything while a scratch one keeps three. That makes freeing attachments answerable. A picture taken out of a note cannot be deleted straight away: the versions from before still show it, and restoring one would find a hole. It is released once no current note and no kept version refers to it — which, with retention by count, is when the last version holding it is pushed out. The server cannot look for itself, bodies being encrypted, so the client reports a note's attachments on every save as it already does for links, and a version inherits what the note held when it was snapshotted — by definition what that version contains. Two things make it safe rather than merely plausible. Silence is not "uses nothing": a note written before this existed has no rows, which looks identical to one with no pictures, so nothing is released for a user while any of their notes has yet to report — the prefetch that fills the search index reports for them on its first pass. And an upload has a row before the note using it does, so recent attachments are left alone. POST /api/admin/housekeeping runs the sweep on demand. Caught while testing: the same block of markup had been pasted into the create-notebook dialog as well, where the state it referenced did not exist — the dialog crashed on open. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
34 lines
1.5 KiB
Bash
34 lines
1.5 KiB
Bash
# cp .env.example .env — Compose reads it from this directory.
|
|
# Back the finished .env up with the database: without ENCRYPTION_KEY the
|
|
# stored notes cannot be read again.
|
|
|
|
# Both: openssl rand -hex 32
|
|
JWT_SECRET=
|
|
ENCRYPTION_KEY=
|
|
|
|
# The first administrator. There is no public sign-up — every other account is
|
|
# created in the app under Account → Administration. Used only until an admin
|
|
# exists, then ignored, so they can never reset anything later.
|
|
INKNOTE_ADMIN_USER=
|
|
INKNOTE_ADMIN_PASSWORD=
|
|
|
|
# Where the notes are kept on the host. Defaults to /srv/inknote in production
|
|
# and ./data for development — leave it commented unless you want another path,
|
|
# since one .env serves both. Create the directory first, or Docker creates it
|
|
# as root and the server (uid 1000) cannot write:
|
|
# mkdir -p /srv/inknote/{db,uploads} && chown -R 1000:1000 /srv/inknote
|
|
# INKNOTE_DATA_DIR=/srv/inknote
|
|
#
|
|
# Under /root also run "chmod o+x /root", or the server cannot reach it at all.
|
|
# That and the alternative: readme.md → Where the data lives.
|
|
|
|
# ── Optional ── unset keeps the default shown
|
|
# INKNOTE_MAX_IMAGE_MB=25 # pasted images
|
|
# INKNOTE_MAX_PDF_MB=200 # imported PDFs, limited separately
|
|
# INKNOTE_UID=1000 # must own INKNOTE_DATA_DIR
|
|
# INKNOTE_GID=1000
|
|
# MAX_VERSIONS_PER_NOTE=50 # default; each notebook can set its own
|
|
# VERSION_RETENTION_DAYS= # off: versions expire by count, not by age
|
|
# VERSION_COALESCE_MINUTES=5
|
|
# ALLOWED_ORIGINS= # only if the UI is on another host
|