Authentication and 2FA¶
There is no default credential. On first access PEART requires you to create the administrator, including enrolling the second factor, so an installation is never reachable with a shipped password.
Passwords¶
Passwords are hashed with Argon2id, using the parameters from OWASP's Password Storage Cheat Sheet,
and stored in the standard PHC string format that carries the parameters and a per-password random salt
with the hash. docs/security.md states the rule by name: never a SHA of any variant, never a shared
salt.
Two-factor authentication¶
The two surfaces treat the second factor differently, by design.
For internal accounts, TOTP (RFC 6238) is mandatory, and the requirement lives in the database schema, not only in code: an active internal account without TOTP enabled is refused by the database, so no CLI, migration or administrative script can create one. Internal TOTP secrets are encrypted at rest with the installation's master identity, which the internal surface already holds.
For portal (client) accounts, the second factor is optional and enabled by the user. A common deployment sits behind an edge that already enforces a second factor (for example Cloudflare Access), so in-app TOTP is a convenience for deployments without one, not the primary gate. Portal TOTP secrets are stored in cleartext, for the same architectural reason the database is: the portal never holds the master identity, and encrypting a secret the verifying surface cannot decrypt would make the second factor unrecoverable rather than safer. The compensating control is the same LUKS-on-host boundary that covers the database, and a client account is by definition lower value: it reads a delivered report, with no internal fields and no exploitation steps behind it.
Recovery and rate limiting¶
Recovery codes are stored only as hashes and invalidated on use. Verification is rate-limited per account. A lost internal device is recovered through the recovery codes or an administrator reset; because internal TOTP is mandatory, that reset re-enrolls the factor rather than dropping it.
Sessions¶
Sessions are shared in shape across both surfaces, with an inactivity timeout and an absolute timeout. The defaults are one hour of inactivity and twelve hours absolute; both are configurable within bounds, and the inactivity timeout cannot exceed a fixed maximum.