# NuQloud Managed Recovery + Emergency Access This document describes the managed recovery and emergency-access flow added to `qortal_integration`. ## Goal - Keep decentralized auth primary. - Provide optional recovery support for users who opt in. - Ensure admins cannot read recovery data at rest. - Require both admin and user participation for recovery reveal. ## User-facing behavior In **Personal Settings** (`/settings/user/connected-accounts`, section `NuQloud for Nextcloud`): - User can opt in to **Managed Recovery**. - User can optionally opt in to **Emergency Temporary Login Reset**. - User chooses: - linked wallet/account - current backup password (verified against wallet backup API) - recovery passphrase (local secret known only to user) - User can later reveal backup password only with: - recovery passphrase, and - admin-issued one-time recovery code. Admin flow is available in **Administration settings -> NuQloud for Nextcloud -> Recovery Operations**: - Check target user recovery status. - Issue one-time recovery code (configurable TTL). - Start emergency temporary-login window (configurable duration). ## Security model - The backup password is encrypted server-side into an escrow blob: - cipher: `aes-256-gcm` - key derivation: `pbkdf2-sha256` with per-record salt and iterations - Admin recovery codes are: - short-lived, - one-time use, - stored hashed with HMAC in app config registry. - Recovery reveal has lockout controls: - failed attempt counter, - temporary lock window after repeated failures. - Emergency reset is explicit opt-in and time-bounded. ## What this is and is not - This is an app-level encrypted escrow model, not raw plaintext storage. - This is not zero-knowledge from the server runtime perspective during active recovery execution. - This does not currently hard-disable centralized login globally; it adds emergency controls around it. - Passwords app mirroring is not yet implemented in this pass. ## Endpoints All routes are under `/apps/qortal_integration`. ### User endpoints - `GET /api/user/recovery/status` - auth: authenticated user - returns current recovery/enrollment/emergency state for the current user. - `POST /api/user/recovery/enroll` - auth: authenticated user - params: - `walletId` - `currentBackupPassword` - `recoveryPassphrase` - `recoveryPassphraseConfirm` - `managedEnabled` (`1`) - `emergencyLoginEnabled` (`1` or empty) - behavior: - verifies wallet backup password first, - encrypts escrow blob, - persists managed recovery settings. - `POST /api/user/recovery/disable` - auth: authenticated user - clears managed recovery configuration for current user. - `POST /api/user/recovery/reveal` - auth: authenticated user - params: - `recoveryPassphrase` - `adminCode` - behavior: - validates and consumes one-time admin code, - decrypts escrow if passphrase is valid, - returns backup password once. - `POST /api/user/recovery/emergency/complete` - auth: authenticated user - marks emergency flow as completed after user replaces backup password. ### Admin endpoints - `POST /api/admin/recovery/code` - auth: ops admin (`admin` or delegated MSP admin) - params: - `targetUserId` - `expiresMinutes` (default 15, bounded) - behavior: - issues one-time recovery code for target user, - returns code and expiration. - `POST /api/admin/recovery/emergency` - auth: ops admin - params: - `targetUserId` - `durationHours` (default 24, bounded) - behavior: - requires user opted into managed + emergency modes, - sets temporary Nextcloud password for target user, - sets emergency-required and emergency-expiry flags. ## Stored app/user config keys User-scope keys (`IConfig::setUserValue`, app `qortal_integration`): - `user_recovery_managed_enabled` - `user_recovery_emergency_login_enabled` - `user_recovery_wallet_id` - `user_recovery_escrow_blob` - `user_recovery_escrow_updated_at` - `user_recovery_failed_attempts` - `user_recovery_locked_until` - `user_recovery_emergency_until` - `user_recovery_emergency_required` - `user_recovery_emergency_started_by` - `user_recovery_emergency_started_at` App-scope key: - `recovery_admin_code_registry` ## Verify on devcloud Use your normal compose file + env file pattern: ```bash docker compose -f docker-compose.devprod.nossl.yml --env-file .env.devprod exec --user www-data nextcloud php -l /var/www/html/custom_apps/qortal_integration/lib/Controller/ApiController.php docker compose -f docker-compose.devprod.nossl.yml --env-file .env.devprod exec --user www-data nextcloud php -l /var/www/html/custom_apps/qortal_integration/lib/Settings/PersonalSettings.php docker compose -f docker-compose.devprod.nossl.yml --env-file .env.devprod exec --user www-data nextcloud php -l /var/www/html/custom_apps/qortal_integration/templates/personal.php ``` Functional smoke: 1. User enables managed recovery in personal settings. 2. Admin issues recovery code for that user. 3. User reveals backup password with admin code + recovery passphrase. 4. Admin triggers emergency reset (if user opted in). 5. User logs in, rotates backup password, clicks emergency complete. ## Known follow-up work - Optionally integrate with Nextcloud Passwords app if a stable API contract is confirmed for target NC versions. - Add hard enforcement of emergency completion in login/post-login policy flow if required.