Skip to content

Releases: otatechie/slipnote

v3.18.1 — Operator board menu fixes

Choose a tag to compare

@otatechie otatechie released this 22 Oct 21:16

Fixes to the ⋯ menu on the operator Boards tab.

  • Delete board works on empty boards. On a faded (empty) row, the next row covered the open menu, so clicking Delete board… opened that board instead. The row with an open menu now sits above the rows below it.
  • ⋯ opens the menu, not the board. The row's link stops short of the ⋯ button.
  • No ⋯ showing through an open menu. The next row's button no longer paints over the dropdown.

🤖 Generated with Claude Code

v3.18.0 — Empty boards cleaned up after 30 days; operator board menu; leaner code

Choose a tag to compare

@otatechie otatechie released this 22 Oct 16:50

Cleaning up after boards that were made and never filled, and a leaner codebase. One migration — see "Before you deploy".

Empty boards

  • Removed after 30 days unused. A board with no files that no person has opened for 30 days is removed, along with its courses. A board with any file in it is never touched.
  • Owners are warned first, where possible. Owners who set a recovery email get "Your board … will be removed in 7 days" with an Open my board button. They always get the full week, and opening the board cancels the removal and resets the clock.
  • Bots don't count as visits. Link-preview fetchers (WhatsApp, Telegram, Slack) and crawlers no longer count as opening a board. They used to keep dead boards looking alive whenever a link was pasted in a group chat, and they skewed "opened … ago" and Recently opened.
  • When it runs: daily, via boards:prune-abandoned on the scheduler where one runs. It also runs at most once a day when someone creates a board or opens the operator console, so it works without a scheduler. You get a Telegram message when boards are removed.
  • Privacy and Terms state the rule. Files are never deleted automatically.

Operator: Boards tab

  • Empty boards are hidden by default. The list shows boards with files, with Show N empty boards underneath. A search still finds any board.
  • ⋯ menu on each row with Note to owner and Delete board. Delete asks for confirmation and says what goes: courses, files, size, and both links. Rows with a note waiting say "Note left".
  • Empty rows show the countdown: "removed in 10 days if still empty", plus "owner warned" where an email went out.
  • Privacy now says the operator can delete a board, for example one that breaks the Terms or whose owner asks.

Codebase

  • About 1,700 lines of design-note comments removed from routes, pages, styles and the PHP app code. One-line comments stay where the code isn't self-evident. No behaviour changed.

Before you deploy

Run migrations: adds workspaces.abandon_warned_at.

What happens on deploy: nothing is removed until a board has had no files and no visits for 30 days. On the live site the oldest empty boards are about three weeks old, so the first removals come in about a week. Owners with a recovery email are warned first, and the operator console shows each board's countdown.

256 tests.

v3.17.0 — Operator boards: paged, sortable, a note dialog; softer cards

Choose a tag to compare

@otatechie otatechie released this 22 Oct 16:29

Operator tools that scale past the first ten boards, a note dialog that keeps your place, and softer dark cards. No migrations and no new settings.

Boards tab (operator)

  • Every board, paged. 20 a page with ← Newer · Page 2 of 3 · Older →, instead of only the 10 newest. A search pages through its matches too.

  • Sort by what matters:

    • Newest: who just signed up.
    • Most visited: arrivals in the last 30 days, the best measure of a board a class actually uses.
    • Recently opened: which boards are alive and which are abandoned.
    • Largest: storage, where abuse like free file hosting shows up and never gets reported.

    Each row shows its 30-day visits. The sort stays in the URL, and the footer says which order you're looking at.

  • Note to owner opens in a dialog over the list: in the middle on desktop, a bottom sheet on phones. You keep your place. Saving returns to the same page, search and sort, and a note that's too long reopens with what you typed. Remove note is in the dialog.

  • Quieter rows: board names aren't underlined, and "Note to owner" stays grey until you hover it. It turns blue once a note has been left.

  • Phones: sort fits on one line (Newest · Visited · Opened · Largest), and each row's size, last opened and note action share one line.

Operator tables

  • Rows are more compact, with no dividers.
  • Visitors:
    • Each count shows its share, like "133 · 73%".
    • When every country is Unknown, a line explains why instead of a one-row table.
    • Referrers that are bare IP addresses are counted as "other". They were the site reached by its own address, often through Cloudflare, not a real source. This applies to rows already recorded too.

Dialogs and sheets, everywhere

  • The page stays put behind an open dialog or sheet. A scroll wheel or swipe used to move the list underneath. The scrollbar's space is always reserved, so the layout doesn't shift when it locks.
  • Dialogs never scroll sideways, and an extension's button beside a text box, such as a writing assistant's, no longer gives the note dialog a scrollbar.

Look

  • Dark-mode cards are softer: #16181c, still a touch darker than the page, without the heaviness of a near-black block on long lists. Light mode is unchanged.

248 tests.

v3.16.1 — PHP 8.4.27: the PHP2Shell fix

Choose a tag to compare

@otatechie otatechie released this 21 Oct 20:12

The image now runs PHP 8.4.27. No app changes and no migrations.

PHP2Shell, fixed

PHP 8.4.27 fixes CVE-2026-109618 and CVE-2026-109619: two critical remote code execution flaws in how PHP reads multipart form uploads. They're reachable before SlipNote's own code runs, by anyone who can send an upload. The runtime image is now serversideup/php:8.4.27-fpm-nginx. The exact tag means the server pulls the patched image instead of reusing a cached 8.4 one.

The asset build stage uses node:22.23.4-alpine, today's Node.js security release. It's used only for the build.

After deploying

In Dokploy's terminal for the service, php -v should print PHP 8.4.27. The environment settings from v3.16.0 (TRUSTED_PROXIES, CLIENT_IP_HEADER) carry over unchanged.

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

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.

v3.15.0 — Reset owner link, board settings as a bottom sheet, dialog focus

Choose a tag to compare

@otatechie otatechie released this 19 Oct 19:27

A leaked owner link can now be taken back. No migrations.

Reset owner link

  • Board settings → Owner link → Reset owner link. For a link pasted into the class group by mistake. It's confirmed first, then you get a new owner link, shown once on the "Save your owner link" screen. The old link stops working everywhere.
  • Other browsers really lose access. Before this, owner access in a browser's session wasn't tied to the link. A browser that had used an old link stayed owner even after recovery changed the link. Owner sessions are now bound to the current link, so a reset or a recovery signs out every other browser on its next page load. The browser that made the change stays signed in.

Board settings on phones

  • Board settings now rises from the bottom, only as tall as its content, like every other sheet. The board stays visible above it, and tapping there closes it. It moves up above the on-screen keyboard while you type a recovery email.
  • Tablet and desktop keep the panel on the right.

Accessibility

  • Dialogs on the board page and the start page, and Suggest, now move keyboard focus inside when they open, keep Tab within them, and return focus to the button that opened them when they close.
  • Confirms open on Cancel, and screen readers announce their question and what will happen.
  • The "Anyone with the link can add files" switch keeps focus while it saves, and the settings sections are headings.

Before you deploy

Nobody is signed out. Sessions from before this release are upgraded the first time they're used, so owners who never saved their link keep access. The catch: a browser that got owner access from a leaked link before this release is upgraded too, and keeps it until the owner resets the link. If an owner thinks their link leaked, Reset owner link is the fix.

220 tests.

v3.14.0 — Who can add files, board settings, reaching an owner, and a page for lecturers

Choose a tag to compare

@otatechie otatechie released this 19 Oct 19:07

Tools for the people who run boards: owners can lock uploads and find every setting in one place, the operator can reach a board's owner, and lecturers get a page that answers their first question. Two migrations — see "Before you deploy".

Who can add files

  • "Anyone with the link can add files" is a switch in Board settings, on by default. Turn it off and only the owner can add files: what a lecturer running a board for their course needs.
  • Enforced on the server. A locked board refuses uploads from anyone but the owner, even from an old tab or a hand-made request ("Only the board's owner can add files here."). Its course pages show no Add file, no upload form and no floating button; an empty course says files are added by the board's owner.
  • Reading is never locked. Classmates still open courses and download files. Owning one board gives no say over another board's switch.

Board settings

  • "Owner ⚙" at the top of the board opens one sheet with everything owner-only: the upload switch, the recovery email, and "Exit owner mode on this device" at the foot (still confirmed first).
  • The settings cards that sat under the course list are gone. A new board with no recovery email keeps one small nudge, since that's when the owner link is likeliest to be lost.

Reaching an owner

SlipNote has no accounts, and the recovery email is promised for recovery alone, so there was no way to contact the owner of a busy board.

  • Operator → Boards can now find any board by name, and Note to owner leaves a short message on it.
  • The note shows only in owner mode on that board, until dismissed; an edited note shows again. Reply opens the suggestion form with the board named, and replies land in Operator → Suggestions.

For lecturers

  • /lecturers: what a board is (one private page per class, invisible to search engines, reached only by its link, no paywall), how to have material removed (⋯ → Report this file → Copyright; a removed file can't be uploaded again anywhere), and running a board for your own course.
  • Linked from the footer and the Terms, listed in the sitemap, and indexable.

Fixes

  • Boards can no longer take a site page's address. A board named "Privacy", "Terms", "Start" or "Operator" got a link that opened that page instead and was unreachable. New boards skip any address a site page uses.
  • Terms and Privacy open with the logo, say "board" instead of "workspace", list the operator's note, and point to Report in its ⋯ menu. Last updated October 19, 2026.
  • Footer links are 44px tall on touch and the theme buttons 36px; a stray "·" on phones is gone.
  • The board's top row has room under the phone's status bar.

Before you deploy

Run migrations. Two new columns on workspaces: operator_note (+ operator_note_at) and uploads_owner_only.

Set CONTACT_EMAIL if it isn't set: /lecturers offers it for bulk removals and hides the line without it.

Check for boards already on a reserved address. The fix applies to new boards only. Any existing board whose slug is one of start, privacy, terms, lecturers, operator, download, materials, workspaces, suggestions, storage, resend or up is unreachable today and needs renaming.

217 tests.

v3.13.1 — Your boards: most recently used first

Choose a tag to compare

@otatechie otatechie released this 19 Oct 15:12

A small follow-up to v3.13.0.

Your boards: most recently used first

/start listed your boards by when this browser last gained owner access (creating a board, opening its owner link, recovering it), not by when you last used them. A board you open every day could sit under one you set up once and forgot.

Opening a board, or any course in it, as its owner now moves it to the top of "Your boards". Classmates' visits don't change the list, and a board that's already first writes nothing, so most page views cost nothing extra.

Before you deploy

Nothing: no migrations, no new env keys.

207 tests.

v3.13.0 — Mobile pass, a start page that leads with your boards, and a suggestion form

Choose a tag to compare

@otatechie otatechie released this 19 Oct 15:09

A pass over every screen a student or owner touches, built for the phone first: a start page that leads with your boards, a board and course page with one action each, a suggestion form that works without a mail app, and an operator dashboard that fits a 360px screen. One migration — see "Before you deploy".

Start page and new boards

  • Your boards first. A browser that manages a board now opens /start on "Your boards": each board with where it stands ("1 course · no files yet"), Remove in a ⋯ menu, and "New board" under the list. A first visit still opens on the create form. Boards deleted since drop out instead of linking to a missing page.
  • "Your class", not "Board name". One board for the whole class, with each course inside it. Asked for a board name, people typed a single subject and had nowhere to put courses.
  • Straight to the first course. The receipt says "Add your first course", the new board opens the New course panel, and saving a course goes into it to upload.
  • A lighter receipt. One red label instead of three, Copy inside the share-link box, a flat disabled Continue.
  • Find by name and owner-link recovery are a line of links; the name field opens in place.

Board page

  • The title wraps instead of truncating, and "New course" floats in the thumb zone on phones.
  • Owner · Exit sits in the top row; the share row is one Share button (the phone's share sheet, WhatsApp included) plus QR. On a computer Share copies the link.
  • Blue means tap. Course codes are ink; file counts are a quiet line ("9 files · updated 2h ago").
  • "Add the first file" on an owner's empty course opens it with the upload form up.
  • Reorder hides behind a toggle; search and sort appear from 7 courses.
  • Classmates get the wordmark home and "Run a class of your own? Make a board".

Course page

  • No section pills. Files are already grouped under section headings with counts; search and sort appear from 7 files.
  • Select files reveals checkboxes for bulk delete; single delete is in a ⋯ menu, away from Download.
  • Titles wrap to two lines on phones, type badges share one width, "Download all" is grey.
  • The upload form is a drop area, three section pills, and the two optional fields on one row.

Storage

A board 90% full says so in a box above the list — for owners "delete old files to make room", for everyone else "new uploads may not fit". The small meter steps aside then.

Suggest a feature

  • A form, not mailto. "Suggest a feature" in the app footer, and "Tell us" on the owner card, open a short form: a message and an optional email or WhatsApp for a reply. Most students have no mail app set up, so "Email us" opened nothing.
  • Stored and pinged. Each message is saved and posted to the operator's Telegram when configured. No IP is stored; the page is kept as a path only, so an ?owner= link never is. Five per hour per source, plus a spam trap.
  • Operator → Suggestions tab, newest first, with delete.
  • The owner card shows only to owners of boards with 5+ files.
  • Privacy page lists suggestions and their Telegram ping.

Operator dashboard

  • Login matches /start: centred, with the mark.
  • Header no longer pins over the lists; empty tabs are one line; "At a glance" shows on the landing tab only.
  • Mobile: all five tabs fit a 360px screen, board rows are tappable whole, tiles are tighter, the ambassador form stacks.

Also

  • Clipped titles keep the tails of g, y and p.
  • 44px tap targets on touch for the small links and menu items.
  • DemoSeeder makes five local boards in every state the UI handles: php artisan db:seed --class=DemoSeeder.

Before you deploy

Run migrations. A new suggestions table.

Nothing new in .env. FEEDBACK_WHATSAPP and FEEDBACK_EMAIL are no longer used by the app's feedback prompt (the landing page still reads them). Suggestions reach you on Telegram if TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID are set, and always in Operator → Suggestions.

205 tests.

v3.12.0 — Landing page redesign, visitor counts and feedback

Choose a tag to compare

@otatechie otatechie released this 17 Oct 22:57

A rebuilt landing page, a way to hear from the people using SlipNote, and counts of where visitors come from. Also an installable board, better download names, and a clearer theme switcher. One migration and two new env keys — see "Before you deploy".

Landing page

Rebuilt to Nielsen Norman Group guidance: say what it is first, one action, students' own words, nothing decorative that says nothing.

  • Hero. "Share your class's notes, slides and past papers with one link." beside a class group chat: someone asks for the slides, someone says "scroll up", you post the board link, and a classmate replies "Finally". The link preview shows what it opens: 6 courses · 48 files.
  • How it works. Three steps on a vertical line. Step 1 shows the link the board's name becomes and introduces the owner link; step 2 shows a small board of courses.
  • Compare. WhatsApp group, SlipNote and Google Drive as rows of things a student wants to do, answered with yes / no / partly marks that look the same in every column. Drive gets fair "partly" marks where it earns them.
  • Questions. Shown openly instead of in an accordion, each answer leading with the direct answer. File limits come from config, so the page never states a number the app doesn't enforce.
  • Layout. A floating rounded nav bar, sections separated by space instead of rules, every button fully rounded, and one label throughout: "Create a board".

Feedback and visitor counts

  • "Using SlipNote a lot? Tell us how." Boards with files show this under the course list, with WhatsApp and email links that already name the board. Dismissed once per browser. Hidden until a contact is configured.
  • Arrival counts. A page opened from outside the site adds one to a daily tally of board × country × source. No IP, user agent or cookie is stored; navigation inside the site, crawlers and WhatsApp link-preview fetches don't count. Country comes from Cloudflare's CF-IPCountry, trusted only from a trusted proxy.
  • Operator → Visitors tab. The last 30 days by country, by source (google, whatsapp, ambassador, direct, or the referring site), and the boards people arrive at with each one's leading countries.
  • Privacy page now describes the counts, which it previously ruled out, and its date is updated.

Also in this release

  • Installable as the board. Each board serves its own web app manifest, so Add to Home Screen opens on the board, not on SlipNote. A service worker answers offline navigation with a notice; it caches nothing.
  • Downloads keep the uploader's title and carry the course code, in single downloads and ZIPs.
  • Theme switcher shows all three modes (system, light, dark) side by side instead of a button that cycled through them.

Before you deploy

Run migrations. A new visit_tallies table.

Set two env keys (both in .env.example) to show the feedback prompt:

  • FEEDBACK_WHATSAPP — your number in international form, digits only, e.g. 233241234567.
  • FEEDBACK_EMAIL — optional; falls back to CONTACT_EMAIL.

Leave both empty and the prompt stays hidden.

For countries in the Visitors tab, the site must sit behind Cloudflare with its ranges in TRUSTED_PROXIES. Without that, every country reads Unknown; everything else still works.

VisitTallyTest covers the counts, the Visitors tab and the feedback links. 199 tests.