# Healthchecks

This repository contains the operational wrapper for the headless login healthcheck. The underlying smoke client lives in `m2dev-server-src`.

## What Exists

Source repository:

- `tests/login_smoke.cpp`
- binary target: `metin_login_smoke`

Runtime repository:

- `deploy/healthcheck/metin-login-healthcheck.sh`

Installed on the VPS:

- `/usr/local/sbin/metin-login-healthcheck`

## What The Headless Healthcheck Verifies

The installed wrapper supports two modes:

- `--mode ready`
- `--mode full`

The full mode performs two headless passes against the live server:

1. a select-screen create/delete pass
2. a full auth + channel + `ENTERGAME` + mall pass

Together they perform the real two-step Metin login flow without a GUI client and cover both character lifecycle and in-game mall open behavior:

1. Connect to the auth socket.
2. Complete the secure handshake.
3. Send login credentials.
4. Receive `AUTH_SUCCESS` and the login key.
5. Open a second connection to the channel socket.
6. Complete the secure handshake again.
7. Send `LOGIN2` with `login` + `login_key`.
8. Verify `EMPIRE`.
9. Verify `LOGIN_SUCCESS4`.
10. Create a temporary character on an empty account.
11. Delete that temporary character on the select screen using the account private code path.
12. Create a fresh temporary character again.
13. Select a character slot.
14. Send `ENTERGAME`.
15. Verify `MAIN_CHARACTER`, `PHASE_GAME`, `TIME`, and `CHANNEL`.
16. Send `/mall_password 000000` through the encrypted chat path.
17. Verify `MALL_OPEN`.

This is an end-to-end gameplay-path verification, not just a TCP port check.

## How The Wrapper Works

`metin-login-healthcheck.sh --mode full` does the following:

- creates two temporary accounts in MariaDB
- runs `metin_login_smoke` once in create/delete mode on the select screen
- runs `metin_login_smoke` again for the full auth + channel + `ENTERGAME` flow
- uses separate temporary accounts because the server enforces a per-account character-create cooldown
- verifies that the shared safebox/mall DB load bootstrap can open the mall with the default empty password
- deletes both temporary accounts and any temporary character rows on exit
- passes the configured client version expected by the server

`metin-login-healthcheck.sh --mode ready` is intentionally lighter:

- creates one temporary account in MariaDB
- runs one headless login flow through auth + channel + character create + select + `ENTERGAME`
- does not run the delete pass
- does not open the mall

This mode is the right readiness probe immediately after a service restart. It verifies that the server is login-ready without depending on the deeper post-login mall path.

It is intended for manual admin use on the VPS.

## Usage

On the VPS:

```bash
ssh mt2
/usr/local/sbin/metin-login-healthcheck
```

Readiness-only mode:

```bash
/usr/local/sbin/metin-login-healthcheck --mode ready
```

The smoke binary can also be run directly:

```bash
sudo -iu mt2.jakubkadlec.dev \
  /home/mt2.jakubkadlec.dev/metin/build/server-src/bin/metin_login_smoke \
  173.249.9.66 11000 11011 <login> <password>
```

Or with password passed through the environment:

```bash
sudo -iu mt2.jakubkadlec.dev env METIN_LOGIN_SMOKE_PASSWORD='<password>' \
  /home/mt2.jakubkadlec.dev/metin/build/server-src/bin/metin_login_smoke \
  173.249.9.66 11000 11011 <login> --password-env=METIN_LOGIN_SMOKE_PASSWORD
```

If you want the smoke client to create a temporary character when the account is empty:

```bash
sudo -iu mt2.jakubkadlec.dev env METIN_LOGIN_SMOKE_PASSWORD='<password>' \
  /home/mt2.jakubkadlec.dev/metin/build/server-src/bin/metin_login_smoke \
  173.249.9.66 11000 11011 <login> --password-env=METIN_LOGIN_SMOKE_PASSWORD \
  --create-character-name=smoketestchar \
  --client-version=1215955205 \
  --mall-password=000000
```

Useful direct flags:

- `--json`
  returns a machine-readable summary including timings and emitted events
- `--expect-auth-failure=STATUS`
  treats an auth failure such as `NOID` or `WRONGPWD` as a successful negative test
- `--expect-channel-failure=STATUS`
  treats a channel failure as a successful negative test
- `--delete-private-code=CODE`
  creates or reuses a character slot, sends `CHARACTER_DELETE`, and verifies `PLAYER_DELETE_SUCCESS`
- `--mall-password=PASSWORD`
  after `ENTERGAME`, opens the in-game mall via encrypted chat command and verifies `MALL_OPEN`

Operational CLI:

```bash
metinctl public-ready
metinctl healthcheck --mode full
metinctl healthcheck --mode ready
metinctl wait-ready
```

`metinctl public-ready` verifies that every enabled client-visible public channel unit is active and that its declared listener port is actually up.

`metinctl wait-ready` now first waits for the public runtime to be up and only then runs the lighter `ready` login probe. The deeper `full` mode remains available as an explicit admin healthcheck.

Example negative auth test:

```bash
sudo -iu mt2.jakubkadlec.dev env METIN_LOGIN_SMOKE_PASSWORD='wrongpass' \
  /home/mt2.jakubkadlec.dev/metin/build/server-src/bin/metin_login_smoke \
  173.249.9.66 11000 11011 someuser --password-env=METIN_LOGIN_SMOKE_PASSWORD \
  --expect-auth-failure=WRONGPWD --json
```

## Security Notes

This does not open a new public network surface. It is a local operational tool.

Current guardrails:

- no new listening port
- root-only installed wrapper (`/usr/local/sbin/metin-login-healthcheck`, mode `700`)
- temporary credentials
- cleanup trap removes the test account
- wrapper passes the password through environment instead of command-line plaintext
- secrets are not committed to git

Remaining trust boundary:

- anyone with effective root access can still inspect or run the check
- therefore this tool assumes root is already trusted
