Emergency Account Recovery
A break-glass path for regaining access to an Athena server when every administrator is locked out or a password is lost. The emergency endpoints unlock an account, optionally reset its password, check a lockout, and ban an abusive IP — and they answer only when called from the server host itself.
These endpoints refuse any request that does not originate from the machine running
Athena (IPv4 or IPv6 loopback). A request from any other address is rejected with
403 Forbidden. There is no way to trigger emergency recovery across the
network — you must have a terminal on the server (SSH, console, or the container shell).
When to use it#
Normal account management — creating users, resetting a colleague's password, or clearing a lockout — is done in the console by an Admin (see User Accounts). Reach for emergency recovery only in the scenarios that lock everyone out of the console:
- The only admin account is hard-locked after too many failed sign-ins (see how lockouts work).
- The admin password was forgotten and no other admin can reset it.
- A single source IP is hammering the sign-in page and you want to block it immediately.
Because it bypasses the sign-in screen, the emergency path is deliberately unreachable except from the host — a operator with shell access to the server is treated as already trusted.
How lockouts work#
Athena protects sign-in with a layered set of counters. Understanding them tells you which emergency action you actually need.
| Protection | Trigger (default) | Effect |
|---|---|---|
| Progressive delay | Each failed sign-in | A growing pause before the next attempt is accepted — 1s, 2s, 4s, 8s, capped at 16s. |
| Temporary lockout | 5 failed sign-ins | The account is locked for 15 minutes, then clears itself automatically. |
| Hard lock | 30 failed sign-ins | The account will not clear on its own — it stays locked until an operator unlocks it (this is the case emergency recovery exists for). |
| Per-IP rate limit | 10 sign-in requests / minute from one IP | Further requests get 429 Too Many Requests, and the IP is auto-banned
for 30 minutes. |
These thresholds are configurable under the Security section of
appsettings.json — see Configuration for
MaxFailedLoginAttempts, LockoutDurationMinutes,
HardLockThreshold, and MaxRequestsPerMinutePerIp.
Lockout counters and IP bans are held in the server's memory, not on disk. Restarting the Athena server therefore releases all temporary lockouts, hard locks, and IP bans at once. A full restart is a heavy hammer, but it is a valid last resort if you cannot reach the server's loopback interface to call the endpoints below.
Unlock an account#
Clear a locked account (temporary or hard) so its owner can sign in again with their existing password. Run this from a shell on the server:
# Unlock the account "admin" — keeps the current password
curl -k -X POST https://localhost:8443/api/Emergency/unlock/admin
A successful call returns the standard response envelope:
{
"success": true,
"message": "Account unlocked successfully",
"data": {
"success": true,
"newPassword": null,
"message": "Account unlocked successfully"
}
}
Reset a forgotten password#
Add ?resetPassword=true to unlock and set a new, randomly generated
password. The server generates a strong 16-character password (mixed upper- and lower-case
letters, digits, and symbols) and returns it once in the response — copy it
immediately, because it is not stored anywhere in plain text and cannot be retrieved again.
# Unlock "admin" AND issue a new random password
curl -k -X POST "https://localhost:8443/api/Emergency/unlock/admin?resetPassword=true"
{
"success": true,
"message": "Account unlocked and password reset successfully",
"data": {
"success": true,
"newPassword": "7bQ!m2Xz…",
"message": "Account unlocked and password reset successfully"
}
}
Sign in with the generated password right away and set a memorable one of your own under
your profile. If the named user does not exist, the call returns
success: false with "User not found". If the account unlocks but the
password reset fails, the account is still unlocked and the existing password remains valid.
Check a lockout#
Inspect the current lockout state of an account before acting — useful to confirm whether a lockout is temporary (will self-clear) or a hard lock that needs an unlock:
curl -k https://localhost:8443/api/Emergency/status/admin
The data payload reports the account's lockout state:
| Field | Meaning |
|---|---|
isLocked | Whether the account is currently locked. |
requiresManualUnlock | true for a hard lock that will not clear on its own. |
lockoutEnd | When a temporary lockout ends (absent for a hard lock). |
failedAttempts | Consecutive failed sign-in count. |
delayMilliseconds | Current progressive-delay pause, in milliseconds. |
message | Human-readable summary of the lockout. |
Ban an IP address#
Immediately block a source IP from signing in. Pass the address and, optionally, a duration in minutes (default 60). While banned, that IP's sign-in requests are rejected before credentials are even checked.
# Ban 203.0.113.10 for the default 60 minutes
curl -k -X POST "https://localhost:8443/api/Emergency/ban-ip?ipAddress=203.0.113.10"
# …or for a custom window (here, 8 hours)
curl -k -X POST "https://localhost:8443/api/Emergency/ban-ip?ipAddress=203.0.113.10&durationMinutes=480"
The ban is held in memory and lifts automatically when its window expires (or on the next server restart). To lift a ban sooner without a restart, wait out the window — bans are not individually removable through this endpoint.
Endpoint summary#
| Method & path | Query | Purpose |
|---|---|---|
POST api/Emergency/unlock/{username} |
resetPassword (bool, default false) |
Clear a lockout; optionally reset the password and return a new one. |
GET api/Emergency/status/{username} |
— | Report the account's current lockout state. |
POST api/Emergency/ban-ip |
ipAddress (required), durationMinutes (default 60) |
Ban a source IP from signing in for a window. |
These endpoints are intentionally hidden from the built-in
OpenAPI explorer and carry no bearer token — their only
gate is the localhost check. Call them directly with curl from the server, as
shown above.
Every action is audited#
Emergency recovery is a privileged, break-glass action, so it is recorded in the tamper-evident
audit trail. An emergency unlock writes an EmergencyUnlock event and an IP ban
writes an EmergencyIpBan event — both at Warning severity, under
the SYSTEM actor, capturing the affected account or IP and whether a password was
reset. Review these in Audit & SIEM after any break-glass use so
the action is accounted for.