Skip to content

v3.16.0 — Unguessable board links, pinned files, delete a board, safer uploads

Choose a tag to compare

@otatechie otatechie released this 21 Oct 14:09
· 13 commits to main since this release

Board links can't be guessed any more, owners can pin files and delete a board, and uploads are checked by name as well as content. One migration, run automatically on start-up.

Board links that can't be guessed

  • New boards get a random code after the name (/econ-level-200-k7q2m9), so trying course names no longer finds a board and its files. Names may repeat.
  • Existing boards keep their addresses, so links already shared still work.
  • Opening from /start takes the board's link, never its name. "Lost your owner link?" builds the recovery address from that link too, and the board's Manage panel links to recovery.
  • Misses are rate-limited per visitor: 60 unknown board addresses and 20 failed opens every 10 minutes.

Pin files, delete a board

  • Pin to top. In owner mode, a file's menu has Pin to top. Up to three pinned files lead each section in every sort and search, marked with an amber pin.
  • Delete board. Board settings → Delete board, confirmed by typing the board's name (checked on the server too). Its courses and files go at once.
  • Recovery email, asked at the right time. Owners see "Don't lose this board" once the board holds files, not on an empty one. Not now hides it for 30 days on that browser.
  • "New" on every device. A course a browser has never opened counts the last week as new, so a phone shows what landed while the laptop was in use.
  • A plainer upload receipt. No "Your name" field. The receipt shares the course link and names the file ("New in BIO 101: Week 3 slides"), WhatsApp first.

Security

  • Uploads are checked by name as well as content. A PDF named slides.hta or .exe used to pass and keep its name. The extension must now be allowed and match the content, and downloads and ZIPs take their extension from the stored file, which also covers older uploads.
  • Owners can't be locked out by strangers. Unlock, owner links, recovery and the upload passphrase were limited per board only, so anyone could lock the real owner out. Limits are now per board and per visitor, with a higher per-board ceiling.
  • Real visitor addresses behind Cloudflare. CLIENT_IP_HEADER reads CF-Connecting-IP from trusted proxies only, so rate limits are per student, not per Cloudflare server. * in TRUSTED_PROXIES is still never honoured.
  • Abuse limits. At most 20 reports an hour per visitor; at most 20 section ZIPs an hour per visitor, and none built without 256 MB free.
  • Search takes text only (100 characters, 8 words), with % and _ matched literally.
  • Secrets stay out of logs. Recovery never makes a link while mail goes to the log, where it was a working owner takeover. Telegram errors are logged without the bot token.
  • Dependencies. Laravel, Symfony http-foundation (Cache-Control ReDoS), Guzzle, PSR-7, CommonMark and Flysystem updated; npm audit fix, including Vite's launch-editor advisories. composer audit and npm audit report nothing.

Looks

  • Cards are dark panels with a double edge and no dividers; one secondary button style; quieter lists; one soft focus ring on every control.
  • A course whose files all sit in one section shows a plain list.
  • Fonts load through Vite, so Open Runde shows under the dev server too.
  • Who runs it. OPERATOR_NAME puts "Made with ♥ for students by …" in the footer and "Who runs SlipNote" on /lecturers. Takedowns promise a review within TAKEDOWN_TURNAROUND (default "3 working days").

Before you deploy

  • Storage volume: check this first. Uploads are saved in storage/app/private. Mount /var/www/html/storage/app (or storage/app/private) as a persistent volume. A volume on storage/app/public alone loses every upload on redeploy.
  • Set these two first, in Dokploy → Environment, then deploy. TRUSTED_PROXIES no longer accepts *. The new per-visitor limits need each visitor's own address, and behind Cloudflare and Traefik that address arrives in Cloudflare's CF-Connecting-IP header:
    TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
    CLIENT_IP_HEADER=CF-Connecting-IP
    
    The header is read only on requests that come from a trusted proxy (Traefik's Docker network), so a request sent straight to the app can't name its own address. Without these, every request looks like it comes from one address, and the limits act site-wide.
  • New, optional settings: OPERATOR_NAME (empty hides it) and TAKEDOWN_TURNAROUND.
  • One migration (materials.by_owner, materials.pinned_at) runs automatically on start-up.
  • PHP itself: PHP 8.4.27 (21 October 2026) fixes two critical remote-code-execution flaws (CVE-2026-109618/109619). Once serversideup/php publishes 8.4.27 images, rebuild on them and check php -v in the container.

245 tests.