Inicialización, códigos QR de dispositivos e inicio de sesión con código de seis dígitos
El Agent utiliza por defecto Exchange en wss://exchange.ivjn.us; la API de administración de CLI/Console utiliza https://exchange.ivjn.us. La configuración existente tiene prioridad y se conservan las direcciones de Exchange autohospedadas.
Registro SaaS y primer inicio de sesión
remote-cli register --username alice --email alice@example.com
# 按隐藏输入提示设置密码,注册后向邮箱发送验证码。
remote-cli verify-email --username alice
# 隐藏输入邮件中的 8 位数字验证码,然后登录。
remote-cli login --email alice@example.com
# init 是 login 的别名,不带 --email 时交互输入邮箱和密码。
remote-cli init
# 原有令牌仍可使用:
remote-cli login --server https://exchange.ivjn.us --token-stdin
El registro y el acceso se conectan por defecto a https://exchange.ivjn.us; añada --server para un servicio propio. La identidad de acceso es el correo electrónico: se eliminan los espacios y se convierte a minúsculas; los formatos no válidos se rechazan inmediatamente. El nombre de usuario es el nombre del espacio de trabajo elegido al registrarse (3-64 letras, dígitos o ._-). Las contraseñas de cuenta no tienen restricciones de longitud ni de caracteres; el servidor solo almacena sus hashes. La automatización puede usar --password-stdin; no incluya contraseñas en argumentos. Las cuentas nuevas no pueden acceder hasta verificar su correo. CLI guarda únicamente el token de sesión devuelto, nunca la contraseña, y conserva el acceso existente mientras el registro espera verificación.
Cada registro crea un espacio de trabajo independiente cuya cuenta es administradora. Los dispositivos, miembros, sesiones remotas, métricas y auditorías están aislados; distintos espacios pueden utilizar el mismo ID de dispositivo. Aprobar un código de seis dígitos añade el dispositivo o miembro nuevo al espacio de trabajo de quien lo aprueba. Para unirse a un espacio existente, utilice el token o la autorización de seis dígitos facilitados por su administrador.
Android Console también permite registrarse y acceder mediante contraseña, token existente o código de seis dígitos. La contraseña solo se utiliza en la solicitud actual; las credenciales se guardan en SecureStore.
Las sesiones de acceso por contraseña duran 30 días, con un máximo de 32 sesiones activas por cuenta; volver a acceder no expulsa a otros clientes. POST /api/auth/logout revoca el token Bearer de la sesión actual; los WebSockets establecidos se cierran en un máximo de 5 segundos. Vuelva a acceder después de su vencimiento. Los tokens de dispositivo y los tokens operativos existentes conservan su ciclo de vida original.
Las identidades que solo tienen token carecen de cuenta con contraseña y no pueden recuperar el acceso por correo. Actualmente no se incluye facturación ni enrutamiento WebSocket entre instancias.
Verificación de correo y recuperación de contraseña
remote-cli resend-verification --email alice@example.com
remote-cli verify-email --username alice
remote-cli forgot-password --email alice@example.com
remote-cli reset-password --email alice@example.com
# 按提示输入邮件验证码、新密码,再重新登录。
# 已登录的旧密码账号绑定或更换邮箱,需要再次验证当前密码。
remote-cli set-email --email alice@example.com
remote-cli verify-email --username alice
La verificación y la recuperación utilizan códigos de ocho dígitos separados según su finalidad. Caducan a los 15 minutos o tras cinco intentos incorrectos consecutivos; enviar otro correo invalida el código anterior. Cada cuenta puede solicitar un correo por finalidad y minuto. Los comandos de verificación y restablecimiento aceptan --code para automatización; por defecto, la entrada se oculta. Un restablecimiento correcto revoca todas las sesiones de acceso de la cuenta; los WebSockets establecidos se desconectan en un máximo de 5 segundos. No afecta a otros miembros ni dispositivos del espacio.
Introduzca el correo en el registro de Console y continúe a la verificación. La página de acceso ofrece Verificar correo y Olvidé mi contraseña. Al cambiar el correo, la dirección anterior sigue activa hasta completar la verificación. Las solicitudes de reenvío y recuperación devuelven el mismo mensaje genérico sin revelar si el correo está registrado.
El correo requiere la migración 0004_email.sql y la configuración SMTP: SMTP_HOST, SMTP_PORT, SMTP_FROM, SMTP_USERNAME, SMTP_PASSWORD y SMTP_SECURITY. Por defecto se utiliza STARTTLS; también se admite TLS implícito. SMTP sin cifrar solo se permite en direcciones de bucle local para pruebas. Guarde las credenciales SMTP en el archivo de entorno del servicio y fuera de Git. Sin configuración de correo, las nuevas interfaces de registro y envío devuelven 503; el acceso de las cuentas existentes continúa funcionando. Configure el envío antes de habilitar el registro en producción. Un fallo temporal de SMTP puede dejar una cuenta pendiente de verificación; cuando se recupere, reenvíe el correo sin repetir el registro.
API de cuentas de Exchange
| Método y ruta | Autenticación | Finalidad |
|---|---|---|
POST /api/auth/register |
Ninguna; limitada por frecuencia | {username,password,email} crea una cuenta pendiente de verificación; devuelve 202 y verification_required, sin emitir una sesión |
POST /api/auth/login |
Ninguna; limitada por frecuencia | {email,password} emite una sesión independiente |
GET /api/auth/me |
Token operativo o sesión de acceso | Devuelve name, tenant_id y role |
POST /api/auth/logout |
Sesión de acceso actual | Revoca la sesión actual sin revocar otros clientes |
POST /api/auth/verify-email |
Ninguna; limitada por frecuencia | {username,code} consume un código de verificación de correo |
POST /api/auth/resend-verification |
Ninguna; limitada por frecuencia | {email} reenvía un correo a la dirección pendiente de verificación |
POST /api/auth/forgot-password |
Ninguna; limitada por frecuencia | {email} envía un código de recuperación a una dirección verificada |
POST /api/auth/reset-password |
Ninguna; limitada por frecuencia | {email,code,password} cambia la contraseña y revoca las sesiones anteriores |
POST /api/auth/email |
Sesión actual y contraseña de la cuenta | {email,password} solicita vincular o cambiar el correo; se aplica tras verificarlo |
El acceso correcto devuelve username, email (si está vinculado), tenant_id, token y expires_in, con Cache-Control: no-store. Las contraseñas se almacenan con sal y Argon2id con parámetros predeterminados. Se permiten 20 solicitudes de cuenta por minuto e IP TCP de origen, y 10 por nombre de usuario, con un máximo de cuatro cálculos de hash simultáneos. No se confía en las IP reenviadas proporcionadas por clientes. Los clientes detrás de un proxy inverso comparten su cuota IP; los despliegues públicos deben añadir límites en una entrada de confianza.
Rust Exchange desactiva el registro público por defecto. Actívelo explícitamente con EXCHANGE_ALLOW_REGISTRATION=true y configure SMTP; false bloquea nuevos registros y las cuentas existentes pueden seguir accediendo.
Mostrar un código de seis dígitos en el Agent
Android Remote Agent ya incluye la dirección predeterminada. Pulse Conectar con un código de seis dígitos para mostrar un código válido durante 5 minutos. En una CLI de administrador con sesión iniciada, ejecute:
remote-cli pair 012345
# 可选:覆盖默认 ID 和显示名称
remote-cli pair 012345 --device-id my-android --name "我的手机"
Al consultar el código, CLI muestra el nombre del dispositivo, el tipo de identidad y el ID sugerido. Compruebe que sea su dispositivo antes de autorizarlo. El ID predeterminado conserva las letras ASCII en minúsculas, dígitos y ._- del nombre, eliminando espacios y otros caracteres. Si no quedan caracteres utilizables, usa device; el máximo es 64 caracteres. Si ya existe en el mismo espacio, se añaden números, por ejemplo realmev5, realmev51 y realmev52, acortando la base cuando sea necesario. Exchange resuelve conflictos simultáneos durante la creación y CLI imprime el ID definitivo. Los ID especificados manualmente también reciben un sufijo numérico en caso de conflicto. Android utiliza el nombre del sistema o, si no está disponible, el modelo; los Agents nativos utilizan el nombre del host. El Agent recibe automáticamente un token de dispositivo independiente, lo guarda cifrado y se conecta. No se habilitan automáticamente accesibilidad ni control de pantalla. La cancelación, el vencimiento o un error de autenticación conservan la configuración anterior; detenga primero un Agent que ya se esté conectando.
Añadir equipos, routers y Agents Linux
Los Agents Windows, macOS, OpenWrt y Linux permiten cualquiera de estos métodos sin introducir manualmente un token:
| Plataforma | Método 1: código de emparejamiento / QR del dispositivo | Método 2: pegar invitación |
|---|---|---|
Windows / macOS (escritorio --ui, iniciado con doble clic) |
En la página local, Añadir este dispositivo → Obtener código de emparejamiento muestra el código de seis dígitos y el QR | Pegue en Invitación y pulse Unirse con invitación |
| OpenWrt (luci-app-remote) | En LuCI, Servicios → Administración remota → Añadir dispositivo → Obtener código de emparejamiento muestra el código y el QR | En la misma página, pegue en Método 2: contenido de la invitación y pulse Unirse |
| Linux / línea de comandos de cualquier plataforma | remote-agent pair muestra el código y un QR de caracteres en la terminal |
remote-agent join '<邀请内容>', o remote-agent join - para leer la entrada estándar |
Método 1: El Agent solicita un código válido durante 5 minutos y muestra un QR con remote://pair?code=012345&server=wss%3A%2F%2Fexchange.ivjn.us (sin credenciales). El administrador puede usar Android Console Añadir dispositivo → Introducir código de emparejamiento → Escanear QR del dispositivo (escanear, comprobar que coincida el servidor, consultar y confirmar la aprobación); introducir los seis dígitos; usar Aprobar código de emparejamiento en Web Console para introducir el código o pegar remote://pair?...; o ejecutar remote-cli pair 012345 (también acepta ese URI). Tras aprobarlo, el Agent guarda el token y se conecta automáticamente.
Método 2: El administrador genera una invitación de un solo uso en Android Console mediante Añadir dispositivo → Añadir escaneando y pulsa Copiar invitación (pegar en equipo / router); también puede ejecutar remote-cli qr --device-id pc-01 --text. La invitación es el JSON del QR ({"type":"remote-enrollment",...}), válido durante 5 minutos y utilizable una vez. También se acepta el contenido de un QR de token de dispositivo (remote-cli device qr).
Ubicación de almacenamiento y conexión:
- Interfaz de escritorio: escribe el archivo de configuración actual (por defecto
%LOCALAPPDATA%\remote-agent\config.json/~/.config/remote-agent/config.json) y se conecta inmediatamente. - OpenWrt: escribe UCI
remote-agent.main(server / device_id / token,enabled=1) y reinicia el servicio procd. - Línea de comandos: utiliza la ruta de configuración de escritorio (UCI por defecto en OpenWrt).
--config PATHelige un JSON y--ucifuerza UCI; tras guardar se conecta en primer plano (--no-connectsolo guarda). Reinicie cualquier servicio que utilice esa configuración.
Por defecto se rechaza sustituir credenciales existentes: la línea de comandos exige --force; la interfaz de escritorio y LuCI solicitan confirmación. Siguen disponibles los métodos de token existentes (--device-id/--token, archivo de configuración y ajustes LuCI).
remote-agent pair # 显示六位码 + 二维码,批准后保存并连接
remote-agent pair --server wss://your-exchange.example.com --config ./agent.json
remote-agent join '{"type":"remote-enrollment","version":1,...}'
remote-agent join - < invite.txt --no-connect
El emparejamiento nativo exige wss://; solo se permite ws:// en bucle local para pruebas. Android y Console exigen HTTPS/WSS.
Acceso a Console con código de seis dígitos
En el acceso de Android Remote Console, pulse Acceder con un código de seis dígitos. En Web Console (/console), elija Código de seis dígitos en la tarjeta de acceso y solicite un código. El administrador puede aprobarlo así:
remote-cli pair 012345 --operator-name mobile-alice
# 只读客户端:追加 --role readonly
O, desde un espacio con sesión iniciada en Web Console, pulse Aprobar código de emparejamiento (solo visible para administradores; los operadores de solo lectura no tienen esta opción), introduzca el código de la pantalla del otro cliente, compruebe el tipo y la etiqueta, y apruebe.
Por defecto se crea una cuenta operator independiente, sin copiar el token del administrador. Este puede especificar --role admin o --role readonly. Tras recibir autorización, Android Console vuelve a validar las credenciales, las guarda en SecureStore y abre la lista de dispositivos. Web Console conserva las credenciales del operador en la memoria de la página actual (igual que el acceso por cuenta/token) y abre la lista. Si se cancela o falla, sigue disponible el acceso anterior por cuenta o token, y se conserva lo introducido posteriormente en otros métodos.
Escanear un código de CLI con Remote Agent
remote-cli qr --device-id my-phone
# 输出 SVG 供手机扫描:
remote-cli qr --device-id my-phone --output ./phone-login.svg
Genere un QR desde una CLI de administrador con sesión iniciada. En el teléfono, abra Remote Agent, pulse Escanear QR de conexión, compruebe el servidor y el ID, y pulse Conectar. El QR solo contiene un ticket temporal aleatorio de 256 bits, válido durante 5 minutos y vinculable a un dispositivo; no contiene un token permanente. Tras escanearlo, Exchange emite un token que el teléfono guarda en Keystore antes de conectarse. El ID debe ser nuevo; no sustituye dispositivos existentes ni rota sus tokens.
Las solicitudes del mismo teléfono incluyen un claim_id aleatorio estable; los reintentos tras una desconexión recuperan el mismo resultado. Otro claim_id no puede reclamarlo de nuevo. Tras guardar, el teléfono confirma la eliminación del ticket. Los tickets se guardan en memoria de proceso y caducan al reiniciar Exchange; un resultado ya reclamado se conserva hasta otros 5 minutos para que el cliente original reintente.
| Método y ruta | Autenticación | Finalidad |
|---|---|---|
POST /api/pairing/invite |
Administrador | {device_id,name?} genera un ticket; máximo de 10 por minuto y administrador, y 256 globales |
POST /api/pairing/claim |
Bearer <ticket> |
{claim_id} devuelve credenciales independientes; los reintentos deben conservar el mismo claim_id hexadecimal de 32 caracteres |
POST /api/pairing/claim/complete |
Igual que arriba | Confirma el almacenamiento correcto y elimina el ticket |
Generar un QR de dispositivo
# 创建设备,同时在终端显示二维码
remote-cli device add my-android --qr
# 新建 SVG 文件,可在电脑打开后供手机扫描
remote-cli device add another-phone --qr-output ./another-phone.svg
# 使用手里已有的设备令牌,不创建或轮换设备
remote-cli device qr my-android --output ./my-android.svg
# 明确轮换令牌并生成二维码;原令牌立即失效
remote-cli device rotate-token my-android --qr
device qr lee el token mediante entrada oculta y también acepta --token-stdin. El QR solo contiene tokens de dispositivo; no se admiten tokens operativos. Los archivos de salida no sobrescriben archivos existentes y contienen credenciales: protéjalos y elimínelos tras usarlos. Los QR requieren HTTPS/WSS.
Android Console muestra un QR inmediatamente después de crear un dispositivo. En los detalles, Rotar token y generar QR explica antes el efecto de desconexión y solicita confirmación. El servidor solo almacena hashes de tokens y no puede recuperar un QR antiguo desde un registro existente; consultar un QR nunca rota silenciosamente el token.
API de emparejamiento de Exchange
| Método y ruta | Autenticación | Finalidad |
|---|---|---|
POST /api/pairing/start |
Ninguna; limitada por frecuencia | Envía kind: device/operator y label; recibe code, poll_token, expires_in: 300 e interval: 3 |
GET /api/pairing/{code} |
Token operativo de administrador | Consulta tipo, etiqueta y tiempo restante; no devuelve credenciales |
POST /api/pairing/approve |
Token operativo de administrador | Envía code, kind, identity, name y role opcionales para crear un dispositivo o cuenta independiente |
POST /api/pairing/poll |
Authorization: Bearer <poll_token> |
Devuelve pending o approved; el resultado aprobado contiene credentials.kind/identity/token |
POST /api/pairing/complete |
Igual que arriba | Elimina la autorización temporal tras guardarla el cliente; se puede repetir con seguridad |
GET /ws/pairing |
Ninguna; limitada por frecuencia | WebSocket de emparejamiento nativo; recibe code y después approved o expired; envía el texto complete tras guardar |
El código de seis dígitos solo permite a un administrador autenticado localizar y confirmar la solicitud; no permite sondear ni obtener credenciales. La credencial de sondeo es un secreto aleatorio de 256 bits y no se incluye en la URL. Se rechazan aprobaciones duplicadas; si falla crear la identidad, se libera la reserva para reintentar. El resultado aprobado puede releerse con la misma credencial privada de sondeo para evitar perder credenciales por una respuesta de red perdida. El cliente confirma su eliminación tras guardar.
Las solicitudes pendientes de aprobación y los resultados aprobados pendientes de recoger se conservan, cada uno, un máximo de 5 minutos. Sin REDIS_URL, las sesiones temporales solo existen en la memoria del proceso Exchange actual; tras reiniciarlo hay que solicitar otro código y, en despliegues con varias instancias, dirigir el emparejamiento a la misma instancia. Con Redis, las entradas y los contadores se guardan allí (con TTL de 5 minutos), de modo que las solicitudes pueden llegar a cualquier instancia sin afinidad de sesión. Cada IP TCP real de origen puede obtener 10 códigos por minuto; cada administrador puede consultar o aprobar 20 veces por minuto. En modo memoria hay además un máximo global de 256 sesiones pendientes. No se confía en IP reenviadas por proxies para limitar frecuencia; los usuarios del mismo proxy comparten cuota. TTL y limpieza periódica eliminan datos vencidos. Las respuestas incluyen Cache-Control: no-store; no se registran secretos de emparejamiento ni tokens emitidos en texto plano.
Las solicitudes de emparejamiento no pueden sobrescribir ID de dispositivo ni cuentas existentes. El administrador debe comprobar tanto el código como el cliente solicitante; no apruebe códigos proporcionados mediante engaño por terceros.
API de códigos de conexión: POST /api/pairing/start admite device_name opcional y devuelve ese nombre. La consulta administrativa GET /api/pairing/{code} devuelve device_name y suggested_device_id del espacio actual; consultar no reserva el ID. Puede omitirse identity del dispositivo en POST /api/pairing/approve; el éxito devuelve 200 con {identity} (ID definitivo, sin token). El WebSocket nativo /ws/pairing?device_name=... admite nombres codificados para URL.