# KYC & User Management

This page covers user onboarding, the KYC lifecycle, session management, and role assignment — all managed via the user database service (port 8548) and the admin dashboard.

## User Registration

New users register by providing email, username, and password. On creation:

* The user receives a unique UUID and an Ethereum address (either provided by the user or generated by the system using their encrypted private key).
* A Swiss-style IBAN is registered in the backend proxy's IBAN registry and linked to their Ethereum address.
* `kyc_status` is set to `pending`. All trading and payment operations are blocked until KYC is approved.

### Demo Seed Accounts

The user DB service seeds two pre-verified investor accounts on first startup:

| Name  | Email             | Password   | Role   | KYC Status | Ethereum Address                             | IBAN                    |
| ----- | ----------------- | ---------- | ------ | ---------- | -------------------------------------------- | ----------------------- |
| Alice | <alice@gmail.com> | `password` | Trader | verified   | `0x70997970C51812dc3A010C7d01b50e0d17dc79C8` | `CH5200033000000000002` |
| Bob   | <bob@gmail.com>   | `password` | Trader | verified   | `0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC` | `CH2500033000000000003` |

{% hint style="danger" %}
These seed accounts use well-known Anvil test private keys. Do not use them in production — the keys are publicly known and any funds sent to these addresses can be stolen.
{% endhint %}

## KYC Lifecycle

Every user progresses through a defined KYC state machine. The state is stored in the user database and enforced at the frontend layer before any on-chain action is allowed.

```mermaid
stateDiagram-v2
    [*] --> pending: user registers
    pending --> submitted: user uploads KYC document
    submitted --> verified: admin approves
    submitted --> rejected: admin rejects
    rejected --> submitted: user re-submits
    verified --> rejected: admin revokes (e.g. sanctions match)
```

| Status      | Description                              | User Can...                                 |
| ----------- | ---------------------------------------- | ------------------------------------------- |
| `pending`   | Registered, no document submitted        | View app; cannot trade, pay, or swap        |
| `submitted` | Document uploaded, awaiting admin review | View app; cannot trade, pay, or swap        |
| `verified`  | Admin has approved the KYC submission    | Full access to trading, payments, and swaps |
| `rejected`  | Admin has rejected the submission        | View rejection reason; re-submit documents  |

### KYC Gate Enforcement

The frontend checks `kyc_status` from the session token on every protected route. A user with `kyc_status != "verified"` sees a banner explaining their status and a link to submit or re-submit their documents. All portfolio, payment, and swap action buttons are disabled.

## Admin KYC Review

Admins review pending KYC submissions via the dashboard at **<http://localhost:3000/kyc>**.

The KYC page shows:

* A list of users with `kyc_status: submitted`, sorted by submission time.
* The uploaded document (PDF) viewable inline.
* User details: email, username, Ethereum address, IBAN.
* Approve and Reject buttons with an optional rejection reason field.

Approvals and rejections take effect immediately — the user's next session refresh picks up the new status.

### Configuring KYC Settings

KYC document requirements and approval workflow can be configured via the admin settings:

```bash
# Update KYC settings (example: require re-verification every 12 months)
PATCH http://localhost:8548/admin/settings
Content-Type: application/json

{
  "kyc_expiry_days": 365,
  "kyc_document_types": ["passport", "drivers_license", "national_id"]
}
```

## User Roles

Each user is assigned exactly one role. Roles are assigned by an Admin via the dashboard or the user DB API.

| Role             | Token Transfer Limit      | Capabilities                                                                               |
| ---------------- | ------------------------- | ------------------------------------------------------------------------------------------ |
| **Admin**        | Unlimited                 | Full access: user management, KYC approval, permission configuration, all chain operations |
| **Trader**       | ≤ $1,000,000 per transfer | Subscribe/redeem MMF shares, send IBAN payments, swap assets                               |
| **SeniorTrader** | ≤ $5,000,000 per transfer | All Trader actions at higher limits                                                        |
| **Compliance**   | Read-only                 | Read all transactions and positions; configure compliance rules; freeze/unfreeze wallets   |
| **Auditor**      | Read-only                 | Access to transactions and audit logs only                                                 |
| **Regulator**    | Read-only                 | Access to selective disclosure endpoints and regulatory reports                            |

Transfer limits are enforced by the `PermissionRegistry` contract via the backend proxy. See [Permission Rules](/reference/permission-rules.md) for the full rule format and how to customize limits.

### Changing a User's Role

Via the dashboard (**Users** page):

1. Find the user in the user list.
2. Click the role badge to open the role editor.
3. Select the new role and confirm.

Via the API:

```bash
PATCH http://localhost:8548/admin/users/{user_id}
Content-Type: application/json

{ "role": "SeniorTrader" }
```

## Session Management

### Sliding Window Sessions

Sessions use a **sliding window** model. Each authenticated call to `GET /auth/me` extends the session by the configured TTL (default: 30 minutes). A user who stays active never gets logged out. A user who goes idle for longer than the TTL is automatically logged out on their next action.

| Parameter              | Default                         | Configurable                      |
| ---------------------- | ------------------------------- | --------------------------------- |
| Session TTL            | 30 minutes                      | Yes — via `PATCH /admin/settings` |
| Session cache in proxy | 5 seconds                       | Via `SESSION_CACHE_TTL` env var   |
| Session storage        | `localStorage` (token + expiry) | —                                 |

### Session Cache

The backend proxy caches validated session tokens for 5 seconds to avoid a user DB round-trip on every RPC call. The cache key is `SHA-256(token)` — the raw token is never cached or logged.

```
Client → backend (8546)
           │
           ├─ Cache hit (< 5s old) → use cached role profile
           └─ Cache miss → GET user_db/auth/me → cache result for 5s
```

This means:

* In `AUTH_MODE=advisory` (default): all RPC calls are forwarded; caller identity is logged from the cache.
* In `AUTH_MODE=strict`: calls with no valid session token are rejected with HTTP 401.

### Configuring Session Timeout

```bash
PATCH http://localhost:8548/admin/settings
Content-Type: application/json

{ "session_timeout_minutes": 60 }
```

Changes take effect for new sessions immediately. Existing sessions retain their original TTL until they are next refreshed.

## User DB API Reference

The user DB service exposes a REST API at port 8548. These endpoints are consumed by the dashboard and investor frontends — they are not part of the JSON-RPC interface.

### Authentication

#### `POST /auth/login`

Authenticate a user and return a session token.

**Request:**

```json
{
  "email": "alice@example.com",
  "password": "password"
}
```

**Response:**

```json
{
  "token": "eyJ...",
  "expires_at": "2026-04-13T10:30:00Z"
}
```

#### `GET /auth/me`

Validate the current session token and return the user profile. Extends the session TTL.

**Headers:** `Authorization: Bearer <token>`

**Response:**

```json
{
  "user_id": "uuid",
  "email": "alice@example.com",
  "username": "alice",
  "ethereum_address": "0x70997970...",
  "role": "Trader",
  "kyc_status": "verified",
  "iban": "CH5200033000000000002",
  "expires_at": "2026-04-13T10:30:00Z"
}
```

### User Management (Admin Only)

#### `GET /admin/users`

List all registered users.

#### `PATCH /admin/users/{user_id}`

Update a user's role or KYC status.

**Request:** `{ "role": "SeniorTrader" }` or `{ "kyc_status": "verified" }`

#### `DELETE /admin/users/{user_id}`

Remove a user account. Does not affect on-chain state.

### KYC

#### `POST /kyc/submit`

Upload a KYC document (PDF). Sets `kyc_status` to `submitted`.

**Request:** `multipart/form-data` with `file` field.

#### `GET /admin/kyc/pending`

List users with `kyc_status: submitted`, ordered by submission time.

#### `POST /admin/kyc/{user_id}/approve`

Approve a KYC submission. Sets `kyc_status` to `verified`.

#### `POST /admin/kyc/{user_id}/reject`

Reject a KYC submission. Optionally include a `reason` in the request body.

### Settings

#### `GET /admin/settings`

Return current settings (session timeout, KYC configuration).

#### `PATCH /admin/settings`

Update one or more settings. Accepts `session_timeout_minutes`, `kyc_expiry_days`, `kyc_document_types`.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://cocoon.erigon.tech/operators/kyc-and-user-management.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
