# Running the Client

The quicproquo CLI client provides subcommands for connectivity testing, identity registration, KeyPackage exchange, and persistent group messaging. All commands connect to the server over QUIC + TLS 1.3 and issue Cap'n Proto RPC calls against the `NodeService` endpoint.

---

## Global flags

These flags apply to every subcommand:

| Flag | Env var | Default | Purpose |
|---|---|---|---|
| `--ca-cert` | `QPQ_CA_CERT` | `data/server-cert.der` | Path to the server's TLS certificate (DER format). The client uses this to verify the server's identity during the TLS handshake. |
| `--server-name` | `QPQ_SERVER_NAME` | `localhost` | Expected TLS server name. Must match a SAN in the server's certificate. |

Most subcommands also accept `--server` (default `127.0.0.1:7000`) to specify the server address.

---

## Connectivity

### `ping`

Send a health probe to the server and print the round-trip time.

```bash
cargo run -p quicproquo-client -- ping
```

```bash
cargo run -p quicproquo-client -- ping --server 192.168.1.10:7000
```

**Output:**
```
health=ok  rtt=3ms
```

This exercises the full QUIC + TLS 1.3 connection setup plus a single Cap'n Proto `health()` RPC call. Useful for verifying that the server is reachable and TLS verification succeeds.

---

## Ephemeral identity commands

These commands generate a fresh identity keypair in memory each time they run. The identity is not persisted and is discarded when the process exits. They are useful for quick tests and for the automated `demo-group` scenario.

### `register`

Generate a fresh Ed25519 identity, create an MLS KeyPackage, and upload it to the Authentication Service.

```bash
cargo run -p quicproquo-client -- register
```

**Output:**
```
identity_key : a1b2c3d4e5f6...  (64 hex chars = 32 bytes)
fingerprint  : 9f8e7d6c5b4a...  (SHA-256 of the KeyPackage)
KeyPackage uploaded successfully.
```

Share the `identity_key` value with peers who want to add you to a group. They will pass it to `fetch-key` or `invite --peer-key`.

### `fetch-key <identity_key>`

Fetch a peer's KeyPackage from the Authentication Service by their Ed25519 public key.

```bash
cargo run -p quicproquo-client -- fetch-key a1b2c3d4e5f6...
```

The `identity_key` argument must be exactly 64 lowercase hex characters (32 bytes).

**Output (success):**
```
fingerprint  : 9f8e7d6c5b4a...
package_len  : 742 bytes
KeyPackage fetched successfully.
```

**Output (no KeyPackage available):**
```
No KeyPackage available for this identity.
```

KeyPackages are single-use: fetching a KeyPackage atomically removes it from the server. The server may also enforce a TTL (e.g. 24 hours) on stored KeyPackages. If the peer needs to be added to another group, or their KeyPackage expired, they must upload a new one (see `refresh-keypackage` below).

### `demo-group`

Run a complete Alice-and-Bob MLS round-trip against a live server. Both identities are created in-process; both communicate through the server's AS and DS.

```bash
cargo run -p quicproquo-client -- demo-group --server 127.0.0.1:7000
```

**Output:**
```
Alice -> Bob plaintext: hello bob
Bob -> Alice plaintext: hello alice
demo-group complete
```

This is the fastest way to verify that the entire stack (QUIC + TLS + Cap'n Proto RPC + MLS group operations + DS relay) is working end to end. For a detailed breakdown of what happens during `demo-group`, see the [Demo Walkthrough](demo-walkthrough.md).

---

## Persistent group commands

These commands use a state file (`--state`, default `qpq-state.bin`) to persist the Ed25519 identity seed and MLS group state between invocations. A companion key store file (same path with `.ks` extension) holds HPKE init private keys.

All persistent commands share the `--state` flag:

| Flag | Env var | Default |
|---|---|---|
| `--state` | `QPQ_STATE` | `qpq-state.bin` |
| `--server` | `QPQ_SERVER` | `127.0.0.1:7000` |

### `register-state`

Create or load a persistent identity, generate a KeyPackage, and upload it to the AS.

```bash
cargo run -p quicproquo-client -- register-state \
  --state alice.bin \
  --server 127.0.0.1:7000
```

If `alice.bin` does not exist, a new identity is generated and saved. If it already exists, the existing identity is loaded and a new KeyPackage is generated from it. You can run `register-state` again at any time to upload a fresh KeyPackage (e.g. after the previous one was consumed or expired). For refresh-only (no new identity), use `refresh-keypackage` instead.

**Output:**
```
identity_key : a1b2c3d4e5f6...
fingerprint  : 9f8e7d6c5b4a...
KeyPackage uploaded successfully.
```

### `refresh-keypackage`

Refresh the KeyPackage on the server using your **existing** state file. Does not create a new identity. Use this when:

- Your KeyPackage has expired (server TTL, e.g. 24h).
- Your KeyPackage was consumed (someone invited you) and you want to be invitable again.

Run with the same `--access-token` (or `QPQ_ACCESS_TOKEN`) as for other commands.

```bash
cargo run -p quicproquo-client -- refresh-keypackage \
  --state alice.bin \
  --server 127.0.0.1:7000
```

**Output:**
```
identity_key : a1b2c3d4e5f6...
fingerprint  : 9f8e7d6c5b4a...
KeyPackage uploaded successfully.
```

If you are told "no key" when someone tries to invite you, have them wait and run `refresh-keypackage`, then try the invite again.

### `create-group`

Create a new MLS group. The caller becomes the sole member at epoch 0.

```bash
cargo run -p quicproquo-client -- create-group \
  --state alice.bin \
  --group-id "project-chat"
```

**Output:**
```
group created: project-chat
```

The group state is saved to the state file. You can now invite peers with `invite`.

### `invite`

Fetch a peer's KeyPackage from the AS, add them to the group, and deliver the Welcome message via the DS.

```bash
cargo run -p quicproquo-client -- invite \
  --state alice.bin \
  --peer-key b9a8c7d6e5f4... \
  --server 127.0.0.1:7000
```

This command performs three operations in sequence:

1. Fetches the peer's KeyPackage from the AS (`fetchKeyPackage` RPC).
2. Calls `add_member()` on the local MLS group, producing a Commit and a Welcome.
3. Enqueues the Welcome to the DS for the peer's identity key (`enqueue` RPC).

**Output:**
```
invited peer (welcome queued)
```

### `join`

Join a group by consuming a Welcome message from the DS.

```bash
cargo run -p quicproquo-client -- join \
  --state bob.bin \
  --server 127.0.0.1:7000
```

The command fetches all pending messages for the local identity from the DS and expects to find a Welcome. The Welcome is processed by `MlsGroup::new_from_welcome()`, which decrypts it using the HPKE init private key stored in the key store.

**Output:**
```
joined group successfully
```

### `send`

Encrypt and send an application message to a peer via the DS.

```bash
cargo run -p quicproquo-client -- send \
  --state alice.bin \
  --peer-key b9a8c7d6e5f4... \
  --msg "hello from alice" \
  --server 127.0.0.1:7000
```

The message is encrypted as an MLS `PrivateMessage` using the current epoch's key schedule, then enqueued to the DS for the specified recipient.

**Output:**
```
message sent
```

### `recv`

Receive and decrypt all pending messages from the DS.

```bash
cargo run -p quicproquo-client -- recv \
  --state bob.bin \
  --server 127.0.0.1:7000
```

**Output:**
```
[0] plaintext: hello from alice
```

Additional flags:

| Flag | Default | Purpose |
|---|---|---|
| `--wait-ms` | `0` | Long-poll timeout in milliseconds. If no messages are queued, wait up to this long before returning. Uses the `fetchWait` RPC. |
| `--stream` | `false` | Continuously long-poll for messages. The process will not exit until interrupted. |

```bash
# Wait up to 5 seconds for messages
cargo run -p quicproquo-client -- recv \
  --state bob.bin \
  --wait-ms 5000

# Stream messages continuously
cargo run -p quicproquo-client -- recv \
  --state bob.bin \
  --stream --wait-ms 10000
```

---

## HPKE init key lifecycle warning

The MLS protocol requires that the HPKE init private key generated during KeyPackage creation is available when processing the corresponding Welcome message. In quicproquo, this private key is stored in the key store file (`.ks` extension alongside the state file).

**The same state file and key store must be used for both `register-state` and `join`.** If you:

- Run `register-state` with `--state bob.bin` (which generates `bob.ks`)
- Delete or move `bob.ks` before running `join`
- Or use a different `--state` path for `join`

...then `join` will fail because the HPKE init private key cannot be found.

In ephemeral mode (`register` and `demo-group`), the key is held in process memory and is only valid for the lifetime of that process.

---

## Command reference summary

| Command | Persistent? | Description |
|---|---|---|
| `ping` | No | Health check, prints RTT |
| `register` | No | Generate ephemeral identity + KeyPackage, upload to AS |
| `fetch-key <hex>` | No | Fetch a peer's KeyPackage from AS |
| `demo-group` | No | Automated Alice-and-Bob round-trip |
| `register-state` | Yes | Upload KeyPackage for persistent identity (creates identity if needed) |
| `refresh-keypackage` | Yes | Upload a fresh KeyPackage from existing state (no new identity) |
| `create-group` | Yes | Create MLS group (sole member, epoch 0) |
| `invite` | Yes | Add peer to group, deliver Welcome via DS |
| `join` | Yes | Consume Welcome from DS, join group |
| `send` | Yes | Encrypt and enqueue application message via DS |
| `recv` | Yes | Fetch, decrypt, and display pending messages |

---

## Next steps

- [Demo Walkthrough](demo-walkthrough.md) -- step-by-step narrative with two terminals
- [Running the Server](running-the-server.md) -- server configuration and TLS setup
- [MLS (RFC 9420)](../protocol-layers/mls.md) -- how MLS group operations work under the hood
