-
v2.6.2-beta - English and French interface Pre-Release
released this
2026-10-03 02:08:52 +02:00 | 75 commits to main since this releaseThis release adds English and French throughout the SPA, with an instant language switch and a preference remembered on this browser.
New
English and French interface
- Choose English or Français from the Language control in the desktop or mobile profile submenu.
- Switch interface language immediately while keeping your current page and unsaved work.
- Keep your selected language after navigation, reload and logout, including on authentication screens.
- See translated forms, validation, notifications, dialogs and accessibility labels, with localized dates, numbers and counts.
Improvements and fixes
- Keep partial bulk-delete failure notifications available after retrying, including when switching languages.
Under the hood
- Add translation catalog parity, fallback, persistence and SPA interaction tests.
Upgrading
- Install the signed update through Settings on a prepared Linux/systemd installation, or replace the binary and restart the service.
- No database migration or new environment variable is required.
- English remains the default until a language is selected; the preference is stored locally in the browser.
- User content, API data and transactional emails retain their existing language.
Known limitations
- Visual parity checks for this feature were skipped under the accepted slice waiver.
Downloads
-
v2.6.1-beta - Browser update setup documentation Pre-Release
released this
2026-10-02 17:19:58 +02:00 | 77 commits to main since this releaseDocumentation-only release for testing the complete signed browser update workflow from v2.6.0-beta.
The only source change since v2.6.0-beta is commit
7f1e8e572d: docs: deployement guide with new ui update settings.Clarifies browser update helper setup, the actual database directory and port, verification steps, and troubleshooting. No application behavior or database schema changes.
All six platform binaries embed version v2.6.1-beta. checksums.txt and checksums.txt.sig are signed with the existing release key.
On a prepared v2.6.0-beta instance, check for updates in Settings, download the update, wait for Ready to install, then Restart and confirm installation. Verify v2.6.1-beta in Settings, public health, and update-result.json with kind update and outcome succeeded.
Downloads
-
v2.6.0-beta - Signed updates and browser restart Pre-Release
released this
2026-10-02 17:00:03 +02:00 | 78 commits to main since this releaseRenewCMS can now check for releases, download a signed update and install it from Settings. On Linux with systemd, the owner can restart the site from the browser, with automatic binary rollback if the new version fails to start.
New
Updates in Settings
- Owners can check for a newer release, read its release notes and download it while the site stays online.
- Downloads show progress, verify the release signature and binary checksum, check the staged version and back up the database before becoming ready to install.
- The Home page and Settings navigation show an owner-only update notification, and the Home banner can be dismissed for each version.
- Admins can read the update status without starting an update or restart.
Browser restart and installation
- Owners can confirm an update installation or a plain site restart from Settings.
- A restart overlay waits for the site to return and offers a reload action when the restart takes longer than expected.
- Linux installations with systemd can enable browser updates using
renewcms enable-browser-updatesand remove the helper usingrenewcms disable-browser-updates. - The helper verifies the staged files again, keeps the previous binary and restores it if the new version does not start successfully.
Improvements and fixes
- Update status and download progress survive navigation and are shared between owners.
- Update checks and download starts have separate per-user rate limits with retry countdowns.
- Restart confirmations identify whether the action installs an update or only restarts the site, and refuse a request when another owner's action has changed that state.
- Completion messages are shown in the browser tab that requested the restart.
API
GET /v1/system/updatereturns the current and latest versions, release notes, release URL, helper availability, progress, state, error kind and operation timestamps for verified users, without changing state.POST /v1/system/update/checkchecks the release feed for owners and returns 200, 403 for insufficient permissions, 429 withRetry-After, or 500 when the feed cannot be reached.POST /v1/system/update/downloadstarts an owner-only background download with 202, or returns 400 when nothing is available, 403, 409 for an unavailable helper or conflicting job, 422 for a pre-flight refusal, or 429.POST /v1/system/update/restartaccepts an optionalkindofupdateorrestartand returns 202, 400 for invalid input, 403, 409 for an unavailable helper or conflicting state, or 500 when the request cannot be saved.
Under the hood
- Releases include signed
checksums.txtandchecksums.txt.sigfiles alongside the six platform binaries. - The update helper runs separately from the unprivileged app, copies staged files into a private directory and verifies the bytes it installs rather than trusting a mutable staged path.
- Feature specifications, fake-backed Go tests and SPA tests cover release checks, downloads, restart requests and rollback decisions.
Upgrading
- Back up your SQLite database and uploads, then replace the binary and restart manually for this first release with browser-update support.
- No database schema change is introduced by the update features.
UPDATE_FEED_URLis optional and defaults to the project's release feed; existing configuration remains usable.- On Linux with systemd, run
sudo renewcms enable-browser-updatesafter the manual upgrade to enable future updates and restarts from Settings. - Keep the update status files, staged files and backups beside the database in the data directory, with the permissions described in
docs/deployment.md.
Known limitations
- The privileged update helper has automated coverage through fakes, but its first real Linux/systemd update and rollback checks are still pending.
- Browser installation and restart require Linux with systemd; Windows, macOS and other init systems remain manual upgrades.
- Updating or restarting briefly takes the site offline, and a reverse proxy may return errors during that interval.
- Automatic rollback restores the previous binary; it does not restore the database, so schema-changing upgrades still require a backup and a recovery plan.
- Update assets and redirects must use HTTPS on the configured feed host.
Downloads
-
v2.5.0-beta - Media library & image picker Pre-Release
released this
2026-10-01 05:32:13 +02:00 | 84 commits to main since this releaseRenewCMS now has a media library. Every uploaded image lives in one place where you can browse, rename, delete and clean up duplicates, and the article editor can reuse an image you already uploaded.
New
Media page
- A new Media page at
/mediasits between Articles and Users in the sidebar and the mobile drawer. - Browse the library as a grid or a list, with search by filename, sort by newest, oldest, name or largest, and Unused and Duplicates tabs with counts.
- Open an image to see its type, size, dimensions, uploader, upload date and URL, copy its URL, and see which articles use it.
- Rename an image. Only the filename changes, the URL stays the same, so articles keep working.
- Delete one image or many at once. Deleting an image that is still used asks for a stronger confirmation and lists the articles that will show a broken image.
- Upload several images at once, with a progress row for each one and a summary. Drop files anywhere on the page.
- When you upload a file that is already in the library, you are told so and can use the existing image instead.
- A Duplicates view groups identical images and merges them into one. Articles that used the other copies switch to the one you keep.
Image picker in the editor
- The editor's "Add an image" dialog now has Upload and Library tabs. Pick an image you already uploaded, set its alt text and insert it.
- Images already in the article are tagged "In this article".
- Removing an image from an article now only removes it from that article. The file stays in the library.
Improvements and fixes
- Deleting an article no longer deletes its images, and its confirmation says so.
- An image that no longer exists shows a "This image is no longer available" placeholder in the editor, with a way to remove it, instead of a collapsed pixel.
- Deleted images stop showing as soon as you open an article, not only after a refresh. Uploaded images are now revalidated by the browser instead of being cached for a year.
- The editor's Images row in the article details counts the images in the article body.
API
GET /v1/media/lists the library withq,filter(all,unused,duplicates),sort,limit,cursorandhashparameters. Each item carries its size, type, dimensions, uploader,used_incount and aduplicateflag, and the response carries the counts for each tab.POST /v1/media/uploads an image (multipartfile). It returns 201, 400 for an unreadable image, 413 for a file over 10 MB and 415 for another type.PATCH /v1/media/{id}renames an image and keeps its extension and URL. It returns 200, or 404.DELETE /v1/media/{id}deletes an image, even when articles use it. It returns 204, or 404.POST /v1/media/mergetakeskeep_idandmerge_ids, switches the articles that use the other copies to the kept image, deletes the copies and returns how many articles changed. It returns 409 when a copy is gone or the images are no longer identical.GET /v1/media/{id}/usagelists the articles that use an image, with their title and whether they are online.- Changed: images no longer belong to an article.
GET /v1/article/{id}no longer returnsimages, and deleting an article keeps its images. - Changed:
DELETE /v1/article/{id}/image/{imageId}andPOST /v1/article/{id}/imageare removed, replaced by the/v1/media/endpoints. - Changed:
/static/uploadedImages/*now sendsCache-Control: public, no-cacheinstead of a one-yearimmutablelifetime.
Under the hood
- The image domain, repository, use cases and handlers were rebuilt around a library model, keeping the domain, use-case and infrastructure layering.
- WEBP dimensions are read with
golang.org/x/image.golang.org/x/sysandgolang.org/x/textwere updated with it. - New feature specs and tests cover the media API, the Media page, uploads, duplicates and the editor picker.
Upgrading
- Replace the binary and restart. Your
.env, SQLite file and uploads directory stay as they are, and there are no new environment variables. - The database migrates itself on startup. It adds columns to the images table, and a one-time backfill reads each existing image file to fill in its size, type, dimensions and content hash. An image whose file is missing is left alone. Back up your SQLite file and uploads directory first as a precaution.
- Existing images become part of the library and are no longer tied to an article. The old article link is no longer used.
- Clients that call the removed article image endpoints, or read
imagesfrom an article, must switch to/v1/media/. - Browsers keep serving images they cached before the upgrade until they are reloaded once.
Known limitations
- The Media page shows only the first 100 images.
- There is no rate limit on
/v1/media/yet. - The library list loads every article and image in memory, so very large libraries will be slow.
- A file whose extension doesn't match its content type, such as a JPEG labelled as PNG, is rejected.
- Searching filenames with non-ASCII letters is case-sensitive.
- Uploaded SVGs are served as they are. They are sandboxed with a restrictive content security policy, but not sanitised.
Downloads
- A new Media page at
-
released this
2026-09-30 03:11:37 +02:00 | 91 commits to main since this releaseRenewCMS now knows who owns the workspace. The owner manages invites, including cancelling and renewing them, while every admin keeps the same access to content. A new Settings page shows which build is running.
New
Owner and admin roles
- The first registered account is the owner. Everyone who joins through an invite is an admin.
- Only the owner can send, cancel or renew invites. Admins keep the same access to articles, images, the Users list, their Profile and Settings.
- There is exactly one owner. Roles can't be changed or transferred yet.
- Existing installs are migrated automatically: the oldest account becomes the owner and every other account an admin.
Invite management
- Pending invites get a Cancel invite action on the Users page. The invite link stops working.
- Expired invites get a Renew invite action. It sends a new email with a new personal link valid for 3 days, and the old link stays dead.
Users list
- The Users table and the mobile card list now show Email, Display name (a dash when empty), Role, Invite status and Created.
- A new role badge tells the owner and admins apart.
Settings page
- A new Settings page at
/settingssits after Integration in the sidebar and the mobile drawer. It shows the version of the running build.
Improvements and fixes
- The desktop account menu's icons, labels and check mark are now centred in their 32 px rows.
- The account menu follows the redesigned board: System, Light and Dark stay in both the menu and the mobile sheet.
- The article editor's toolbar scrolls horizontally on mobile instead of overflowing.
- The editor's last-saved label now moves from minutes to hours and days instead of counting minutes only.
API
DELETE /v1/user/invite/{id}cancels a pending invite and returns 204.POST /v1/user/invite/{id}/renewreturns 200 with a new code, setsexpires_atto now plus 3 days andcreated_atto now, and resends the invitation email.- Changed:
POST /v1/user/inviteis now owner-only, like the two endpoints above. Other users get 403 with "Only the owner can do this." GET /v1/settings(authenticated, verified users) returns the running build's version.- Changed: user objects from
GET /v1/user/,GET /v1/auth/meandPOST /v1/auth/verifynow includerole(owneroradmin).
Upgrading
- Replace the binary and restart. Your
.envand SQLite file stay as they are. - The database migrates itself on startup. It adds a
rolecolumn tousersand fills it in: the account with the lowest id becomes the owner, every other account an admin. A database that already has roles is left unchanged. Back up your SQLite file first as a precaution. - Behaviour change: admins who joined through a v2.3.0-beta invite can no longer send invites.
- No new environment variables.
Known limitations
- No endpoint changes or transfers the owner role, and users can't be removed yet.
Downloads
-
released this
2026-09-29 10:56:23 +02:00 | 102 commits to main since this releaseRenewCMS is no longer a one-person tool. The admin can now invite other people, each with their own account and the exact same access, and everyone can set a display name.
New
Invite other users
- A new Users page in the back-office lists everyone who can sign in, plus pending and expired invites, with a status badge for each.
- Invite user sends an email with a personal link. The link works once and expires after 3 days.
- Invited people choose their own username and password on a new Accept invite screen, and land on Home already logged in.
- Everyone shares one workspace: same articles, same images, same access.
- Registration stays closed to everyone who wasn't invited.
- A new invitation email template supports light and dark mode.
Display name and profile
- A new Profile page is opened from the account menu at the bottom of the sidebar.
- Set an optional display name (up to 60 characters). It replaces your username in the sidebar, the account menu, the Home greeting, the Users list and the emails RenewCMS sends. Clear it to go back to your username.
- Send yourself a password reset link from the Profile page.
- The account chip now opens a menu with Profile and Log out, and keeps the System, Light and Dark theme choice.
Improvements and fixes
- Accepting an invite is atomic: two simultaneous requests with the same link can only create one account.
- The Accept invite screen shows a retry option instead of a permanent spinner when the invite can't be loaded.
- Invite email validation is now the same in the browser and on the server.
- The mobile account sheet reports its open state correctly and returns focus to the account chip when it closes.
- A failed password reset email is now logged on the server. The response is unchanged.
API
PATCH /v1/user/mesets the display name.GET /v1/auth/meandPOST /v1/auth/verifynow returndisplay_name.GET /v1/user/lists users and invites.POST /v1/user/invitesends an invite and is rate limited.GET /v1/auth/inviteandPOST /v1/auth/invite/acceptpower the Accept invite screen.
Under the hood
- Deleting a user now goes through the persistence model.
Upgrading
- Download the binary for your platform, replace the old one and restart. Your
.envand SQLite file stay as they are. - The database migrates itself on startup. It adds a
display_namecolumn tousersand a newinvitestable. Existing data is untouched. Back up your SQLite file first as a precaution. - Invitation emails need working SMTP settings, like the verification and reset emails already do. No new environment variables.
Known limitations
- Users and invites can't be removed, revoked or resent yet, and there are no roles: everybody is an equal admin.
- Invited addresses that already have an account are rejected. An expired invite can be replaced by sending a new one.
Downloads
-
released this
2026-09-29 05:19:46 +02:00 | 110 commits to main since this releaseThis release reworks image handling in the article editor and brings the password-reset screens up to the new design.
New
Crop and resize before upload
- A Crop and resize step now sits between picking an image and uploading it. It applies to JPEG, PNG and WEBP files. SVG files still upload straight away.
- Move or resize the crop frame by dragging it or its handles. Arrow keys nudge it by 1 px, and Shift+arrow resizes it by 1 px.
- Aspect ratios: Original, Free, 16:9, 4:3 and 1:1.
- Rotate and Reset buttons.
- Output width presets: Original, 1600 px (default), 1200 px and 800 px. The output is never wider than the crop frame.
- A live line shows the final pixel size and an approximate file size before you upload.
Remove images from the editor
- Images in an article can now be removed from the editor. The image file and its database row are both deleted.
- The editor renders images as a dedicated image node.
Password-reset screens
- The Forgot password and Reset password screens follow the new design and cover every state in the spec.
API
DELETE /v1/article/{id}/image/{imageId}(authenticated) deletes an image. It returns 204 on success, 404 for an unknown article or image, and 400 for an ID that isn't a valid uint32.- Changed:
POST /v1/article/{id}/imagenow returns{"id": …, "location": …}instead of{"location": …}. The change is additive, but anything parsing the response strictly should be checked.
Under the hood
- The editor now depends on
@tiptap/extension-image,@tiptap/pmand@tiptap/vue-3, all pinned to 3.31.3. - New feature specs and QA procedures cover crop and resize, image removal and both reset screens.
- New Go HTTP tests cover image deletion, and Vitest specs were added and updated for the editor and reset views.
- The visual-check states cover the new screens, and the design docs are updated to match.
Upgrading
- No new environment variables and no schema changes.
- Replace the binary and restart.
Downloads
-
v2.1.0-beta - Image upload in the editor Pre-Release
released this
2026-09-28 02:32:17 +02:00 | 114 commits to main since this releaseThis release adds image upload to the article editor and fixes several rough edges from v2.0.0-beta.
New
Image upload in the article editor
- The editor's Image toolbar button, or dropping a file onto the editor, opens an upload dialog.
- Choose or drop a PNG, JPEG, WEBP or SVG file up to 10 MB. Progress is shown live, and the image is inserted at the cursor as soon as it is uploaded.
- Invalid files (wrong type or too large) are rejected in the browser before anything is sent. The same wording is reused if the server rejects a file the browser let through.
- A failed upload leaves the article untouched and offers a retry.
Improvements and fixes
- Uploaded images now render in the editor and the article body. The dev server proxy was missing a route for the endpoint that serves them.
- Clicking a row in the article list now opens the editor. The row-click handler was reading the wrong argument.
- The article details panel's copy URL control now copies the link and confirms with a toast.
- The user menu's dropdown trigger no longer misrenders when the sidebar is collapsed.
- The sidebar's collapsed or expanded state is remembered across reloads.
- Smaller styling and layout fixes in the article details panel, editor toolbar and new-article dialog.
Upgrading
- Replace the binary and restart.
Downloads
-
released this
2026-09-27 18:12:04 +02:00 | 116 commits to main since this releaseA ground-up rewrite of RenewCMS's backend architecture and frontend. The Go-template UI is replaced by a Vue 3 single-page app embedded in the same binary.
New
Vue 3 single-page app
- Built with Vite, Pinia and vue-router, and embedded into the single binary. There is no separate frontend deployment.
- The UI uses Nuxt UI (migrated from an initial PrimeVue base) with shared design tokens.
- New screens: login, registration, home and navigation, article list, article editor (ProseMirror-based) with a creation modal, image upload, and an account and integration screen.
- A typed API client and a session store handle 401, unverified 403, 429 and offline states the same way everywhere.
Domain-driven backend
internal/domain(entities, sentinel errors, repository interfaces) is isolated from persistence (GORM), mailer and API concerns, with a strict inward dependency rule.- Business logic moved out of handlers and the persistence layer into use cases: article rules, image validation, password and code hashing, not-found handling, and session invalidation on password reset.
- The composition root (
api/dependecyInjection.go) wires the database, repositories and use cases explicitly. Runtime settings are read once at startup. - The auth handler is split into dedicated registration, login, verification and password-reset handlers, each with per-client rate limiting on the public routes.
Improvements and fixes
- API responses no longer leak password hashes, verification or reset code hashes, or their expiry dates.
- Article read rules now apply the same way to the single-article and list endpoints: the public sees online articles only, verified users also see offline ones.
- Registration only opens when no verified account exists, and deleting a user frees its username and email for reuse.
- Rate limiting now keys off the real peer IP.
Under the hood
- Use cases are covered by a full in-memory fake test suite with clean gremlins mutation coverage.
- Vitest and Vue Test Utils run the frontend tests, and an automated visual-parity check compares the app to the design source boards.
Upgrading
- Deployment is unchanged: a single binary, systemd and a reverse proxy. Replace the binary and restart.
Downloads