Skip to main content

Authentication

Remi uses Ed25519 keypairs for mutual authentication. Both the server (daemon) and client (web app/phone) prove their identity during every connection.

Overview

Authentication protects against:

  • Unauthorized access - only clients with authorized keys can connect
  • Man-in-the-middle attacks - mutual authentication ensures you are talking to the real server
  • Replay attacks - challenges are one-time use

Server Setup

1. Generate a Server Identity

remi keygen

By default, this creates an unencrypted identity for zero-friction startup. To encrypt with a passphrase:

remi keygen --passphrase

2. Start the Daemon

remi my-project

If your identity is encrypted, you will be prompted for the passphrase at startup. To skip the prompt:

REMI_PASSPHRASE=mypassphrase remi my-project

Unencrypted identities (the default) unlock automatically with no prompt.

Client Setup

Web App

The Remi web app generates a client identity automatically in the Settings panel. You can also:

  • Generate a new identity
  • Import an existing identity (JSON)
  • Export your identity for backup or transfer

Client keys are stored in the browser's localStorage (encrypted).

Sharing Across Devices

To use the same identity on multiple devices:

# On the source device
remi export-key > my-identity.json

# On the target device
remi import-key my-identity.json

To share only the public key (for authorization on the server):

remi export-key --public-only > my-public-key.json

Key Exchange

To authorize a client, the server needs the client's public key:

1. Export the Client's Public Key

On the client device (or from the web app's export):

remi export-key --public-only > client-key.json

2. Transfer to Server

Send client-key.json to the server through any secure channel (SCP, messaging app, USB drive).

3. Authorize on Server

remi authorize client-key.json --label "My Phone"

4. Verify

remi keys
Authorized keys:
a1b2c3d4 My Phone 2026-03-01 last used: never

Connection Handshake

When a client connects, this happens automatically:

  1. Client sends hello with its ID and version
  2. Server sends auth_challenge with a random one-time challenge and the server's fingerprint and public key
  3. Client signs the challenge with its private key and sends auth_response
  4. Server verifies the signature against its authorized keys list
  5. Server signs the same challenge with its own key and sends auth_result (mutual auth)
  6. Client verifies the server's signature
  7. Connection is established with hello_ack

Trust-On-First-Use (TOFU)

The first time a client connects to a server, it stores the server's fingerprint. On subsequent connections, the client checks that the fingerprint has not changed.

If the fingerprint changes (e.g., the server regenerated its identity), the client rejects the connection with a warning. This prevents man-in-the-middle attacks.

To connect to a server with a new identity, clear the old fingerprint from the web app's Settings panel under "Known Hosts."

When Authentication is Required

Connection TypeAuth Required?
Direct WebSocketAlways
Relay with rotating codeNo (code acts as shared secret)
Relay with permanent codeAlways