Skip to content

Let members change their own name, email and student number, with username migration - #119

Draft
AlaqmarG wants to merge 11 commits into
mainfrom
feat/self-service-rename
Draft

AlaqmarG wants to merge 11 commits into
mainfrom
feat/self-service-rename

Conversation

@AlaqmarG

@AlaqmarG AlaqmarG commented Sep 13, 2026

Copy link
Copy Markdown
Member

Description

  • Members edit their first name, last name, personal email and student number under Account details on their profile; name and email need the current password, and notices go to the old and new addresses
  • A name that changes the derived username runs a migration: new Keycloak user and Stalwart mailbox, every folder, message, flag and sieve script copied, cut over, verified, then the old pair deleted; approvers can rename anyone from People (temp password path)
  • The old address stays an alias on the new mailbox for 90 days with an auto-reply and subject tag, then a sweep moves it onto a sink that bounces; the local part goes into retired_usernames and is never reissued (sign-up allocation now checks it)
  • Approver-only preflight exercises every previously unused Stalwart call on a throwaway account pair; real renames refuse to start until it has passed on the current deploy
  • Identity changes panel on People (steps, verification, Resume, Abort); the profile polls progress and hands the session over to the new login
  • Review findings are addressed: the runner lease is a real lock and abort reaches a running rename, roles/step-down/password resets are held while one is in flight, the mail copy never lets one message stand for two, and names cannot derive into role addresses

Stalwart behaviour that only the preflight can prove (Email/copy, account destroy, alias re-attach, sieve notice) is unverified until it runs in production.

Checklist

  • npm run typecheck, npm run lint, npm run format:check and npm run build pass
  • Schema changes have a committed migration (npm run db:generate)
  • New env vars added to .env.example, .env.local.example, deploy/docker-compose.yml and komodo/deploy-context.mjs
  • Admin-only routes gated with requireAdmin / requireApprover
  • Tested on the branch's preview deploy, in light and dark themes
  • No secrets, internal hostnames or IPs committed

Testing Evidence

To follow: screenshots from the preview deploy (account details, rename confirmation, progress, Identity changes panel, preflight report).

@AlaqmarG AlaqmarG added feature New capability for the people using the site security Access control, data exposure or dependency risk area: mail Mailboxes, the mail client, sending or delivery area: admin The admin portal: analytics, users, events area: identity Keycloak, roles, sign-up and permissions labels Sep 13, 2026
… rename

The lease is taken and renewed with one conditional update, every save
checks it still holds, and the runner registry, password vault and
preflight result live on globalThis so the instrumentation and route
copies of the module share them. Abort queues a cancel the runner acts
on before the cut-over, and refuses once the cut-over has begun whatever
the step status says. Startup drops a lease from the container that went
away and a rescan picks up what nobody is watching.
A rename copies roles and the password as they were when it started, so
a change made meanwhile would land on the login that is about to be
deleted. Role items and step-down are refused for the duration, and both
password resets wait until the sign-up record points at the new login.

A name edit only counts as a rename when the new name derives a
different username from the old name, so a member allocated janesmith2
can fix their capitalisation without migrating. The email-change notice
now goes out only where Keycloak was actually updated.
A target message counts as the copy of an old one only when its
Message-ID, size and received date match and no other message has
claimed it, so a reused Message-ID cannot swallow a message. The final
delta and its strict check run while both sides are quiet; after the
cut-over verification only compares what the member cannot have changed,
and the old mailbox's app passwords go when it is frozen.

Retired local parts and their aliases are never reissued, a dotted alias
dropped by a name edit is retired the same way, and the sweep moves
expired addresses onto a sink that bounces instead of letting them fall
to the co-presidents catch-all. Names can no longer derive into role
addresses such as postmaster or hostmaster. Mail rules without a blob
fail the copy rather than being skipped, the retired-address notice
hands activation back to the member's own script, and the preflight
covers the calls the migration makes that it did not.
sync-from-prod checked that the prod schema exists, but not that each table
in its copy list does. This branch's own retired_usernames table doesn't
exist in prod yet, which crashed every preview build. Skip missing tables
instead of crashing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: admin The admin portal: analytics, users, events area: identity Keycloak, roles, sign-up and permissions area: mail Mailboxes, the mail client, sending or delivery feature New capability for the people using the site security Access control, data exposure or dependency risk

Development

Successfully merging this pull request may close these issues.

1 participant