> ## Documentation Index
> Fetch the complete documentation index at: https://pyrelight.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Security

> Password storage, brute-force lockouts, 2FA, and the one setting that isn't a setting.

## Passwords never reach the log

The Minecraft server writes every command to the console **before** any plugin
can cancel it. That means `/login hunter2` normally lands in `latest.log` in the
clear, ready to be read by anyone with the file — or shipped off to whatever
log aggregator the host runs.

rLogin installs a log filter at startup that masks the password in any command
that carries one, and records the attempt without it.

<Warning>
  **There is no setting to turn this off**, and that is deliberate.

  An admin who could disable it would be able to build a file of their players'
  passwords. People reuse passwords across servers, and across things that
  aren't servers. It is not a knob anyone should have.
</Warning>

Stored passwords are **bcrypt** hashes, cost 10 by default. Raising
`security.password.bcrypt-cost` doubles the hashing time per `+1`, which is what
makes a stolen database expensive to crack — at the cost of slower logins on a
weak CPU.

## Account recovery

At registration, a player is shown a set of one-time codes. Losing a password
or an authenticator app otherwise ends at an administrator with database
access — and the usual fix, deleting the account, hands the name to whoever
registers it next, which makes the recovery path itself an attack if anyone
can talk staff into it.

```bash theme={null}
/recover <code> <new password>
```

```yaml theme={null}
security:
  recovery:
    enabled: true
    codes: 5
```

<Note>
  A code sets a new password **and clears 2FA**. The two things people lose are
  the two things a code has to replace: restoring only the password would leave
  them locked out by the very factor they came here about.
</Note>

Codes are stored as bcrypt hashes and never in plain text — a recovery code
readable from the database is a second password that skips the first. Each
works once, and the player is told how many are left. Using one also ends any
active session, since whoever held it is usually why the account needed
recovering.

<Warning>
  They are shown **once**, at registration. There is no command to see them
  again — that command would be the hole this feature exists to close.
</Warning>

## Registration limits

```yaml theme={null}
security:
  registration:
    max-per-ip: 3
    window-minutes: 60
```

Brute-force protection guards guessing a password that already exists. This
guards the other direction: creating accounts. Without it one address can
register as fast as it can send packets, claiming every name on the server and
growing the database without bound — none of which requires owning an account.

Set `max-per-ip: 0` to turn it off.

## Password rules

```yaml theme={null}
security:
  password:
    min-length: 5
    max-length: 30
    reject-common: true
```

A length rule on its own accepts `123456`, and accepts a player's own name —
which is public on a server. Both are refused. The bundled list is small on
purpose: it is not an attempt to enumerate bad passwords, just the handful at
the top of every breach corpus, where refusing costs one retry.

<Note>
  Because people reuse passwords across servers, a weak one accepted here was
  never only this server's problem.
</Note>

## Brute-force protection

```yaml theme={null}
security:
  bruteforce:
    enabled: true
    max-attempts: 5
    lockout-seconds: 60
    lockout-multiplier: 2.0
    max-lockout-seconds: 3600
```

After `max-attempts` wrong passwords the **address** is locked out, and each
further failure multiplies the lockout — 60s, then 120s, 240s, up to an hour.

<Note>
  The account is never locked. Locking accounts would let anyone who knows a
  name keep its owner out of their own server, just by failing logins on
  purpose. Locking the address puts the cost on whoever is actually guessing.
</Note>

## Two-factor authentication

Optional TOTP through any authenticator app — Google Authenticator, Aegis,
1Password, whatever the player already uses. They opt in themselves; there is no
way for an admin to impose it.

<Steps>
  <Step title="The player starts setup">
    ```bash theme={null}
    /2fa enable
    ```

    They are shown a secret to add to their app.
  </Step>

  <Step title="They confirm with a code">
    ```bash theme={null}
    /2fa confirm <code>
    ```

    Proves the app is set up correctly before anything is enforced — nobody gets
    locked out by a half-finished setup.
  </Step>

  <Step title="From then on">
    ```bash theme={null}
    /login <password> <code>
    ```
  </Step>
</Steps>

`/2fa disable` turns it off again.

```yaml theme={null}
security:
  totp:
    enabled: true
    issuer: rLogin
```

`issuer` is the name players see for this server inside their authenticator app
— worth changing to your server's name if you run more than one.

<Note>
  Accounts with 2FA are never given a
  ["remember me" session](/rlogin/features/sessions#accounts-with-2fa-are-excluded).
</Note>

## Premium name protection

```yaml theme={null}
premium:
  protect-premium-names: true
```

Refuses `/register` on a name that belongs to a real Minecraft account, so its
owner never loses the ability to auto-login with it. On by default.

## Login timeout

```yaml theme={null}
limbo:
  login-timeout-seconds: 60
```

A connection parked forever at the login prompt still holds a player slot —
which is all it takes to fill a server without owning a single account. Players
who never log in are disconnected. `0` disables it.

## What rLogin does not claim

<AccordionGroup>
  <Accordion title="A name lookup is not verification" icon="triangle-exclamation">
    Asking Mojang "is this name premium?" proves only that the name exists.
    Anyone can type it. rLogin never treats that answer as proof — it is used
    only to decide whether the cryptographic handshake is worth starting.
  </Accordion>

  <Accordion title="An IP is not an identity" icon="triangle-exclamation">
    "Remember me" is a convenience with a real trade-off, spelled out in
    [Sessions](/rlogin/features/sessions#what-it-trusts). It is 30 minutes by
    default for a reason.
  </Accordion>

  <Accordion title="rLogin cannot protect a server that trusts the wrong thing" icon="triangle-exclamation">
    If `login-servers.enforce` is off and your proxy routes players somewhere
    unprotected first, they arrive before rLogin can ask them anything. Leave it
    on unless you handle the routing yourself.
  </Accordion>
</AccordionGroup>
