Skip to main content

Changelog

Release notes, security updates, and feature milestones.

  • Security hardening across the API, auth and data layer.
  • Node.js runtime upgraded to the latest security release. npm dependencies updated to their newest versions.
  • Repository hygiene.

  • Contact ticket system instead of a public e-mail address. /contact replaces every published mailbox: a form (category, optional e-mail, subject, message) creates a ticket with a short public reference such as F0CK-7F3K9Q. Reporters keep the number to check the status and a one-time reply token to add information; a moderator answers by e-mail only when an address was given. Limits: 3 new tickets per hour per IP, honeypot + minimum fill time + the site's existing hCaptcha.
  • Encrypted at rest. Subject, message, e-mail address and all replies are AES-256-GCM ciphertext (v1:<iv>:<tag>:<ciphertext>, new IV per record). Writing is fail-closed: with a missing or malformed CONTACT_ENCRYPTION_KEY no ticket is created and the reason is logged. IPs are never stored - only sha256(CONTACT_IP_SALT + ip). See wiki/guides/Contact-Tickets.md.
  • Moderation → "Contact" tab. Queue with status/priority filters ("only mine", "unclaimed"), priority badges, detail panel with the decrypted conversation, claim / reply / close / release / delete and a full audit trail. Claiming is atomic (status: "open" is part of the update filter), so two moderators clicking at once produce exactly one winner and the loser gets a 409 naming the current owner. A claimed ticket is read-only for everybody else; admins can release it back to the queue (logged).
  • Staff notification. A new ticket mails every admin/moderator with an e-mail address - individually, no shared To header - with subject [f0ck Contact] <Category> — <ticket number>, a ~200 character message preview and a deep link to https://f0ck.org/moderation?tab=contact&ticket=<id>. Mail failures never block ticket creation.
  • Monero (XMR) as a second way to pay for Premium. The Premium page now has a "Pay with Monero" section next to Stripe with the same four plans, each showing its USD price and the XMR amount, plus a scannable QR code for a monero:<address>?tx_amount=<amount> URI (no payment ID needed). Users can attach an optional transaction hash and press "I have sent the payment", which opens a pending ticket that freezes the XMR amount. An admin confirms or rejects it in the Moderation Dashboard → Monero tab, and confirming sets the same premium fields the Stripe webhook writes (isActive, plan, startDate, endDate for the plan's duration).
  • The XMR rate is never hardcoded: it comes from a public price API by default and falls back to operator-maintained environment values when that call fails, so the operator can pin the amounts exactly. The receive address, the price endpoint and the ticket lifetime are all configurable - see .env.example for the full list.
  • Monero receive address is now checksum-verified. MONERO_WALLET_ADDRESS is validated offline with the full Base58 block codec, the network prefix and the Keccak checksum - everything validate_address on the wallet RPC checks. A typo that used to look fine is now a loud error naming the reason (bad_checksum, not_mainnet, integrated address) instead of money routed to a stranger. Integrated addresses are refused as the receive address because their embedded payment id collides with per-ticket tracking; subaddresses are accepted. Keccak-256 is implemented in-module (~60 lines of Keccak-f[1600]) rather than pulling in a dependency for one 69-byte digest.
  • Optional wallet-RPC auto-check: GET /api/monero/check. Admin/mod only. With MONERO_WALLET_RPC_URL set, the route asks a view-only monero-wallet-rpc (via incoming_transfers, matched on the frozen piconero amount, with a tx-hash match winning over an amount match) which open tickets have money waiting, and writes that into the ticket (lastCheckedAt, rpcMatched*) so staff do not have to open a block explorer. A hit never grants premium and never changes a ticket's status - it is a hint, because several tickets can share one amount. Without the RPC URL the route answers 501 and says exactly that, and staff approval stays the complete path. MONERO_MIN_CONFIRMATIONS (default 10) and MONERO_RPC_TIMEOUT_MS (default 15000) tune the lookup.
  • Receive-only, enforced by a test. The XMR rail cannot move funds: there is no spend method, payout or withdrawal code anywhere in src/. tests/monero-wallet-rpc.test.ts sweeps the tree for the wallet-RPC spend verbs and the payout vocabulary, and asserts the operator's secret view key (read from the gitignored deployment env files, never hardcoded) appears in no code file, no doc and no tracked env template. Operator keys live in .env.local / .env.production (both gitignored); .env.example documents them and deliberately ships MONERO_SECRET_VIEW_KEY= empty.
  • Monero auto-check is live. A view-only monero-wallet-rpc (official Monero binaries v0.18.5.3, sha256-verified) runs next to the app as a systemd service on the deploy host, bound to 127.0.0.1:18083 with digest auth and no published port. The wallet is created from the operator's address and secret view key - no spend key on the server - and restores from a height ~4 weeks back (the age of the address), so the initial scan takes minutes and a few hundred megabytes instead of the whole chain; the wallet file stays in the kilobyte range and no node runs on the box. GET /api/monero/check now asks the wallet which open tickets have money waiting (amount match, tx-hash match wins, 10 confirmations) and marks the ticket as a hint - a human still confirms it. The manual approval path stays the fallback and answers exactly that when the RPC is unreachable.
  • The wallet-RPC client speaks epee's digest properly. fetch cannot talk to monero-wallet-rpc: undici refuses URLs that carry credentials, and epee scopes the digest challenge nonce to the TCP connection that received it, so a correctly signed retry on a fresh connection is still rejected with 401. The client parses the credentials out of the URL, signs the challenge (RFC 2617, pinned against the published example vector in the tests) and pins both requests to one keep-alive socket through a single-socket node:http agent. Found against the real binary during deployment - the mocked unit tests alone could not see it.
  • GitHub login -- Sign in or register with GitHub next to Discord, and link or unlink it from the account page.
  • Private messaging -- Encrypted direct messages with attachments and server-enforced tier limits.
  • Follows -- Follow users and tags, with follower and following counts on profiles and tag follow buttons showing real numbers everywhere a tag appears.
  • Statistics-driven tags page -- Rebuilt around actual statistics: KPI tiles, a tag-type donut, top 10 bars and 7-day activity, computed from one API call with server-side aggregations.
  • Structured data everywhere -- Organization and website graph in the served HTML, ImageObject and VideoObject on posts, ItemLists on tag, pool and list pages, ProfilePage on user profiles and full article schema for the blog. No fake ratings or metrics were invented.
  • Accessibility pass -- Focus trap and focus return in the lightbox, toasts that render, labels on every form control, ARIA states on toggles, pagination and charts, contrast fixes and reduced-motion coverage.
  • Content-rating gate on the public API -- Safe content works anonymously; sketchy and unsafe require a login, session or API key on read, upload and edit, with a documented 401 contract.
  • Browser push notifications -- Opt in once under settings, works across multiple devices, the existing notification toggles decide what gets pushed, dead subscriptions clean themselves up and quiet hours are respected. Requires VAPID keys (NEXT_PUBLIC_VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT); without them push is skipped with a warning while in-app notifications keep working. Use the keys from .env.example rather than ones from another project.
  • Moderator management -- Admins can add and remove moderators, with last-admin protection.
  • Post content-rating changes -- Moderators can change a post's content rating.
  • Message deletion -- Delete whole conversations or single messages.
  • Message readability pass -- Day separators, delivery and read ticks, a composer that sticks to the bottom while the thread scrolls, and real loading, error and retry states. Failed sends stay visible with a retry button instead of vanishing.
  • Settings have one source of truth -- Backend first, so autoplay and mute choices apply everywhere instead of wherever the client felt like it.
  • Mod and admin badges -- Now outrank premium badges everywhere.
  • Ad-free experience for premium and staff -- Staff and ad-free users no longer see the sponsored footer or premium upsells.
  • Profile, pool and tags page redesigns -- Quieter layout, breadcrumbs and small pills removed, stats cards in one calm style, privacy settings moved directly under the profile editor.
  • Deployment pipeline -- Rebuilds deterministically, checks disk usage, prunes dangling images before building and health-checks the pm2 process afterwards.
  • Contact ticket system linked prominently -- Linked in navbar and footer, so there is exactly one official channel instead of a mailto link nobody answers. The ticket system itself is described in the Added section above.
  • Premium perks are enforced, not just listed -- 50MB GIFs, original video quality (free stays at 720p), no ads for premium, real pool and tag-favorite limits with honest upgrade prompts, and every notification toggle fires.
  • API documentation and help pages -- Document the content-rating gate on the API docs, help, register and premium pages. The website itself stays fully usable for anonymous visitors.
  • Account page rearranged -- The profile is one horizontal card (avatar and actions on the left, the nickname/email/bio fields on the right) instead of two columns of unequal height that burned vertical space, and the password section now sits above Login and Connections instead of buried inside it.
  • Avatar controls are buttons, not floating icons -- Upload and remove are pill buttons below the tier hint, in the same style family as Edit Profile. They used to be round icons pinned to the avatar's corner, where they collided with the hint text underneath.
  • Buy Premium button -- Gradient pill with the sparkle icon, matching the premium page instead of the old flat strip.
  • Premium live preview -- Rebuilt in the account page's field language: avatar with frame on the left, labeled nickname input, boxed preview of how others see it.
  • User profile stats complete -- Tags Created and Pools join Uploads, Comments, Likes and Favorites on public profiles, same tile palette as /account.
  • Premium purchase happens after registering, never anonymously -- Both pay buttons send anonymous visitors to /register (a ticket is bound to an account, so there is nothing to buy without one), and register carries its destination through login, so the user lands back on the purchase. The dead-end "Failed to start checkout process" toast is gone: provider problems name themselves, an expired session says so. Monero moved to its own page (/premium/monero) and gates anonymous visitors the same way.
  • Monero symbol -- The real Monero mark from the official press kit replaces the "XMR" text glyph on the premium page, the pay button and the checkout.
  • Telegram group in the footer -- A "Telegram" link to https://t.me/f0ckorg opens the external-services row, ahead of the ShareX/Flameshot host, ZNC bouncer and TeamSpeak entries.
  • Monero integrated-address checksum verified against the wrong bytes -- The 106-character form was checked against bytes 65..69 of the decoded 77-byte blob, which is the first half of the payment id, not the checksum. Every real integrated address was rejected as bad_checksum because the payment id was never covered. The test suite stayed green only because the 106-character test vector had been invented to fit the broken rule.
  • The checksum is now verified against the published example addresses from the official Monero documentation, and changing a single character inside the payment id breaks the checksum as expected.
  • Upload tags no longer silently dropped -- Tags that are typed, pasted or picked from the dropdown all persist. One clear error message replaces the guessing, and a single tag that cannot be stored no longer kills the whole upload.
  • Animated GIFs stay animated -- Sharp used to flatten them into static images without telling anyone.
  • Login broken by CSP -- The widget's Content Security Policy blocked hCaptcha's newassets domain, so verification failed. Allowed in both the main CSP and the proxy; the rotated cocks secret is picked up again. An invalid secret is now called out loudly in the server log instead of hiding behind a generic error.
  • Canonical URLs pointed at the homepage -- The metadata helper and root layout defaulted every page to /, which pushed /tags and friends out of Google's index. Every public page now declares its own canonical.
  • Legacy premium accounts repaired on startup -- Accounts that still carried the old hideAds: false default are fixed on server start, no manual step.
  • Ad posts stay visible with closed comments -- The rotating ad URLs used to break the comment APIs.
  • Nickname gradients render as gradient text -- The CSS background shorthand was resetting the text clip, producing colored blocks. All saved formats still work.
  • Mention autocomplete -- Stopped showing stale results.
  • Message threads no longer jump while reading.
  • Avatar/username collision on profiles -- Avatars no longer collide with usernames.
  • Tag follower counts heal on read instead of drifting.
  • Login redirect stays on-site -- A callbackUrl on /login is now validated to be a relative path before the post-login jump, the same guard the register page already had. A crafted link could land a freshly authenticated user on an external phishing clone; that path is closed now.
  • Private messages encrypted at rest -- Every message body is stored as AES-256-GCM ciphertext. The key comes from the new MESSAGE_ENCRYPTION_KEY environment variable and never leaves the server, so a database leak hands you blobs instead of conversations. Old unencrypted messages are re-encrypted silently the first time they are read. Transport stays HTTPS/TLS on both hops, and DM notifications never carry message text anymore. Set MESSAGE_ENCRYPTION_REQUIRED=true to make sending fail loudly instead of falling back when the key is missing. Full writeup in wiki/guides/Message-Encryption.md.
  • Register redirect stays on-site -- A callbackUrl on /register must be a relative path; an absolute URL is dropped instead of forwarded, so the post-login jump cannot be pointed at another origin. The login page consumes the same parameter and now applies the identical guard.
  • Public API documentation -- With a live tester on the docs page.
  • Saner API key manager -- One-time key display and safe revoke.
  • Tags page redesign -- Clean, readable cards in both light and dark mode.
  • Light theme readability -- Stronger text contrast and proper glass panels everywhere.
  • Avatar frames -- Calm variants next to the glow; the settings preview shows what you actually get.
  • Homepage -- Quieter, with one shared button style.
  • Footer -- The ZNC link points to cocks.io now, with an API docs entry between help and mod view.
  • Comment notification links -- Open the right picture and scroll to the comment they are about instead of dropping you somewhere random.
  • Post links, sitemap and Google previews -- All agree on one address now, so search results stopped sending people to the wrong upload.
  • Upload tags -- Tags survive the upload again whether typed, pasted or picked, and the rules are the same everywhere: 3 to 30 characters, max 30 per post.
  • Oversized uploads -- Tell you before you wait instead of failing in silence.
  • Video previews -- Show an honest error instead of spinning forever when a preview cannot load.
  • Theme flash -- The site stops flashing white on first open; that one hit mobile browsers hardest.
  • Login persistence -- Staying logged in was dead on Tailscale and local network addresses.
  • Public moderation log -- Actually public now and listed for search engines.
  • Comment behavior -- GIFs load, emojis insert where your cursor is, Enter gives a newline while Ctrl+Enter sends, and blank lines survive.
  • Premium nicknames -- Render as gradient text instead of colored blocks.
  • Deployment pipeline -- Rebuilds the server deterministically and checks itself afterwards.
  • Dependency updates -- Next.js, the mailer, image processing and the HTML sanitizer were updated, plus the Giphy access fix that gets comment GIFs loading again. The few dev-tool warnings that remain have no upstream fix and never reach production.

  • Premium pricing -- $2.99 monthly, $7.99 quarterly, $14.99 half-yearly, $24.99 yearly, with the live Stripe prices created and a webhook endpoint covering all events.
  • Migration plan -- For the old szurubooru data.
  • Self-hosted CI -- Our own GitHub Actions runner came online.
  • Premium effects everywhere -- Navbar, post grid, post details, comments, user lists and the notification bell; admins and moderators get them automatically.
  • Premium settings persisted -- Max GIF size and original video quality are saved to the backend instead of being forgotten on reload.
  • Premium page avatar frames -- Now use the same component as everywhere else.
  • Homepage stats -- Compact tiles instead of bulky cards.
  • Ad banner -- Admins can edit it from settings.
  • Pool filters -- Survive a refresh through URL params.
  • Content rating filter -- Lives in local storage instead of the URL.
  • Page title -- "f0ck.org" again.
  • Manifest -- Shortcuts and an Apple touch icon.
  • Registration text -- Bigger on mobile.
  • Pool lightbox -- Detects video from the URL.
  • Comment GIFs -- Stopped lazy loading.
  • Blog -- Renders images and links lazily.
  • Fonts -- JetBrains Mono removed from Google Fonts back to a system monospace fallback, because the import cost us builds.
  • hCaptcha wrapper -- The safe wrapper was removed again in favor of the original library.
  • User model -- New font field; the posts API populates the author's role; the users API counts admins and mods as premium.
  • Footer -- ShareX link back, a working ZNC link and the new /f0ck pages listed.
  • API keys -- Show only the first 8 and last 4 characters, do proper ObjectId validation on delete, and admins and mods get the enterprise tier.
  • Service worker broke login -- It cached every script on the internet, hCaptcha's and Cloudflare's included. Same-origin assets only now, and the version bump to v2 forces everyone's cache to refresh.
  • hCaptcha loading -- Loaded earlier with a readiness check and a spinner instead of a gamble on first paint, and it remounts on navigation.
  • Nickname previews -- Use inline CSS instead of dynamic Tailwind classes, so the JIT can't eat them.
  • Theme flash -- The theme stops flashing because an inline script sets the dark class before first paint.
  • Tag edits -- Save to the backend the moment you make them.
  • Tags API role check -- Fixed.
  • Hardcoded Stripe API key removed -- A script with a hardcoded key is deleted from the repo. Rotate that key if you ever ran it.
  • CSP -- cloudflareinsights.com added to the script and connect sources.

  • Nickname fonts for premium -- Space Grotesk, JetBrains Mono, system sans or serif.
  • Nickname colors -- Amber, rose and violet joined the palette, violet being a gradient.
  • Pool lightbox -- Keyboard and touch navigation.
  • Premium billing -- Quarterly at $7.99, half-yearly at $14.99 and yearly at $24.99 next to monthly at $2.99, with discounts that get bigger the longer you commit. The one-time payment option is gone; monthly went from $1.99 to $2.99 and yearly from $15.00 to $24.99.
  • Premium page -- Explains features, provider and refunds, and the preview got an editable nickname input with real-time style switching.
  • Changelog badge -- Moved to 5.6.0.
  • Tag creation while uploading -- Enter, comma and space did nothing unless you picked from the dropdown. TagAutocomplete now actually creates tags, in single and in batch uploads.
  • Login from Tailscale and the local network -- The cookie domain was hardcoded to .f0ck.org. It reads NEXTAUTH_COOKIE_DOMAIN now and those hostnames are allowed origins.
  • hCaptcha preloaded -- In the root layout, so the first login doesn't fail.
  • Stripe webhook import path -- Had a double slash.
  • Demo mode checkout URL -- Was missing a question mark.
  • FileList -- A few dead functions removed.
  • hCaptcha -- On login, registration and password reset, verified server side, because someone kept farming accounts.
  • Blog -- The site finally has one.
  • API docs page -- For anyone building on the public API.
  • Premium tiers -- Basic, pro and enterprise through Stripe, monthly or yearly, with custom nickname colors, avatar frames and an ad-free experience. The premium page previews all of it: six solid colors, six gradients, six frame animations, before you pay.
  • Ad posts fight back -- Obfuscated URLs that rotate every five minutes so adblockers can't freeze one id, and random positions in the grid instead of always being first.
  • Full design pass -- One card and button system across every page, consistent rounded corners, glass panels and purple accents on headings and interactive things.
  • SEO infrastructure -- Skip link, SEO metadata with JSON-LD, canonical URLs, Twitter cards and an OG image.
  • Footer links -- ShareX host, TeamSpeak and mod view; Discord and Mastodon are gone.
  • Mobile post view -- Swipes.
  • Comments keep their line breaks.
  • Admins can comment on ad posts.
  • Comments on disabled posts -- Still show read-only instead of vanishing.
  • Tags finally stick to their uploads.
  • Post navigation -- Stops skipping unsafe and sketchy posts, so you always land on the actually adjacent one.
  • Posts page crash -- Stopped crashing from a circular URL sync.
  • Tag filter -- Stopped reacting to a stale closure.
  • Emoji picker and report modal -- Stopped hiding behind comments.
  • Theme provider -- Stopped blowing up the settings page, and defaults to dark on first paint.
  • Banned users -- Really can't log in anymore, not even with a live session, because the refresh callback kills the token.
  • Profile stats -- Stopped counting pools twice while reporting zero uploads.
  • Tag validation -- Same on client and server now, umlauts and uppercase included, 30 characters max.
  • Bio -- Got its 250-character limit.
  • Privacy toggles -- Save themselves with a debounce instead of pretending.
  • Page titles -- Stopped doubling themselves as "f0ck.org | f0ck.org".
  • robots.txt -- Is a Next.js file now, so the moderation log is crawlable instead of blocked.
  • Sitemap -- Refreshes when posts, tags, comments or bans change.
  • Comment links -- Stopped producing NaN branches.
  • ESLint warnings -- Down from 56 to 26.
  • Content Security Policy -- New.
  • Permissions Policy -- Kills camera, mic and geolocation site-wide and restricts payments to the Stripe frame.
  • Rate limits on every mutation route -- Auth 5/min, API 30, reads 60, uploads 10, with proper 429s and retry hints.
  • EXIF stripped from uploads -- Your GPS coordinates stay home.
  • SVG stays disabled -- Because it's an XSS door.
  • CORS -- Only talks to trusted domains.
  • Hardcoded Stripe keys -- Out of the source, env vars only from here.
  • Pre-commit hooks -- With detect-secrets, gitleaks and friends that scan before anything lands.
  • Admin mass delete -- For spam accounts.
  • Type errors -- Now fail the build in production instead of being ignored.

  • Hardcoded Stripe test keys removed -- Everything goes through env vars and the code throws if they're missing. The old keys are still sitting in git history, which is written down in SECURITY_ROTATION.md — rotate them if you run a fork.
  • Pre-commit hooks -- detect-secrets and gitleaks scan before anything lands, plus a local secret scan script wired up as an npm task.
  • Content Security Policy -- In next.config.mjs, covering Stripe, Discord, Giphy and websocket connections.
  • Permissions Policy -- Kills camera, mic and geolocation site-wide.
  • Security headers -- Consistent through a shared util, plus CORS rules for the API.
  • Uploads -- Strip EXIF so your GPS coordinates stay home; SVG stays disabled because it's an XSS door.
  • Rate limits -- On every mutation route: auth 5/min, API 30, reads 60, uploads 10, answered with a 429 and a retry header.
  • SEO groundwork -- JSON-LD with a website schema, canonical URLs, keywords, Twitter cards, an OG image and a skip link.
  • Contributor docs -- Conduct, contributing, dependency, audit, rotation and SEO docs.
  • Dependabot -- Watches GitHub Actions now too.
  • Build -- Type errors fail the build in production instead of being ignored.

  • Tag types -- General, character, series, artist and meta, with colors and badges on cards and lists, a filter on the tags page, and a tagtype parameter on the API to match.
  • Public status endpoint -- /api/public/status with active users, posts, comments and tags for anyone who wants to build something on the API.
  • Premium page preview -- Six solid colors, six gradients, six frame animations, so the page finally previews what you buy.
  • Footer -- Loses its Mastodon link.
  • Video uploads when ffmpeg is missing -- They used to fail the whole upload. They upload without transcoding now, and when metadata extraction fails the upload uses sensible defaults instead of erroring out.
  • Dark mode by default from first paint -- The context used to wait for a mounted check, which flashed white and ate system preferences on the way.
  • Tag validation -- Same on client and server now, umlauts and uppercase included.
  • Twelve npm vulnerabilities fixed.

  • Comment formatting -- Bold, italic, underline and strikethrough, with buttons in both comment toolbars and a preview that shows formatted text instead of raw markdown.
  • Sitemap -- Refreshes when posts, tags, comments or bans change; the cache dropped from an hour to five minutes. The moderation log stopped being submitted as a sitemap, because it's a page, not a sitemap.
  • Page titles -- Stopped doubling themselves as "f0ck.org | f0ck.org".
  • robots.txt -- Is a Next.js file now, so the public moderation log is crawlable instead of blocked.
  • Footer, help and terms -- Ko-fi links are gone, replaced by ShareX and TeamSpeak links.
  • Post lookup by URL -- The two-step post lookup was removed, so posts stopped being found when you opened them by URL while the database had them just fine. A patch for the breakage the 5.3.3 commit itself caused.
  • Theme provider crash -- Crashed the settings page: a mounted guard rendered children outside the provider.
  • Tags missing from post cards -- The listing API didn't select them and the grid didn't render them.

  • Comments linking to wrong posts -- The canonical id fallback quietly overwrote the real post id with the numeric one whenever the two drifted apart, so links landed somewhere else entirely. The API now only fills in the numeric field when it's missing and leaves the original id alone, and every place that builds a post link — comments, notifications, profiles, recent activity — prefers id over numericId, matching the URLs people actually see.
  • Tagless posts returned 500 -- A ReferenceError from an undefined variable in the post detail route turned every tagless post into a 500 wearing a "post not found" costume.

  • Comment links rendered as "[object Object]" -- Object ids weren't converted to strings in the API and in the components, so fragment links pointed nowhere helpful. The comments API also returns a real string id on create instead of whatever Mongoose felt like.
  • Upload tag bug -- Touching one item's input polluted the others. The inputs are locked during upload and each item keeps its own state now.
  • Video temp file leak -- A temp file could leak on failure.
  • video/x-mov whitelist -- Finally made it.
  • nodemailer 8.0.11 arbitrary file read (GHSA-p6gq-j5cr-w38f) -- Via the raw option. Upgraded to 9.0.1, and with it all 23 dependabot alerts are closed.

  • Admin user management endpoint -- List, search, change roles and grant or revoke premium.
  • Footer link -- To the ZNC bouncer.
  • Packages -- Regular dependency update.
  • Typo fix -- Someone finally fixed the "keep a keep a changelog" typo.
  • ObjectId queries -- Produced NaN branches when resolving URLs.
  • Comment fragment previews -- Fed unapproved comments to search engines.
  • Adjacent post navigation -- Didn't understand legacy numeric routes.
  • Sitemap -- Keeps unsafe posts out of comment URLs now.

  • Comment post linking -- Fixed critical bug where comments linked to wrong post IDs (e.g. comment on post/300 linked to post/25). Root cause was id vs numericId field divergence in Post model. All comment API routes now consistently resolve and return numericId as canonical post identifier.
  • Comment API post lookup -- GET and POST /api/comments now search by { $or: [{ numericId }, { id }] } instead of only { id }, ensuring correct post resolution regardless of which field matches.
  • Comment POST response -- Fixed postObjectId?.id (which returned ObjectId hex string) to use the actual numeric post ID.
  • CommentList data mapping -- Post ID mapping now prioritizes numericId over _id for consistent URL generation.
  • Tag upload limit -- Increased max tags from 15 to 30 across all upload paths (normalizeInputTags, validateUploadParams, client-side validation in FileList, BatchTagging, upload page).
  • Sitemap comment URLs -- Comments sitemap now resolves numeric post IDs via batch lookup instead of using raw ObjectId strings in URLs.
  • Sitemap post filtering -- Unsafe-rated posts excluded from sitemap for better SEO quality.
  • User profile Recent Activity overflow -- Added min-w-0, break-words, truncate classes to prevent long titles/content from breaking layout.
  • Post page adjacent post navigation -- Fixed to use numericId for navigation and _id for fallback lookup.
  • Post page metadata -- Removed noindex for unsafe content -- all posts are now indexable.
  • Root layout title -- Changed from "Free ShareX Host" to "f0ck.org - Anonymous Imageboard".
  • Site metadata name -- Changed from "Free ShareX Host" to "f0ck.org" across all pages.
  • Moderation public page -- Redesigned from card-based list to structured table layout with consistent columns (Action, Time, Details, Reason).
  • Filter hover effects -- Removed hover:-translate-y lift animations from filter containers and buttons on /posts/, /tags/, and /users/ pages for a cleaner, more static appearance.
  • Tags page -- Added missing <Footer /> component to /tags/ page.

  • Dynamic version display on homepage pulled from package.json environment variable.
  • Public moderation log page at /moderation/public with read-only activity wall.
  • Footer "Mod View" link to the public moderation log, so every visitor can reach it.
  • API endpoint /api/moderation/public-activity for anonymous access to moderation actions.
  • video/x-mov MIME type support for .mov video uploads.
  • Footer: Removed sx.f0ck.org link, Teamspeak entry moved to Ko-fi Support link (already in place).
  • Changelog page error message: Now names the searched file paths when CHANGELOG.md cannot be read from the filesystem.
  • Rules page: Removed Discord integration section, added "Reporting Content" guidelines.
  • Navigation: Desktop "Home" link now uses gradient styling matching footer.
  • Tag validation: Now allows underscores in tag names for better compatibility.
  • Validation error messages: Improved specificity for unsupported file types.
  • Upload rejects fewer-than-3 tags and normalizes/dedupes/lowercases tag input before persistence.
  • Post tag editor now creates tags via /api/tags and persists additions with PUT /api/posts/[id]/tags; spacebar no longer triggers tag creation.
  • Post page tag enrichment uses one $in query instead of N+1 findOne calls and matches case-insensitively.
  • Moderation delete path decrements author uploads for posts and invokes comment stats cleanup for comment deletions.
  • Sitemap revalidation added for /api/user profile updates and pool membership changes.
  • Comment timestamps now link to the matching comments view for the post instead of fragment-only anchors.
  • Post page exposes a Mod Log shortcut for moderators/admins.
  • Account statistics tiles for likes, favorites and tags are now clickable filters.
  • Updated dependency versions to address Dependabot advisories.

  • PNG upload error -- Resolved a.pipeThrough is not a function crash by aligning processUpload to the existing Readable.toWeb(Readable.from(...)) pattern already used in /api/upload and assemble-chunks routes, restoring PNG/Web Stream compatibility with file-type v22.
  • Chunked image upload rejection -- The assemble-chunks route no longer rejects all non-video uploads; PNG, JPEG, and other image formats are now accepted after chunk reassembly.
  • Chunk metadata safety -- Malformed or undefined chunk meta.json values (tags, contentRating) no longer crash chunk assembly; tags are validated as arrays and content ratings are coerced to the supported set before being persisted.
  • Legacy PNG MIME support -- image/x-png is now whitelisted across upload entry points, preventing false rejections on older browser-reported PNG types.
  • Client paste path validation -- Clipboard-paste uploads in the dropzone now apply the same type/size filter as drag-and-drop, removing a bypass for oversized or unsupported pasted PNGs.

  • Comment formatting toolbar -- Bold, Italic, Link, and Preview buttons in comment creation/reply/edit forms.
  • Comment anonymous hint -- Shows "No account needed" text for unauthenticated users in comment form.
  • Comment post thumbnails -- Comment listings now show post thumbnails next to "Comment on:" links.
  • Blog system -- /f0ck/blog/ listing and detail pages with RSS feed at /api/blog/rss.
  • Dynamic sitemap -- Sitemap now includes public user profiles and individual comment pages alongside posts, tags, and pools.
  • Changelog page redesign -- Collapsible version sections, search/filter, collapsible subsections with item counts, back-to-top button, compact summary badges per version.
  • Settings page redesign -- Polished animated Switch component with checkmark/X icons, improved setting row layout with descriptions, better section grouping.
  • Comment mobile responsiveness -- Action buttons wrap on mobile, responsive mention dropdown, scrollable formatting toolbar.
  • EmojiPicker portal rendering -- Now renders via React Portal to document.body to prevent z-index stacking issues.
  • Privacy settings placement -- Moved to bottom of account page, after security integrations section.
  • Post navigation skipping unsafe/sketchy posts -- getAdjacentPosts no longer filters by content rating -- navigation always finds the truly adjacent post regardless of rating.
  • Posts page pagination crash -- Fixed circular dependency between URL sync and page state that caused infinite offset loop when infinite scroll was disabled.
  • Tag filter double-click -- Changed from value-based to index-based tag removal using ref to avoid stale closure.
  • Comment z-index layering -- Emoji picker and report modal now properly layer above comments via portal rendering and z-index fixes.
  • PostFilter "More" button overflow -- Added flex-shrink-0 to prevent button compression on smaller screens.
  • Comment report modal z-index -- Bumped to z-[10000] to render above emoji picker.
  • Account bio character limit -- Enforced 250 character limit with visual feedback (yellow at 200+, red at 250).
  • Privacy settings not saving -- Privacy toggles now auto-save to backend via PUT /api/user with 500ms debounce and status feedback.

API -- /api/user

  • MongoDB password projection error corrected -- GET /api/user now returns 500 because the aggregation pipeline mixed inclusion (password: 1) with exclusion (password: 0) in the same $project stage. Fixed by removing the inclusion; only password: 0 is used, preventing MongoServerError: Invalid $project. Ref: src/app/api/user/route.ts.

EmojiPicker & GifSelector -- runtime crash

  • onClose is not a function crash fixed in both EmojiPicker (src/components/EmojiPicker.tsx) and GifSelector (src/components/GifSelector.tsx): onClose is now an optional prop with a default no-op, so components render safely even when the prop is omitted.
  • CommentList comment creation form (src/app/comments/components/CommentList.tsx): EmojiPicker and GifSelector instances in the comment creation box now receive the correct onClose callback and no longer depend on a non-existent wrapper onClick handler.
  • PostDetails comment composition box (src/app/posts/components/PostDetails.tsx): EmojiPicker and GifSelector both receive onClose callbacks, matching the implementation already present in the inline Comment reply box.

Navigation labels swapped

  • Posts list pagination (src/app/posts/components/PostsPage.tsx): Left arrow button was labelled "Next" but navigated to the previous page; right arrow button was labelled "Previous" but navigated forward. Both labels swapped to read Previous / Next, matching the Math.max(1, p-1) and Math.min(totalPages, p+1) action handlers.

Post-level navigation labels swapped

  • Single-post post navigator (src/app/posts/components/PostNavigation.tsx): Previous button was wired to nextPostId (higher-ID / newer post, not the older one) and Next button was wired to previousPostId. JSX conditionals swapped so that Previous is shown only when previousPostId exists and navigates to the older post, while Next is shown only when nextPostId exists and navigates to the newer post.

Footer

  • Discord server link removed from footer (src/components/Footer.tsx); TeamSpeak 3 ts3server://ts.f0ck.org link retained as the sole external chat link.

Next.js Image config

  • images.qualities: [75, 95] added to next.config.mjs so that quality={95} used in PostDetails.tsx is an allowed value and the Next.js build/DevTools warning disappears.

Account page -- session fallback for avatar

  • AccountCard initial avatarUrl state (src/app/account/components/AccountCard.tsx) falls back to session?.user?.avatar (the value already known to NextAuth at login), preventing a flash of the default avatar before the /api/user profile API completes.

Google indexing / SEO

  • Added src/app/sitemap.ts so that Google and other search-engines can discover /posts, /comments, /account, /moderation, /f0ck/changelog, /rules, /terms, and /help -- previously only the homepage was listed in any indexable source.
  • Changelog page file-access path candidates expanded (src/app/f0ck/changelog/page.tsx) to include an absolute project-root fallback, so CHANGELOG.md reliably loads even in standalone Docker/prod contexts where process.cwd() points to the server working directory rather than the source tree root.

  • Phase 0-1-1: Critical SSRF hardened with env var fallback + DNS resolution checks; production data-leak (console.log) removed from PostDetails.tsx; ModActionsPanel action whitelist via TypeScript as const.
  • Phase 0-1-2: ESLint reduced 56 to 26 warnings (0 errors); let to const x8; 4 HTML-entity escapes; 4 require() to ESM imports; 11 react-hooks/exhaustive-deps fixes.
  • Phase 0-1-3: ErrorBoundary class component with getDerivedStateFromError + Try-Again reset; ThemeContext SSR hydration fixed; module-level side-effects removed from layout.tsx.
  • Phase 0-1-4: Tag validation fixed (max 15 tags, per-tag length/regex check inside loop); normalizeInputTags helper added; tag persistence fallback (new Tag().save() when findOrCreate fails); chunk upload tags forwarded via meta.json side-channel; N+1 tag query batched to $in; Tag model regex accepts hyphens.
  • Phase 0-1-5: LiveWall component with AbortController polling; QuickActions onTabChange wired; PostModerator 8 dead typeof toast !== 'undefined' guards removed; ModLog action types fixed (feature/unfeature to MARK_AS_AD/REMOVE_AD).
  • Phase 0-1-6: Comment creation form UI added to unified CommentList; stale closure fixed in handleLoadMore; 3 replace() to replaceAll() fixes in GIF/content rendering; /post/[id] page renders inline CommentList; 1,798 lines of dead code removed across 6 old comment files.
  • Phase 0-1-7: PostNavigation label direction corrected; content-rating filter reads ?rating= from URL; keyboard navigation fixed (A = older, D = newer); PostNavigation wrapped in Suspense boundary; getImageUrlWithCacheBuster regex fixed with g flag.
  • Phase 0-1-8: CommentsPage ingestion path quarantined (no longer imported into posts detail routes); CommentModeration uses unified component; CommentFilter and CommentList refactored to shared component shape.
  • Phase 0-1-9: Manifest page with layout/content/PDF route; Values page (8 hacker values); Community page with guidelines; History page with milestones; API documentation page; Blog system with data/listing/detail pages/API/RSS.
  • Phase 0-1-10: Vitest config with setupFiles; test env vars/hooks; 18 navigation filter tests; 30 tag E2E tests; 48 new tests all passing.
  • Phase 0-1-11: AddToPoolModal enhanced (search, loading, error); Pool API permission checks with moderator override; contributor tracking on pool actions; Pools field added to User model; Pool stats on user profiles.
  • Phase 0-1-12: Full build, lint, test pass; CHANGELOG and README updated; 66 tests (65 passing, 1 pre-existing TS check unrelated).
  • Tag model type enum now includes 'meta' alongside general/character/copyright/artist.
  • User model privacySettings now has showPools field with isProfilePrivate defaulting to false.
  • Account page: _maxAge: '0' added to profile-fetch requests to ensure uncached data.
  • Tag edit/count facade backed by service abstractions; view-layer tags delegate to orchestrator.
  • Upload edge validated; pool-edit guarded; narrative system refactored with unambiguous results.
  • Help system migrated to dynamic sub-page layout.
  • Session-update token-hardening prevents privileged-field overwrite from client-triggered updates; identity-field refresh avoids stale session during auth transitions.
  • Changelog blank-line rendering preserves intentional empty lines from Markdown; list-typo and indentation preserved.
  • Tag edit endpoint infinite-loop corrected; stale user-state after anonymous upload countered.
  • comments/filter-users route pruning; error-format contract explicitly checked.
  • Post count updated via ModLog pipeline; creative writing filter response corrected.
  • User-list deduplication.
  • Pool bug categorisation: in-place tag/pool editor page loading.
  • Watermark placement corrected on CMD+Shift+2; embed page layout removed; test-administration contract hardened.

  • file-type upgraded from v21 to v22 with migration to new stream-based API (fileTypeFromStream).
  • eslint upgraded from v9 to v10 with full Flat Config migration (languageOptions, globals, js.configs.recommended).
  • typescript upgraded from 5.9 to 6.0.3.
  • @types/react upgraded to 19.2.18, @types/react-dom to 19.2.6, @types/node to 25.7.0.
  • Dependency Stack Refresh -- All production dependencies updated to latest stable versions:
    • eslint: 9.39.2 to 10.3.0
    • file-type: 21.3.0 to 22.0.1 (breaking: stream-based API)
    • typescript: 5.9.3 to 6.0.3
    • next: 16.2.6 (unchanged, latest stable)
    • react/react-dom: 19.2.6 (unchanged, latest stable)
    • tailwindcss/@tailwindcss/postcss/@tailwindcss/forms: 4.3.0 (unchanged, latest stable)
    • @types/react: 19.2.14 to 19.2.18
    • @types/react-dom: 19.2.3 to 19.2.6
    • @types/node: 25.6.2 to 25.7.0
    • eslint-config-next: 16.2.6 (unchanged)
  • ESLint 10 Flat Config Migration (eslint.config.mjs):
    • Added js.configs.recommended as base config.
    • Consolidated env, globals, parserOptions into unified languageOptions block.
    • Imported globals from globals npm package (globals.browser, globals.node, globals.es2021).
    • Removed deprecated @eslint/eslintrc import pattern.
    • All react-hooks rules retained at warn level for backward compatibility.
  • file-type v22 Migration:
    • fileTypeFromBuffer() replaced with fileTypeFromStream(Readable.from(buffer)) in:
      • src/lib/upload.ts
      • src/app/api/upload/route.ts
      • src/app/api/upload/assemble-chunks/route.ts
      • src/app/api/user/avatar/route.ts
    • Added import { Readable } from 'stream' where needed.
  • Security Overrides Expanded in package.override:
    • Added transitive dependency pins: glob-parent, send, mime, qs, path-parse, debug, ms, lodash.merge.
    • Ensures all transitive dependencies locked to known-safe versions.
  • 7 Dependabot advisories addressed via transitive dependency overrides:
    • glob-parent >= 6.0.2 (path traversal)
    • send >= 1.2.0 (DoS)
    • mime >= 4.0.0 (content-type sniffing)
    • qs >= 6.14.0 (prototype pollution)
    • path-parse >= 1.0.7 (ReDoS)
    • debug >= 4.4.3 (ReDoS)
    • ms >= 2.1.3 (ReDoS)
    • lodash.merge >= 4.6.2 (prototype pollution)
  • npm audit passes with 0 vulnerabilities.
  • Broken fileTypeFromBuffer calls now use v22-compatible fileTypeFromStream with Readable wrapper.
  • ESLint build passes cleanly on v10 with updated Flat Config.
  • TypeScript compilation compatible with v6 strict mode.
  • Next.js 16.2.6, React 19.2.6, and TailwindCSS 4.3.0 remain the stable foundation -- no breaking changes in these packages since last release.
  • This release is fully backward-compatible for end users; all changes are internal dependency upgrades and security hardening.

  • Secure FFmpeg-based media processing pipeline for uploaded videos with H.264/AAC normalization.
  • Image optimization pipeline with automatic WebP output for storage and bandwidth efficiency.
  • Extended pools domain capabilities:
    • create/edit/delete pool lifecycle
    • visibility and content rating controls
    • moderation-safe pool management operations
  • Universal report flow for posts and comments with actionable moderation reasons.
  • Expanded moderation action center:
    • bulk target support
    • reason preset support
    • report-clearing operations
  • Dedicated in-app project namespace:
    • /f0ck as a new project-native landing route
    • /f0ck/changelog as in-app release notes
  • /changelog redirect route for consistent external entry points.
  • Dedicated metadata coverage for:
    • /changelog
    • /f0ck/changelog
    • /f0ck
  • Runtime logo-discovery API at /api/logos for dynamic .png logo selection.
  • Privacy persistence and enforcement flow across account, profile, and feed entry points.
  • Tag activity live-stat aggregation for newPostsToday and newPostsThisWeek.
  • Changelog rendering is now sourced directly from Markdown and displayed in structured release cards.
  • Changelog page visual system has been reworked:
    • wider content canvas
    • more readable vertical rhythm
    • mono-styled release title treatment
    • section badges for release categories
  • Release version headers are anchor-linkable from both sidebar and in-card title.
  • Changelog renderer normalizes display text by stripping emoji glyphs.
  • Changelog markdown typography now applies explicit spacing and list indentation rules for long-form readability.
  • Random logo selection no longer uses hardcoded paths and now discovers available logos from public/logos.
  • Moderation dashboard width and density were tuned for large-screen operational workflows.
  • Post and tag filter behavior is aligned for consistent intersection logic.
  • Notifications and tags pagination contracts are aligned between backend response and frontend consumption.
  • Legacy password route now delegates to a central password policy flow with compatibility headers.
  • Changelog blank-line rendering now preserves intentional empty lines from Markdown.
  • Changelog list rendering now keeps correct list indentation and spacing.
  • Mention autocomplete reliability and dropdown lifecycle handling in post comments.
  • GIF handling in post comments now supports independent selection/removal without clearing typed text.
  • Favorite API compatibility by returning both favoriteCount and favoritesCount.
  • Tag filter deduplication and duplicate key issues in active tag rendering flow.
  • User stats navigation query consistency for:
    • liked_by
    • favorited_by
    • disliked_by
  • Compile regression in users/[username]/activity caused by duplicate imports.
  • Notifications pagination behavior (page, limit, hasMore) and load-more UI state drift.
  • Comments and tag-detail page restoration to full component rendering paths.
  • Dependabot alert #74 resolved by upgrading nodemailer to 8.0.5 (GHSA-vvjj-xcjg-gr5g).
  • Session update hardening prevents privileged token field overwrite from client-triggered updates.
  • Identity field refresh during token/session update avoids stale user identity state.
  • Upload route hardening:
    • strict chunk metadata validation
    • safer chunk assembly filename handling
  • URL import temporary file ownership model enforced per user scope.
  • Expanded SSRF defenses:
    • protocol restrictions
    • redirect-hop validation
    • private/internal target blocking
    • payload size limits
  • Additional rate limits on sensitive upload and user-search paths.
  • Atomic report-write safeguards against duplicate report race conditions.
  • Unified path sanitization and base containment checks in image/thumbnail/file routes.
  • Regex escaping for username-query APIs to prevent pattern abuse and false-positive matches.
  • Moderator/admin authorization enforcement for tag mutation endpoints.
  • Post-release security patches (2026-04-10):
    • Fixed path traversal vulnerability in moderation thumbnail regeneration.
    • Fixed password hash exposure in user API aggregation.
    • (Reverted: anonymous uploads remain allowed with ownership validation)
    • Fixed tempFilePath ownership bypass -- now validates for both authenticated and anonymous users.
    • Added pagination to reported posts endpoint to prevent memory exhaustion.
    • Added input validation (length limits, whitelists) to moderation actions.
    • Added pagination bounds to posts API (max 100, min 1).
    • Fixed unhandled promise in user API lastSeen update.
    • Added ObjectId validation in reports API.
  • Live tag activity counters are computed from current DB state on request.
  • Privacy settings are persisted in user documents and enforced in profile, activity, and feed APIs.
  • User list responses are privacy-aware and redact hidden profile fields where required.
  • This is still part of the larger 5.0.0-beta rollout and remains under active stabilization.
  • This entry intentionally serves as the main comprehensive reference for the current beta milestone.

  • More deterministic account-registration error semantics for duplicate username/email handling.
  • Improved fallback handling paths for media previews and selector overlays.
  • Discord OAuth username handling now preserves actual provider identity instead of synthetic fallback naming.
  • Discord OAuth email ingestion now maps cleanly to account profile fields.
  • Registration race condition around duplicate username/email writes.
  • GIF/emoji selector layering and z-index issues in comment composition UI.
  • Video thumbnail fallback behavior for comments containing video posts.
  • Pool navigation assumptions around non-numeric IDs (ObjectId-safe behavior).
  • Error-message quality for registration collisions (clear username/email conflict reporting).
  • Platform-facing UI copy moved to consistent English output.
  • HTML language attribute switched to en for semantic correctness and crawler/accessibility consistency.
  • Authentication and registration messaging normalized for cleaner frontend handling.
  • 4.7.1-beta focused on high-impact correctness fixes ahead of the larger 5.0.0-beta stabilization cycle.

  • Upload progress UI improvements for multi-file flows with retry-friendly feedback.
  • Timezone utility layer for consistent day/week windows in statistics.
  • Scheduled tag-stat maintenance endpoint for periodic counter resets.
  • Additional database indexes for high-frequency query paths.
  • XSS vulnerability in the comment edit endpoint.
  • Video thumbnail behavior in comment-list rendering.
  • Mobile GIF picker positioning and viewport overlap handling.
  • Tag stats daily/weekly counter logic with timezone-aware boundaries.
  • Pool sorting behavior for most_items and related query edge cases.
  • Stats update drift in tag-removal flows.
  • Avatar caching behavior improved to reduce unnecessary reloads.
  • Discord and tag query performance improved through targeted index coverage.
  • Upload and media pipelines aligned for better operational stability during heavy usage.
  • 4.7.0-beta was the foundation release that stabilized media, stats, and moderation-adjacent workflows before 4.7.1-beta and 5.0.0-beta.

Showing 22 of 22 versions