Skip to main content

8 posts tagged with "fix"

View all tags

Uni-Verein 1.10.0 – Income, EÜR Export, Two-Factor Authentication and Demo Mode πŸ’ΆπŸ”

Β· 9 min read
RenΓ© Herrmann
Senior developer

Published on October 30, 2026

Uni-Verein 1.10.0 turns the contribution management into a complete income management with acknowledgements, stable booking numbers and payment date. The new financial analysis compares income and expenses and creates the EÜR export for tax purposes at the push of a button. On top come two-factor authentication, a demo mode and a whole series of security improvements. There are several things to note when updating, see the notes below.

Highlights​

  • πŸ’Ά Income (UV-65): The "Contributions" page is now the "Income" page. Besides membership fees there are freely configurable income categories such as donations or sponsoring, also without a bank connection
  • 🧾 Acknowledgements (UV-65): For every paid fee and every paid income an acknowledgement can be created as a PDF with a consecutive number
  • πŸ“‘ EÜR export (UV-65): The income and expense statement as an Excel file with tax checks, journal and fee proof. It needs a member with the task "Chief finance officer"
  • πŸ”’ Stable booking numbers (UV-68): E-YYYY-NNNN for income, A-YYYY-NNNN for receipts, plus an explicit payment date
  • πŸ“Š Financial analysis (UV-60): Income and expenses compared, replaces the receipt analytics
  • πŸ” Two-factor authentication (UV-67): Login with a code from an authenticator app and recovery codes
  • 🎬 Demo mode (UV-66): A demo instance with sample data that resets itself every night
  • πŸ›‘οΈ Hardening (UV-29, UV-64): Login token in an httpOnly cookie, mail encryption enforced, secured backup restore and more

New Features​

  • πŸ’Ά Income (UV-65): The "Membership fees" tab shows the SEPA fees, every further tab is a configurable income category (under "Manage income categories", e.g. "Donations"). Admins and Finance Managers can record, edit, mark as paid and delete income, Admins restore deleted entries. Key figures and a period filter (default: current year) are new. Paid fees are no longer deleted automatically after 14 days, the migration restores fees that were already removed this way
  • 🧾 Acknowledgements (UV-65): A PDF with a consecutive acknowledgement number (YYYY-NNNN) for every paid fee and every paid income. It is kept exactly as issued, afterwards the payment can no longer be changed. The acknowledgement is not a donation receipt under Β§ 10b EStG
  • πŸ“‘ EÜR export (UV-65): The EÜR export is available for Admins and Finance Managers on the "Financial analysis" page, for all years from 2020. The Excel file contains the sheets "EÜR", "Stammdaten", "Journal", "Beitragsnachweis" and "Hinweise". The tax checks use the limits of the exported year (€45,000 until 2025, €50,000 / €50,000 / €100,000 from 2026). Opening balances and the cash/bank reconciliation are empty cells to fill in by hand. For the treasurer to appear in the EÜR, there has to be a member with the task "Chief finance officer", see the prerequisites
  • 🏷️ Tax area (UV-65): Income and receipt categories have a tax area (non-profit sphere, asset management, purpose-related operation, commercial operation), the bank details an optional "Tax number"
  • πŸ”’ Booking numbers and payment date (UV-68): When booked as paid, every entry receives a booking number once, E-YYYY-NNNN for fees and income, A-YYYY-NNNN for receipts, which never changes and is never reused. The payment date (default: today) determines EÜR, financial analysis and income totals. Entries that are already paid keep their previous date, earlier figures do not change
  • πŸ“Š Financial analysis (UV-60): The financial analysis replaces the receipt analytics and compares income and expenses (overview, by month and category, category table). Only paid entries count, each in the month of its payment date, so the balance matches the EÜR. Open receipts no longer appear in the expenses until they are paid. Only Admins and Finance Managers can open it
  • πŸ” Two-factor authentication (UV-67): Under "Profile settings" β†’ "Security" every user sets up 2FA (TOTP) with a QR code and 10 recovery codes. With 2FA enabled the code prompt follows the password, wrong codes are blocked per address and per account (recovery codes keep working meanwhile). Admins can reset the 2FA of a user. The secret is stored encrypted, optionally the name in the app can be set with TwoFactor__Issuer. Not available in demo mode. If the only Admin loses device and recovery codes, there is no way back
  • πŸ”’ Sessions (UV-67): Logging out, changing the password and changing the 2FA end the sessions on all other devices
  • 🎬 Demo mode (UV-66): docker-compose-demo.yml builds nothing itself and needs no SQL dump. VERSION in the .env selects the published images, DEMO_DATA_PATH the data folder. With Demo__Enabled=true the backend wipes database and receipt files at every start and at 03:00 UTC every night and generates sample data relative to the current date. Mailpit catches e-mails at /mail/. GET /web-page-config returns demo, so the published frontend image shows the demo hint without its own build
  • πŸ‘₯ Finance Managers with more rights (UV-70): Finance Managers open the contribution plan administration and the income and receipt categories (the "Settings" menu shows them only these three entries) and create, edit and delete contribution plans and categories like Admins

Changes​

  • πŸͺ Login token in a cookie (UV-29): The token lives in an httpOnly cookie (uv_auth) instead of localStorage, so injected scripts can no longer read it. Cookie requests that change data carry the header X-UV-CSRF, Authorization: Bearer keeps working. GET /auth/me and POST /auth/logout are new. ?access_token= is no longer accepted on /backup, /receipts/export and /emailProgress
  • πŸ“§ Mail encryption (UV-64): With SSL enabled, an encrypted connection is enforced (TLS on ports 465/993, otherwise STARTTLS). Before, the mailbox password could be sent in cleartext when STARTTLS was missing. The test mail reports why something failed
  • πŸ”Œ API changes: GET /receipts/analytics is replaced by GET /finance/analytics. POST /contributions/{id} and POST /income/{id} expect { "paid", "paymentDate" } in the body instead of the query parameter paid, POST /receipts/{id}/pay and POST /income accept an optional paymentDate

Fixes and Security​

  • πŸ›‘οΈ Backup restore (UV-64): An uploaded SQL file can no longer run shell commands in the backend container (psql runs in restricted mode, PostgreSQL 17.6 or later is required, otherwise the restore is refused)
  • 🎬 Demo mode on the server (UV-64): Backup download and restore answer 403 DEMO_MODE_DISABLED in demo mode. Before, the demo mode only existed in the frontend
  • ⚠️ Demo wipe (UV-65): Wiping the database needs an explicit Demo__Enabled of true, 1, yes or on. Before, any other value (e.g. disabled) would have wiped a production database
  • πŸ“ Self-enrollment (UV-64): All text fields have a maximum length, POST /self-enrollment rejects bodies above 32 KB. Requests above a size limit (also receipt uploads) are answered with 413 instead of 500
  • πŸ’Ά Amounts (UV-64): Receipt and income amounts are limited to 1,000,000. Before, the finance analytics could no longer be loaded with receipts near the maximum value (HTTP 500). Amounts already stored above the limit are not changed
  • πŸ“„ CSV exports (UV-64): The receipts ZIP and the member export neutralise spreadsheet formulas (fields starting with =, +, - or @)
  • πŸ–ΌοΈ PDF preview (UV-64): Pages are rendered at a limited size, so a PDF with a huge page can no longer exhaust the browser. A tile that cannot be rendered shows a placeholder with the link to the original
  • πŸ”‘ Mail settings (UV-64): The SMTP/IMAP password must be entered again on every save, so the stored password cannot be redirected to another mail host
  • πŸ“Š Income totals (UV-65): Paid amounts are assigned to the period by payment date, paid income without a category is no longer left out
  • πŸ—‘οΈ Deleted fees (UV-65): A deleted fee can no longer be marked as paid

⚠️ Update Notes​

  • Always update with docker-compose-update.yml, as described in the README. The nginx.conf has to be updated together with the images (X-Forwarded-Proto), the update script takes care of that
  • All users have to log in once again after the update (new column Users.session_version and the switch to the cookie)
  • Assign tax areas: The migration sets all existing income and receipt categories to "Non-profit sphere". Assign the areas before you export the first EÜR, see Assign tax areas
  • For the EÜR: Create a member with the task "Chief finance officer" so the treasurer appears in the file
  • API clients: The token has to be sent as Authorization: Bearer (?access_token= is gone), POST /contributions/{id} and POST /income/{id} need the body { "paid", "paymentDate" }
  • Mail servers without encryption no longer work with SSL enabled
  • Open receipts appear in the financial analysis only once they are paid
  • Paid fees that used to be deleted automatically after 14 days are restored by the migration. In future paid fees are kept permanently

Why These Updates Matter​

With income, stable booking numbers and the payment date, Uni-Verein keeps the club's cash traceable: every payment has a fixed number and a date, donations can be documented with an acknowledgement, and the EÜR for the tax office is created from the same data as the analysis. Two-factor authentication protects accounts with access to member and bank data, and the hardening of tokens, mail encryption and backup restore closes several attack paths.

What's Next?​

Further feedback from practice is as always very welcome, feel free to use GitHub Issues. Keep checking back here!

Uni-Verein 1.9.1 – Membership Status, Better Contribution Management and More Security πŸ§ΎπŸ›‘οΈ

Β· 6 min read
RenΓ© Herrmann
Senior developer

Published on October 23, 2026

Uni-Verein 1.9.1 brings a membership status (member or supporting member), an improved contribution management with deletion of open contributions, and a considerably extended audit log. Several security improvements come on top. If you run your own reverse proxy in front of Uni-Verein, you have to set something up once when updating, see the update notes below.

Highlights​

  • ⭐ Membership status (UV-58): Every member is a member or a supporting member, with a column and filter in the member list and the contribution management
  • πŸ—‘οΈ Delete open contributions (UV-57): Admins and financial managers can delete open contributions, e.g. for returned direct debits. Admins can restore them
  • 🚫 No more billing after leaving (UV-63): Deleted members and members whose exit date has passed are no longer billed
  • πŸ“œ Extended audit log: Users, creditor data, contribution rates, contributions, mail settings, backup restore and CSV import are logged
  • πŸ›‘οΈ Safer login: The login lockout no longer affects all users of an account, and deleted or changed users lose access immediately

New Features​

  • ⭐ Membership status (UV-58): Members now have a membership status, adjustable in the form and in the CSV import as the column "Membership status" (MEMBER or SUPPORTING_MEMBER, empty = member). The member list and the contribution management get a column and a filter for it. The contribution management additionally shows the member number
  • πŸ—‘οΈ Delete open contributions (UV-57): Next to "Payment received" there is a trash icon that removes an open contribution after a confirmation. Contributions are soft-deleted and recorded in the audit log, admins can view them via "Show deleted" and restore them
  • πŸ“œ Audit log for all important actions: In addition to members, changes to users, creditor data, contribution rates, paid/open contributions, SMTP/mail settings, restoring a backup and the CSV import of members are logged. Passwords, IBAN/BIC and imported member data are not stored

Bug Fixes​

  • 🚫 Billing (UV-63): Deleted members and members whose exit date has passed are no longer considered when generating contributions and in the SEPA export. If the exit date lies in the future, the member is still billed until that date
  • πŸ‘₯ Member category filter (UV-61): When the member category filter is set back to "All", the list shows all members again
  • πŸ•΅οΈ Audit log (UV-10): Entries of deleted users are shown as "Deleted user" instead of being hidden
  • πŸ“± App icon on Android (UV-55): The web manifest has the correct content type, and a monochrome icon is included for the themed icons of Android 16. The app icons are RGBA PNGs, the iOS icon is opaque
  • 🌐 English texts of the contribution management: Because of a wrong translation key the English texts were not shown
  • πŸ“§ Update notice by e-mail: The "new version available" mail was sent to the admins every hour when one of them had no e-mail address. Such admins are skipped now
  • πŸ’Ά SEPA export: The IBAN and creditor ID checks only accept the digits 0-9, so invalid IBANs can no longer break the pain.008 file

Security​

  • πŸ” Login lockout per user name and address: The lockout is no longer counted per account but per user name and visitor address. An attacker can therefore no longer lock known accounts like Admin out permanently for everybody. IPv6 counts per /64 network, unknown user names are locked like known ones. The state is kept in memory and reset by a backend restart. More under User Account & Security
  • 🎫 Tokens are checked on every request: The login is checked against the current user and role on every request, deleted or changed users lose access immediately instead of after up to 8 hours
  • πŸ—ƒοΈ Deleted members: Only admins can still read or change deleted members, a second deletion no longer overwrites the original deletion time
  • πŸ“ Self-enrollment: The date of the SEPA consent is set by the server, sepaConsent is no longer accepted by POST /self-enrollment. HTML in names is encoded in system e-mails and broadcast mail placeholders, name fields are limited to 100 characters
  • 🧱 Proxy trust (nginx): X-Forwarded-For is no longer accepted from the whole range 172.16.0.0/12, which allowed bypassing rate limits and the login lockout and forging the IP of self-enrollments. Only the Caddy from docker-compose-tls.yml is trusted (fixed address 172.31.250.2 in the network edge)

Logging​

  • πŸͺ΅ Important actions are logged: Logins, user and member management, self-enrollment, CSV import/export, contributions, SEPA exports, receipts, mail, calendar, backups, settings and background jobs end up in the backend log with IDs and the ID of the acting user, plus failed token validations, forbidden access and rate limit hits. Personal data is no longer logged
  • πŸ“Š Every request once with status and duration: /health and the duplicate REQUEST: line that leaked the calendar feed token are gone. Failed backups and restores, unhandled exceptions (with stack trace), startup information and the hourly update check are logged properly

⚠️ Update Notes​

  • Always update with docker-compose-update.yml, as described in the README
  • A new database migration is included (AddMembershipStatusToMembers), it runs automatically at start. Existing members get the status "Member"
  • Option B only (your own reverse proxy): Next to the compose file, create the folder trusted-proxies and put the address of your proxy into a .conf file in it, e.g. set_real_ip_from 192.168.1.20;. Your proxy has to overwrite X-Forwarded-For with the visitor address (nginx: proxy_set_header X-Forwarded-For $remote_addr;). Without this step all visitors share one rate limit and one login lockout. The instructions are under Get Started, and the folder is not touched by updates. Option A (Caddy) needs nothing

Why These Updates Matter​

The membership status makes supporting members recognizable at a glance in the list and the contribution management, and deleting open contributions takes away the pressure of having to work around wrong or returned demands. Not billing members who have left prevents wrong bookings. The extended audit log shows traceably who changed what without storing sensitive data, and the security improvements make sure that the login lockout and rate limits really only affect attackers and not your own board.

What's Next?​

Further feedback from practice is as always very welcome, feel free to use GitHub Issues. Keep checking back here!

Uni-Verein 1.9.0 – The Club Calendar Has Arrived πŸ“…πŸŽ‰

Β· 4 min read
RenΓ© Herrmann
Senior developer

Published on October 16, 2026

With Uni-Verein 1.9.0, your club gets its own calendar: plan events, invite members, collect replies, create recurring events and get reminded by bell and e-mail. If you like, subscribe to the club calendar in Apple Calendar, Google Calendar or Outlook, or publish selected events in a public calendar.

Highlights​

  • πŸ“… Club calendar for all roles: Every logged-in user can create events with a location, online link, description and colored category
  • πŸ‘₯ Invitations and replies: Other users can be invited as attendees or co-editors and answer with yes, maybe or no
  • πŸ” Recurring events and reminders: Repeat daily, weekly, monthly or yearly, cancel single events, reminder from "at the start" up to a week before
  • πŸ”” Notifications by bell and e-mail: Invitations, changes, deletions, replies and reminders, e-mails with an .ics invitation
  • 🌍 Calendar subscription and public calendar: Personal subscription link for Apple, Google and Outlook as well as a public calendar page without login

New Features​

  • πŸ“… New calendar (UV-6): The calendar is available in the sidebar under "Calendar", with month, week and list views. Events can be timed or all-day and optionally have a location with address (including a map link), an online link and a category. Admins create the categories under "Manage calendar categories". On smartphones "Calendar" replaces the "Analytics" tab in the bottom navigation, the receipt analytics remain reachable through the menu. Everything about it is in the calendar guide
  • πŸ‘οΈ Visibility: An event is visible to the whole club, only to invited users, or privately only to the owner, privately not even to admins
  • 🀝 Collaboration: Invited people are attendees or co-editors. Co-editors change the event together with the owner. If someone else saved in the meantime, the change is rejected with a hint to reload instead of silently overwriting anything
  • πŸ” Recurrences: Events repeat daily, weekly, monthly or yearly. The time stays the same across daylight saving time changes, and single events of a series can be cancelled
  • ⏰ Reminders: From "at the start" up to a week before, by bell and e-mail to everyone subscribed to the event
  • πŸ”” Notifications: The bell is now visible for every role. Notifications can be deleted one by one, already read ones at once with "Delete read". Every kind of calendar e-mail can be turned off in the profile settings
  • πŸ“² Personal calendar feed: Every user can subscribe to their calendar in Apple Calendar, Google Calendar or Outlook through a secret webcal link, which can be revoked at any time
  • 🌍 Public calendar: Admins can enable a public calendar that shows single events or whole categories without participants or other personal data at /calendar/public and as a webcal feed, optionally only through a secret link. More under Calendar subscription & public calendar
  • πŸ—‚οΈ Hiding past events: The list view of the current month hides past events behind "Show past events", in the club calendar and in the public calendar

Bug Fixes​

  • 🧾 Description in the receipt form (UV-53): The description field is now multi-line and uses the full width, so longer descriptions fit in the receipt management
  • πŸ“ Self-enrollment title (UV-54): The public registration form of the self-enrollment now shows a proper title (Join the club <club name> or Join the club) and a subtitle addressed to prospective members. Previously it showed the title and text of the admin's "create member" dialog
  • πŸ‘€ Admins can edit themselves again (UV-62): Admins can change the user name, e-mail and password of their own account again. Only changing their own role is still rejected

⚠️ Update Notes​

  • Always update with docker-compose-update.yml, as described in the README
  • Four new database migrations are included (AddCalendar, AddCalendarCollaboration, AddCalendarRecurrenceAndReminders, AddCalendarFeedsAndPublicCalendar), which run automatically at start. No manual steps are required
  • New dependencies are Ical.Net (backend) and FullCalendar (frontend), both MIT licensed
  • For calendar e-mails to be sent, the mail settings have to be set up. Notifications in the bell work without them

Why These Updates Matter​

In many clubs, events and replies used to live in chat groups and spreadsheets. With the calendar they are where members, contributions and receipts already are. Those who are invited get notified, those who co-plan do not overwrite others' changes, and the events land in the private calendar through a subscription. The public calendar helps to show events to the outside without revealing member data.

What's Next?​

We are looking forward to your feedback on the calendar, feel free to use GitHub Issues. Keep checking back here!

Uni-Verein 1.8.4 – No More Blank Screens, Mail Works Again on iPhone πŸ“±πŸ›Ÿ

Β· 2 min read
RenΓ© Herrmann
Senior developer

Published on October 3, 2026

On the same day as 1.8.3 comes Uni-Verein 1.8.4, a small bug-fix release: sending mail works on the iPhone again, and if a page ever fails to load, the app stays usable from now on.

Highlights​

  • πŸ“± Mail works on the iPhone again: Opening "Mail" no longer shows a blank white screen
  • πŸ›Ÿ No more blank screens after errors: If a page fails to load, you now see an error message with a reload button. The navigation stays usable, so you can switch to another page
  • πŸ”„ Smoother updates: If the app is open while a new version is deployed, it reloads once on its own instead of failing to open a page

Fixes​

  • πŸ“± Mail on iPhone (UV-51): Opening "Mail" on an iPhone no longer shows a blank white screen. The mail editor was accessed after it had already been closed, and the resulting error took down the whole app
  • πŸ›Ÿ Error message instead of a blank screen: If a page runs into an error, an error message ("Hier ist etwas schiefgelaufen") with the buttons "Erneut versuchen" and "Neu laden" now appears in the content area. The sidebar and the mobile bottom navigation keep working, and switching to another page clears the error
  • πŸ”„ Missing page files after an update: If the app was still open from the previous version and a page file of that version no longer exists, the app now reloads once and loads the current version. If the file is still missing after that, the error message appears instead of reloading again and again

⚠️ Upgrade Notes​

  • Always update with docker-compose-update.yml, as described in the README
  • No database migration is needed
  • No changes to your settings or the API are needed

Why These Updates Matter​

A blank screen is the worst feedback for users: you don't know what happened or what to do next. With the error message and the navigation staying usable, everyone can carry on even after an unexpected error, and the automatic reload after updates prevents many such cases in the first place.

What's Next?​

Real-world feedback is, as always, very welcome via GitHub Issues. Check back here again soon!

Uni-Verein 1.8.3 – SEPA Export Ready for November 2026 πŸ¦βœ…

Β· 4 min read
RenΓ© Herrmann
Senior developer

Published on October 3, 2026

Uni-Verein 1.8.3 prepares the SEPA export for the changes that apply from November 15, 2026: the direct debit file (pain.008.001.08) follows the EPC rules for SDD Core 2025 and uses structured addresses. Checks are also stricter, so no file is produced that the bank would reject, and the mobile view has been tidied up.

Highlights​

  • 🏦 SEPA export ready for November 2026: The direct debit file follows the EPC rules for SDD Core 2025 and the new structured address rules that apply from 15 November 2026
  • βœ… More reliable SEPA files: IBANs and the creditor identifier are checked including their check digits, text is converted to the SEPA character set (e.g. Γ€ β†’ ae), and every transaction gets its own unique ID
  • πŸ’³ Members without BIC are no longer skipped: They are now exported as NOTPROVIDED, as SEPA allows
  • πŸ“± Better mobile view: The icons in the bottom navigation are aligned again, and the user management no longer runs off the screen

Fixes​

  • 🏦 SEPA export follows current rules (UV-50): The export (pain.008.001.08) follows the EPC rules for SDD Core 2025 and the address rules from 15 November 2026:
    • Addresses are fully structured (street, postal code, town, country) instead of a single free-text address line
    • Text uses the SEPA character set (e.g. Γ€ β†’ ae)
    • Every transaction gets a unique ID, so several open contributions of the same member no longer collide
    • Members without a BIC are exported as NOTPROVIDED instead of being skipped
    • The club's BIC, the IBANs, and the creditor identifier are checked including their check digits. Contributions with an invalid IBAN are left out of the file
    • An export without collectible contributions shows a message instead of producing an empty file
    • Deleted exports no longer appear in the export history
  • πŸ“± Mobile navigation (UV-48): The icons in the mobile bottom navigation are aligned again. Inactive tabs hid their label only visually, so "SEPA Export" wrapped to two lines and pushed the bank icon up. All tabs now show a short, single-line label (e.g. "SEPA", "Mail", "Auswertung") and the active tab no longer shifts
  • πŸ“± Mobile user management (UV-49): The user management no longer overflows the screen on mobile. The long "Benachrichtigungseinstellungen" tab label had widened the whole page. It is now shortened to "Benachrichtigungen", the tabs scroll if space runs out, and the tab indicator stays inside the rounded corners

Improvements​

  • 🐳 The frontend Docker image is built with Node 24 instead of Node 20, which has been end-of-life since April 2026. The runtime image (nginx) is unchanged
  • πŸ“¦ Updated dependencies: MailKit 4.18.1 and the Tiptap mail editor 3.31.4, plus several development tools

⚠️ Upgrade Notes​

  • Always update with docker-compose-update.yml, as described in the README
  • No database migration is needed
  • Check your SEPA settings before the next export: The club's street and postal code are now written as a structured address, so make sure they are filled in. The creditor identifier is now checked including its check digits, so a mistyped one stops the export with an error message instead of producing a file the bank would reject
  • Members without a BIC are now included in the export. Previously they were silently left out. Check the export before uploading it to your bank if you relied on that
  • If there are no collectible contributions, you now get a message instead of an empty file

Why These Updates Matter​

From November 15, 2026, new rules for structured addresses apply to SEPA files. Banks reject files that don't comply, and that happens exactly when contributions are due to be collected. With the stricter checks, mistakes now surface directly in the export instead of after the bank has rejected the file. The fixes to the mobile view also make using the app on a phone pleasant again.

What's Next?​

Real-world feedback is, as always, very welcome via GitHub Issues. Check back here again soon!

Uni-Verein 1.8.2 – Safer Mails, Masked IBANs, and Club Logo πŸ›‘οΈβœ‰οΈ

Β· 4 min read
RenΓ© Herrmann
Senior developer

Published on September 30, 2026

Uni-Verein 1.8.2 continues the security work from 1.8.1: mail bodies are sanitized on the server, IBANs are masked for regular users, and changing your password now requires the current one. On top of that come a dedicated sender name for mails and the club logo in the sidebar.

Highlights​

  • πŸ›‘οΈ Safer mails: Mail bodies are sanitized on the server. Only the formatting the mail editor can produce is kept, scripts, event handlers, javascript: links, forms, and iframes are stripped
  • πŸ”’ IBANs masked for regular users: Only ADMIN and FINANCIAL_MANAGER see full IBANs, the USER role sees them masked except for the last 6 characters
  • πŸ”‘ Password change requires the current password: Wrong attempts count towards the login lockout
  • βœ‰οΈ Sender name for mails: New "Sender name" field in the mail settings, so mails can show up as e.g. Vorstand <vorstand@verein.de>
  • πŸ–ΌοΈ Club logo in the sidebar: The sidebar now shows the logo from the general settings

New Features​

  • βœ‰οΈ Sender name (UV-47): The mail settings have a new optional "Sender name" field that recipients see as the sender. Previously the "Sender email (From)" value was used as both name and address, so entering a name there made sending mails fail. The sender email field now only accepts a valid address
  • πŸ–ΌοΈ Club logo in the sidebar (UV-24): The logo from the general settings appears in the top-left corner of the sidebar, both expanded and collapsed. The first letter of the club name is still used when no logo is uploaded

Security​

  • πŸ›‘οΈ Sanitized mail bodies (UV-17): The body of mails sent via /mail/send is sanitized server-side with HtmlSanitizer. Text, headings, lists, links, images, color, and alignment are kept. Scripts, event handlers, javascript: links, forms, and iframes are removed, and data: URLs are only allowed for inline images (no SVG). Markdown release notes in firmware update mails also no longer render raw HTML
  • πŸ”’ Masked IBANs (UV-26): Full IBANs of members are only returned to ADMIN and FINANCIAL_MANAGER. The USER role gets them masked except for the last 6 characters (member list, member create/update responses, and the "Pending members" review queue), so they can still check an IBAN and replace it with a new one. A masked IBAN sent back to the API is treated as unchanged and never stored
  • πŸ”‘ Current password required (UV-30): Changing your own password now requires the current password, both in the profile settings and when an admin edits their own account in the user management. Wrong attempts count towards the login lockout. Admins can still reset other users' passwords without it
  • 🧰 The transitive build dependency serialize-javascript was bumped to 7.1.2 to fix GHSA-gfhx-hw2g-v5hg (XSS via unescaped </script> in serialized function bodies, severity low)

⚠️ Upgrade Notes​

  • Always update with docker-compose-update.yml, as described in the README
  • The database migration (AddMailFromName) runs automatically on startup. Existing mail settings keep working, the new sender name is simply empty until you set it
  • If you entered a name in "Sender email (From)" as a workaround, move it to the new "Sender name" field. The sender email field now only accepts a valid address

Why These Updates Matter​

Email is a sensitive channel: anyone sending content from the editor should be able to rely on no active code travelling along. Masked IBANs make sure that bank details are only fully visible to those who really need them, and asking for the current password protects accounts even when someone briefly sits at an unlocked computer. A sender name and the logo also make the club easier to recognize in the inbox and in the interface.

What's Next?​

Real-world feedback is, as always, very welcome via GitHub Issues. Check back here again soon!

Uni-Verein 1.8.1 – HTTPS Included and Comprehensive Security Hardening πŸ”πŸ›‘οΈ

Β· 5 min read
RenΓ© Herrmann
Senior developer

Published on September 26, 2026

Uni-Verein 1.8.1 is a security release: the installation gets HTTPS out of the box, the login becomes much more resistant to attacks, uploads are checked more strictly, and all containers now run without root privileges.

Highlights​

  • πŸ” HTTPS out of the box: A new docker-compose-tls.yml plus Caddyfile puts Caddy in front of the stack and automatically fetches a Let's Encrypt certificate. This is now the recommended installation path
  • πŸ›‘οΈ Hardened login: Per-IP rate limiting, no more hints about whether a username exists, and lockouts last at most 15 minutes instead of up to 24 hours
  • πŸ”‘ No more default admin password in use: Logging in with admin123 now forces you to set a new password first
  • πŸ“¦ Safer uploads and containers: Receipt files are checked by their actual content, upload sizes are capped, and every container runs as a non-root user

New Features​

  • πŸ” HTTPS with Let's Encrypt (UV-27): docker-compose-tls.yml and Caddyfile set up automatic HTTPS (requires a DNS record plus ports 80 and 443). The self-update flow detects and supports both setups and also updates the Caddyfile. nginx recovers the real client IP behind Caddy, so rate limiting and audit logs keep working
  • 🚦 Login rate limiter (UV-31): /auth/login is limited per IP address (default 20 requests per window, configurable via RateLimiting:AuthLogin:PermitLimit)
  • πŸ”‘ Forced password change (UV-25): Logging in with the default password admin123 opens a dialog that can't be dismissed until a new password is set
  • πŸ” Automated dependency checks (UV-42): npm audit --audit-level=high runs in CI, plus Dependabot (one bundled weekly PR, no major updates)

Improvements​

  • πŸ”’ Authentication by default (UV-41): Endpoints now require authentication by default. Intentionally public ones (login, self-enrollment, public page configuration, /health) are explicitly allowed
  • ⏱️ Fairer lockouts (UV-45): Failed login attempts expire after 15 minutes, and the lockout is capped at 15 minutes (previously up to 24 hours)
  • πŸ‘€ Admin protection (UV-25): The default admin account is only created when no users exist at all. Admins can no longer delete their own account or change their own role
  • 🧾 Uploads checked by content (UV-32): Receipt files are validated by their magic bytes (PNG, JPEG, GIF, WebP, PDF) instead of the type reported by the browser. SVG and unknown formats are rejected. Downloads send X-Content-Type-Options: nosniff and Content-Disposition: attachment
  • πŸ“ Upload limits (UV-43): The global request limit drops from 10 GB to 100 MB, only the backup restore keeps 10 GB. Receipt files and the CSV import of members are capped at 10 MB per file, and oversized PDFs are rejected right in the browser
  • 🧱 Security headers (UV-33): Both nginx configs send CSP, X-Frame-Options, nosniff, Referrer-Policy, and Permissions-Policy
  • 🌐 CORS (UV-28): Allowed origins are configured via Cors:AllowedOrigins (empty in production) instead of allowing any localhost origin
  • πŸ”— Safe confirmation links (UV-35): Self-enrollment links use a stored PublicBaseUrl instead of the request's Host header, preventing phishing links via forged hosts
  • πŸ“¦ Containers without root (UV-37): All containers run as non-root users, with cap_drop: [ALL] and no-new-privileges where possible
  • πŸ—οΈ Secrets (UV-38, UV-39): The init script generates random database passwords and only prints the names of the .env keys, not their values. .env.example ships placeholders only instead of real keys
  • 🧰 Dependencies (UV-40): Serilog.Sinks.File is pinned to the stable 7.0.0, the unused BCrypt.Net-Next was removed, plus minor/patch updates for backend and frontend

Fixes​

  • πŸ–₯️ Blank screen after login (UV-18): After an update, stale lazily-loaded parts of the app could cause an empty screen. The app now reloads automatically when a new service worker takes over
  • πŸ”” Firmware hint (UV-46): The firmware-update hint now compares real version numbers and only shows when a newer version actually exists
  • πŸ•΅οΈ Username enumeration (UV-44): Login response timing no longer reveals whether a username exists. Raw usernames are no longer written to the log
  • 🌍 Forged IP headers (UV-34): X-Real-IP is only trusted from private/internal networks, so IP-based rate limits can no longer be bypassed with a spoofed header

⚠️ Upgrade Notes​

  • Always update with docker-compose-update.yml, as described in the README. Proxy and frontend now listen on port 8080 inside the container (the host port stays 80), so nginx.conf and the compose file have to be replaced together. The update script does this for you
  • logs/ and receipts/ must be writable by the non-root backend (UID 1654). The update script handles this via docker-compose-ini.yml. On a manual setup, run chown -R 1654:1654 logs receipts
  • Self-enrollment users: If self-enrollment was already enabled before this update, an admin has to open the general settings and save them once. This stores the public URL used in confirmation links. Until then, new submissions fail
  • The database migration (AddLastFailedLoginAttemptAndPublicBaseUrl) runs automatically on startup

Why These Updates Matter​

A club management system holds names, addresses, and bank details, so it should be run as securely as possible. With HTTPS out of the box, a more robust login, and stricter checks for uploads and network access, 1.8.1 closes a whole series of possible attack surfaces at once. Running the containers without root privileges also limits the damage should something ever go wrong.

What's Next?​

Real-world feedback is, as always, very welcome via GitHub Issues. Check back here again soon!

Uni-Verein 1.8.0 – Self-Enrollment Ready for Permanent Use πŸ“βœ…

Β· 3 min read
RenΓ© Herrmann
Senior developer

Published on September 20, 2026

Uni-Verein 1.8.0 makes self-enrollment originally built for short-term use such as a trade fair booth - ready for permanent operation: registrations now only create a member after the email address is confirmed and a logged-in person has reviewed them, instead of immediately. On top of that comes a new opt-in notification for new registrations and a security fix for already-paid receipts.

Highlights​

  • πŸ“ Hardened self-enrollment: Email confirmation (48h) plus a review queue instead of creating the member directly
  • πŸ”” Opt-in notification: Any logged-in user can get an email whenever a new registration comes in
  • πŸ”’ Receipt fix: A regular user can no longer delete a receipt that's already been marked as paid

New Features​

  • πŸ“ Self-enrollment: The public form no longer creates a member directly, it first requires confirming the submitted email address via a link valid for 48 hours. Only then does the registration land in the "Pending members" tab of member management, visible to every logged-in role, where it can be approved or rejected
  • ✍️ Motivation instead of category: Applicants instead provide a short motivation plus required IBAN and BIC, a member category is only assigned by the reviewer when approving
  • πŸ›‘οΈ Hardened for everyday use: Per-IP rate limiting, stricter email validation, a requirement that mail settings are configured before self-enrollment can be enabled, and a mobile-friendly view for the review queue
  • πŸ”” New-enrollment notification: Any logged-in user, regardless of role, can opt in via a new toggle in the notification settings to receive an email for every newly confirmed registration, off by default

Fixes​

  • πŸ”’ Deleting a paid receipt: A regular user could still delete their own receipt even after an Admin or Finance Manager had marked it as paid, because the delete check only looked at receipt ownership, not the paid status. Deleting a paid receipt as a regular user now returns an error, and the delete button is hidden for it. Admins and Finance Managers can still delete paid receipts

Why These Updates Matter​

The original self-enrollment feature was built for short-term use, such as quickly signing up new members at an event. Running it permanently on the club website needs more safeguards: email confirmation prevents registrations with incorrect or someone else's address, the review queue keeps the board in control of every new admission, and the opt-in notification means nobody has to manually check whether something new is waiting.

What's Next?​

Real-world feedback is, as always, very welcome via GitHub Issues. Check back here again soon!