156 lines
6.3 KiB
Markdown
156 lines
6.3 KiB
Markdown
# Arbiter
|
||
|
||
Arbiter is a permissioned signing service for cryptocurrency wallets. It runs as a background service on the user's machine with an optional client application for vault management.
|
||
|
||
**Core principle:** The vault NEVER exposes key material. It only produces signatures when a request satisfies the configured policies.
|
||
|
||
---
|
||
|
||
## 1. Peer Types
|
||
|
||
Arbiter distinguishes two kinds of peers:
|
||
|
||
- **User Agent** — A client application used by the owner to manage the vault (create wallets, approve SDK clients, configure policies).
|
||
- **SDK Client** — A consumer of signing capabilities, typically an automation tool. In the future, this could include a browser-based wallet.
|
||
|
||
---
|
||
|
||
## 2. Authentication
|
||
|
||
### 2.1 Challenge-Response
|
||
|
||
All peers authenticate via public-key cryptography using a challenge-response protocol:
|
||
|
||
1. The peer sends its public key and requests a challenge.
|
||
2. The server looks up the key in its database. If found, it increments the nonce and returns a challenge (replay-attack protection).
|
||
3. The peer signs the challenge with its private key and sends the signature back.
|
||
4. The server verifies the signature:
|
||
- **Pass:** The connection is considered authenticated.
|
||
- **Fail:** The server closes the connection.
|
||
|
||
### 2.2 User Agent Bootstrap
|
||
|
||
On first run — when no User Agents are registered — the server generates a one-time bootstrap token. It is made available in two ways:
|
||
|
||
- **Local setup:** Written to `~/.arbiter/bootstrap_token` for automatic discovery by a co-located User Agent.
|
||
- **Remote setup:** Printed to the server's console output.
|
||
|
||
The first User Agent must present this token alongside the standard challenge-response to complete registration.
|
||
|
||
### 2.3 SDK Client Registration
|
||
|
||
There is no bootstrap mechanism for SDK clients. They must be explicitly approved by an already-registered User Agent.
|
||
|
||
---
|
||
|
||
## 3. Server Identity
|
||
|
||
The server proves its identity using TLS with a self-signed certificate. The TLS private key is generated on first run and is long-term; no rotation mechanism exists yet due to the complexity of multi-peer coordination.
|
||
|
||
Peers verify the server by its **public key fingerprint**:
|
||
|
||
- **User Agent (local):** Receives the fingerprint automatically through the bootstrap token.
|
||
- **User Agent (remote) / SDK Client:** Must receive the fingerprint out-of-band.
|
||
|
||
> A streamlined setup mechanism using a single connection string is planned but not yet implemented.
|
||
|
||
---
|
||
|
||
## 4. Key Management
|
||
|
||
### 4.1 Key Hierarchy
|
||
|
||
There are three layers of keys:
|
||
|
||
| Key | Encrypts | Encrypted by |
|
||
|---|---|---|
|
||
| **User key** (password) | Root key | — (derived from user input) |
|
||
| **Root key** | Wallet keys | User key |
|
||
| **Wallet keys** | — (used for signing) | Root key |
|
||
|
||
This layered design enables:
|
||
|
||
- **Password rotation** without re-encrypting every wallet key (only the root key is re-encrypted).
|
||
- **Root key rotation** without requiring the user to change their password.
|
||
|
||
### 4.2 Encryption at Rest
|
||
|
||
The database stores everything in encrypted form using symmetric AEAD. The encryption scheme is versioned to support transparent migration — when the vault unseals, Arbiter automatically re-encrypts any entries that are behind the current scheme version. See [IMPLEMENTATION.md](IMPLEMENTATION.md) for the specific scheme and versioning mechanism.
|
||
|
||
---
|
||
|
||
## 5. Vault Lifecycle
|
||
|
||
### 5.1 Sealed State
|
||
|
||
On boot, the root key is encrypted and the server cannot perform any signing operations. This state is called **Sealed**.
|
||
|
||
### 5.2 Unseal Flow
|
||
|
||
To transition to the **Unsealed** state, a User Agent must provide the password:
|
||
|
||
1. The User Agent initiates an unseal request.
|
||
2. The server generates a one-time key pair and returns the public key.
|
||
3. The User Agent encrypts the user's password with this one-time public key and sends the ciphertext to the server.
|
||
4. The server decrypts and verifies the password:
|
||
- **Success:** The root key is decrypted and placed into a hardened memory cell. The server transitions to `Unsealed`. Any entries pending encryption scheme migration are re-encrypted.
|
||
- **Failure:** The server returns an error indicating the password is incorrect.
|
||
|
||
### 5.3 Memory Protection
|
||
|
||
Once unsealed, the root key must be protected in memory against:
|
||
|
||
- Memory dumps
|
||
- Page swaps to disk
|
||
- Hibernation files
|
||
|
||
See [IMPLEMENTATION.md](IMPLEMENTATION.md) for the current and planned memory protection approaches.
|
||
|
||
---
|
||
|
||
## 6. Permission Engine
|
||
|
||
### 6.1 Fundamental Rules
|
||
|
||
- SDK clients have **no access by default**.
|
||
- Access is granted **explicitly** by a User Agent.
|
||
- Grants are scoped to **specific wallets** and governed by **policies**.
|
||
|
||
Each blockchain requires its own policy system due to differences in static transaction analysis. Currently, only EVM is supported; Solana support is planned.
|
||
|
||
Arbiter is also responsible for ensuring that **transaction nonces are never reused**.
|
||
|
||
### 6.2 EVM Policies
|
||
|
||
Every EVM grant is scoped to a specific **wallet** and **chain ID**.
|
||
|
||
#### 6.2.1 Transaction Sub-Grants
|
||
|
||
Arbiter maintains an ever-expanding database of known contracts and their ABIs. Based on contract knowledge, transaction requests fall into three categories:
|
||
|
||
**1. Known contract (ABI available)**
|
||
|
||
The transaction can be decoded and presented with semantic meaning. For example: *"Client X wants to transfer Y USDT to address Z."*
|
||
|
||
Available restrictions:
|
||
- Volume limits (e.g., "no more than 10,000 tokens ever")
|
||
- Rate limits (e.g., "no more than 100 tokens per hour")
|
||
|
||
**2. Unknown contract (no ABI)**
|
||
|
||
The transaction cannot be decoded, so its effects are opaque — it could do anything, including draining all tokens. The user is warned, and if approved, access is granted to all interactions with the contract (matched by the `to` field).
|
||
|
||
Available restrictions:
|
||
- Transaction count limits (e.g., "no more than 100 transactions ever")
|
||
- Rate limits (e.g., "no more than 5 transactions per hour")
|
||
|
||
**3. Plain ether transfer (no calldata)**
|
||
|
||
These transactions have no `calldata` and therefore cannot interact with contracts. They can be subject to the same volume and rate restrictions as above.
|
||
|
||
#### 6.2.2 Global Limits
|
||
|
||
In addition to sub-grant-specific restrictions, the following limits can be applied across all grant types:
|
||
|
||
- **Gas limit** — Maximum gas per transaction.
|
||
- **Time-window restrictions** — e.g., signing allowed only 08:00–20:00 on Mondays and Thursdays. |