App para iOS (Nodo)

Disponibilidad: vista previa interna. La app de iOS aún no se distribuye públicamente.

Qué hace

  • Se conecta a un Gateway por WebSocket (LAN o tailnet).
  • Expone capacidades del nodo: Canvas, Captura de pantalla, Captura de cámara, Ubicación, Modo conversación, Activación por voz.
  • Recibe comandos node.invoke y reporta eventos de estado del nodo.

Requisitos

  • Gateway ejecutándose en otro dispositivo (macOS, Linux o Windows vía WSL2).
  • Ruta de red:
    • Misma LAN vía Bonjour, o
    • Tailnet vía DNS-SD unicast (dominio de ejemplo: openclaw.internal.), o
    • Host/puerto manual (alternativa).

Inicio rápido (emparejar + conectar)

  1. Inicia el Gateway:
openclaw gateway --port 18789
  1. En la app de iOS, abre Ajustes y elige un Gateway descubierto (o activa Host manual e ingresa host/puerto).

  2. Aprueba la solicitud de emparejamiento en el host del Gateway:

openclaw devices list
openclaw devices approve <requestId>
  1. Verifica la conexión:
openclaw nodes status
openclaw gateway call node.list --params "{}"

Push respaldado por relay para builds oficiales

Los builds oficiales distribuidos de iOS usan el relay de push externo en lugar de publicar el token APNs crudo al Gateway.

Requisito del lado del Gateway:

{
  gateway: {
    push: {
      apns: {
        relay: {
          baseUrl: "https://relay.example.com",
        },
      },
    },
  },
}

Cómo funciona el flujo:

  • La app de iOS se registra con el relay usando App Attest y el recibo de la app.
  • El relay devuelve un handle de relay opaco más una concesión de envío con ámbito de registro.
  • La app de iOS obtiene la identidad del Gateway emparejado y la incluye en el registro del relay, de modo que el registro respaldado por relay se delega a ese Gateway específico.
  • La app reenvía ese registro respaldado por relay al Gateway emparejado con push.apns.register.
  • El Gateway usa ese handle de relay almacenado para push.test, despertares en segundo plano y nudges de despertar.
  • La URL base del relay del Gateway debe coincidir con la URL del relay incorporada en el build oficial de iOS/TestFlight.
  • Si la app luego se conecta a un Gateway diferente o a un build con una URL base de relay diferente, refresca el registro del relay en lugar de reusar el binding anterior.

Lo que el Gateway no necesita para esta ruta:

  • Ningún token de relay a nivel de despliegue.
  • Ninguna clave APNs directa para envíos respaldados por relay oficiales/TestFlight.

Flujo esperado del operador:

  1. Instala el build oficial de iOS/TestFlight.
  2. Configura gateway.push.apns.relay.baseUrl en el Gateway.
  3. Empareja la app con el Gateway y deja que termine de conectarse.
  4. La app publica push.apns.register automáticamente después de que tiene un token APNs, la sesión del operador está conectada y el registro del relay tiene éxito.
  5. Después de eso, push.test, despertares de reconexión y nudges de despertar pueden usar el registro respaldado por relay almacenado.

Nota de compatibilidad:

  • OPENCLAW_APNS_RELAY_BASE_URL sigue funcionando como override temporal de entorno para el Gateway.

Flujo de autenticación y confianza

El relay existe para imponer dos restricciones que APNs directos en el Gateway no pueden proporcionar para builds oficiales de iOS:

  • Solo builds genuinos de OpenClaw para iOS distribuidos a través de Apple pueden usar el relay hospedado.
  • Un Gateway solo puede enviar pushes respaldados por relay para dispositivos iOS que se emparejaron con ese Gateway específico.

Salto por salto:

  1. App iOS -> Gateway

    • La app primero se empareja con el Gateway a través del flujo de autenticación normal del Gateway.
    • Eso le da a la app una sesión de nodo autenticada más una sesión de operador autenticada.
    • La sesión del operador se usa para llamar a gateway.identity.get.
  2. App iOS -> relay

    • La app llama a los endpoints de registro del relay por HTTPS.
    • El registro incluye prueba de App Attest más el recibo de la app.
    • El relay valida el bundle ID, la prueba de App Attest y el recibo de Apple, y requiere la ruta de distribución oficial/producción.
    • Esto es lo que bloquea builds locales de Xcode/dev de usar el relay hospedado. Un build local puede estar firmado, pero no satisface la prueba de distribución oficial de Apple que el relay espera.
  3. Delegación de identidad del Gateway

    • Antes del registro en el relay, la app obtiene la identidad del Gateway emparejado de gateway.identity.get.
    • La app incluye esa identidad del Gateway en el payload de registro del relay.
    • El relay devuelve un handle de relay y una concesión de envío con ámbito de registro que están delegados a esa identidad de Gateway.
  4. Gateway -> relay

    • El Gateway almacena el handle de relay y la concesión de envío de push.apns.register.
    • En push.test, despertares de reconexión y nudges de despertar, el Gateway firma la solicitud de envío con su propia identidad de dispositivo.
    • El relay verifica tanto la concesión de envío almacenada como la firma del Gateway contra la identidad de Gateway delegada del registro.
    • Otro Gateway no puede reusar ese registro almacenado, incluso si de algún modo obtiene el handle.
  5. Relay -> APNs

    • El relay posee las credenciales APNs de producción y el token APNs crudo para el build oficial.
    • El Gateway nunca almacena el token APNs crudo para builds oficiales respaldados por relay.
    • El relay envía el push final a APNs en nombre del Gateway emparejado.

Por qué se creó este diseño:

  • Para mantener las credenciales APNs de producción fuera de los Gateways de los usuarios.
  • Para evitar almacenar tokens APNs crudos de builds oficiales en el Gateway.
  • Para permitir el uso del relay hospedado solo para builds oficiales/TestFlight de OpenClaw.
  • Para evitar que un Gateway envíe pushes de despertar a dispositivos iOS que pertenecen a un Gateway diferente.

Los builds locales/manuales permanecen en APNs directos. Si estás probando esos builds sin el relay, el Gateway aún necesita credenciales APNs directas:

export OPENCLAW_APNS_TEAM_ID="TEAMID"
export OPENCLAW_APNS_KEY_ID="KEYID"
export OPENCLAW_APNS_PRIVATE_KEY_P8="$(cat /path/to/AuthKey_KEYID.p8)"

Rutas de descubrimiento

Bonjour (LAN)

El Gateway anuncia _openclaw-gw._tcp en local.. La app de iOS las lista automáticamente.

Tailnet (entre redes)

Si mDNS está bloqueado, usa una zona DNS-SD unicast (elige un dominio; ejemplo: openclaw.internal.) y split DNS de Tailscale. Consulta Bonjour para el ejemplo con CoreDNS.

Host/puerto manual

En Ajustes, activa Manual Host e ingresa el host del Gateway + puerto (por defecto 18789).

Canvas + A2UI

El nodo iOS renderiza un canvas WKWebView. Usa node.invoke para controlarlo:

openclaw nodes invoke --node "iOS Node" --command canvas.navigate --params '{"url":"http://<gateway-host>:18789/__openclaw__/canvas/"}'

Notas:

  • El host canvas del Gateway sirve /__openclaw__/canvas/ y /__openclaw__/a2ui/.
  • Se sirve desde el servidor HTTP del Gateway (mismo puerto que gateway.port, por defecto 18789).
  • El nodo iOS auto-navega a A2UI al conectarse cuando se anuncia una URL de host canvas.
  • Vuelve al scaffold integrado con canvas.navigate y {"url":""}.

Canvas eval / snapshot

openclaw nodes invoke --node "iOS Node" --command canvas.eval --params '{"javaScript":"(() => { const {ctx} = window.__openclaw; ctx.clearRect(0,0,innerWidth,innerHeight); ctx.lineWidth=6; ctx.strokeStyle=\"#ff2d55\"; ctx.beginPath(); ctx.moveTo(40,40); ctx.lineTo(innerWidth-40, innerHeight-40); ctx.stroke(); return \"ok\"; })()"}'
openclaw nodes invoke --node "iOS Node" --command canvas.snapshot --params '{"maxWidth":900,"format":"jpeg"}'

Activación por voz + modo conversación

  • La activación por voz y el modo conversación están disponibles en Ajustes.
  • iOS puede suspender el audio en segundo plano; trata las funciones de voz como mejor esfuerzo cuando la app no está activa.

Errores comunes

  • NODE_BACKGROUND_UNAVAILABLE: trae la app de iOS al primer plano (los comandos de canvas/cámara/pantalla lo requieren).
  • A2UI_HOST_NOT_CONFIGURED: el Gateway no anunció una URL de host canvas; verifica canvasHost en Configuración del Gateway.
  • El aviso de emparejamiento nunca aparece: ejecuta openclaw devices list y aprueba manualmente.
  • La reconexión falla tras reinstalar: el token de emparejamiento del Keychain fue eliminado; vuelve a emparejar el nodo.

Documentación relacionada