Skip to main content
Democratia
Create account
Appearance

Releases

July 10, 2026

Two account roles. Administrators manage accounts and reach every poll — with two deliberate exceptions.

Accounts now have a role. Everything a member could do before, they still do; nothing about the ordinary account has changed.

Added

Two roles

  • Member — the ordinary account, and what registration always creates. Owns the polls it creates, takes part in the polls it is invited to.
  • Administrator — additionally administers the platform: reaches every poll regardless of ownership, and manages every account.

The role is stored on the account, defaults to member at the database level, and is not mass assignable. Adding role=admin to the registration or profile form achieves nothing — a test asserts it. The only paths to the role are the administration area and the seeder.

An administration area

At /admin, for administrators only:

  • An overview of platform counts: accounts, administrators, unconfirmed addresses, polls broken down by phase, participants, guests and ballots cast.
  • An account list with what each account owns and takes part in, filterable by role, confirmation status, and name or email address.
  • An account page from which an administrator can change the role, confirm the address on the holder's behalf, or delete the account.

Every role change, manual confirmation and deletion is written to the application log with the administrator who performed it, so the action stays attributable.

Seeing and managing every poll

  • The poll list gains an Everything on this platform scope for administrators. The scope name is re-checked against the policy inside the query, so passing it in a URL as a member does nothing.
  • Every poll management screen — settings, options, invitations, lifecycle, participant link — now works on any poll for an administrator. A banner says plainly when a poll is being viewed as an administrator rather than as its owner.
  • Administrators' own dashboards stay personal. A dashboard listing every poll on the platform would be noise, so "everything" lives in the poll list and the administration area.

A seeded administrator

Every installation is now seeded with dev@belodigital.com, password password on a fresh install.

Unlike the demonstration accounts, this one is seeded in every environment, because an installation with nobody to administer it cannot be administered. Two safeguards apply:

  • The password is written only when the account is created, so re-seeding an existing installation never resets a password somebody has changed.
  • DEMOCRATIA_ADMIN_PASSWORD overrides the default, and outside local and testing the seeder says out loud which password it used.

A favicon that matches the wordmark

The dot before the wordmark in the header is now the tab icon too. php artisan brand:icons generates favicon.svg, favicon.ico and apple-touch-icon.png from the --accent token in the stylesheet, so the two cannot drift — and a test fails if the committed files go stale. The SVG carries both palettes and follows the browser's light or dark chrome.

Two things administrators deliberately cannot do

Both look like oversights. Neither is.

They cannot see unpublished result totals. This is not a role restriction applied unevenly — it is the same rule that already refuses the poll's own owner. Result secrecy means nobody sees totals early, and three layers enforce it. Admitting administrators would change the promise from "nobody can see totals before publication" to "nobody except an administrator", which is a different product and therefore a decision for whoever runs the installation, not a default. Published results of any poll, including private ones, are visible to an administrator.

They cannot vote in a private poll they were not invited to. Administering a poll is not the same as being entitled to decide it. An administrator who could quietly add a ballot to any private poll would undermine every result on the platform. Invite them and they vote like anybody else.

Both are documented, and both are pinned by tests that will tell you exactly which promise you have altered if you change them.

Fixed

Every form built from x-form.field submitted nothing. The field component tried to hand its name, id and aria attributes to the control in its slot with ComponentSlot::withAttributes(). That method decorates the slot object; it does not reach a component rendered inside the slot. So 37 controls across nine files rendered with no name attribute at all — the browser sent nothing for them, the server saw empty input, and the form came back blank with "field is required" on everything.

Controls now read the field's name and help with @aware and build their own attributes, so the label's for, the control's id and the ids in aria-describedby are wired in one place and cannot drift apart. A control given its own name — a filter input, say — is left exactly as written.

The reason the suite missed it is worth recording: every form test posted an array straight to a route, so the browser's half of the contract was never exercised. FormRenderingTest now parses the rendered HTML of every form, asserts each control carries a name, checks that every label points at a control that exists, and submits using only the fields the form actually declares. Reintroducing the original bug fails 25 of those tests.

Changed

  • PollDomainException is now DomainRuleException. Exceptions are no longer poll-only, and the base class name said otherwise. Mechanical rename; the architecture test still enforces a single base.
  • Deleting an account is refused when it owns a poll or has taken part in one. The database already refused it — the RESTRICT foreign keys exist so a ballot is never destroyed as a side effect of removing a person — so this turns a database error into a clear explanation.
  • An administrator cannot demote or delete themselves, either of which would let one person lock the platform out of its own administration by accident.

Documentation

  • A new page, Roles and administration, covering both roles, what an administrator can and cannot do, and how to promote somebody.
  • Security and privacy updated for roles, and the installation notes for the administrator every installation starts with.

Test coverage

478 tests, up from 427. The new coverage includes the full administrator authorization matrix, that the role cannot be granted through any form, the self-demotion and self-deletion guards, deletion guarded by records held, the platform poll scope for both roles, and that unpublished totals stay unreachable for an administrator in every phase.