Skip to main content

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:

NameEmailPasswordRoleKYC StatusEthereum AddressIBAN
Alicealice@gmail.compasswordTraderverified0x70997970C51812dc3A010C7d01b50e0d17dc79C8CH5200033000000000002
Bobbob@gmail.compasswordTraderverified0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BCCH2500033000000000003
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.

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.

StatusDescriptionUser Can...
pendingRegistered, no document submittedView app; cannot trade, pay, or swap
submittedDocument uploaded, awaiting admin reviewView app; cannot trade, pay, or swap
verifiedAdmin has approved the KYC submissionFull access to trading, payments, and swaps
rejectedAdmin has rejected the submissionView 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:

# 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.

RoleToken Transfer LimitCapabilities
AdminUnlimitedFull access: user management, KYC approval, permission configuration, all chain operations
Trader≤ $1,000,000 per transferSubscribe/redeem MMF shares, send IBAN payments, swap assets
SeniorTrader≤ $5,000,000 per transferAll Trader actions at higher limits
ComplianceRead-onlyRead all transactions and positions; configure compliance rules; freeze/unfreeze wallets
AuditorRead-onlyAccess to transactions and audit logs only
RegulatorRead-onlyAccess to selective disclosure endpoints and regulatory reports

Transfer limits are enforced by the PermissionRegistry contract via the backend proxy. See Permission Rules 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:

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.

ParameterDefaultConfigurable
Session TTL30 minutesYes — via PATCH /admin/settings
Session cache in proxy5 secondsVia SESSION_CACHE_TTL env var
Session storagelocalStorage (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

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:

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

Response:

{
"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:

{
"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.