Staff accounts
Staff in the admin lists everyone who can sign in, invites colleagues, and removes them. The admin was a single shared account until this existed: one password for a two-person shop, and no way to revoke access when someone left.
Invitations
Section titled “Invitations”Access is granted by a single-use invitation. Only a hash of the token is stored, exactly as a password would be, and the raw token exists only in the emailed link; it works once and expires after 72 hours. When SMTP is not configured, the link is returned to the inviting admin to pass on, so a self-hosted store without email can still add a colleague.
Removal
Section titled “Removal”Removing someone destroys their sessions immediately rather than waiting for a cookie to expire, which is most of the point. You cannot remove your own account, and you cannot remove the last owner; a store with no owner has nobody who can add one back.
Password reset
Section titled “Password reset”An administrator who forgets their password can reset it by email from
/admin/login: a single-use, hour-long token, and every session the account had is
destroyed on reset. When SMTP is not configured the link is logged rather than
sent, so a self-hosted store can still recover an account by reading the API’s log.
Registration, login and reset answer identically for a known and an unknown email.
role is recorded (owner or staff) but does not gate anything: every
administrator can do everything, and the UI says so. Gating it would multiply the
permission surface across every route and needs its own security-test matrix,
which is a separate decision. The column exists now so that decision is not also
a migration.
Passwords
Section titled “Passwords”argon2id hashes in the database, never in a file the server rewrites at runtime.