---
name: wg_client_install
kind: function
lang: bash
domain: infra
version: "1.0.0"
purity: impure
signature: "wg_client_install(config_path_or_stdin, [interface_name]) -> json"
description: "Device-side: instala wg0.conf en /etc/wireguard/, habilita systemd wg-quick@wg0, verifica handshake con hub. Idempotente. Acepta config por path o stdin (para pipes desde wg_client_config)."
tags: [wireguard, client, install, mesh, systemd]
uses_functions: [wg_install_bash_infra]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
params:
  - name: config_path_or_stdin
    desc: "path al archivo .conf existente, o '-' para leer de stdin (compatible con pipe desde wg_client_config)"
  - name: interface_name
    desc: "nombre de la interfaz WireGuard (default: wg0). Determina /etc/wireguard/<iface>.conf y la unit systemd wg-quick@<iface>"
output: "JSON {status, interface, hub_endpoint, handshake_seen}. status: installed | already-configured | installed-no-handshake | installed-no-systemd"
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/infra/wg_client_install.sh"
---

## Ejemplo

```bash
source bash/functions/infra/wg_client_install.sh

# Desde pipe (caso más común en flow 0009):
wg_client_config_go_infra | jq -r '.INI' | wg_client_install -
# {"status":"installed","interface":"wg0","hub_endpoint":"203.0.113.1:51820","handshake_seen":true}

# Desde archivo .conf generado previamente:
wg_client_install /tmp/peer_laptop.conf
# {"status":"installed","interface":"wg0","hub_endpoint":"203.0.113.1:51820","handshake_seen":true}

# Con interfaz personalizada:
wg_client_install /tmp/peer_laptop.conf wg1
# {"status":"installed","interface":"wg1","hub_endpoint":"203.0.113.1:51820","handshake_seen":true}

# Segunda ejecución con misma config (idempotente):
wg_client_install /tmp/peer_laptop.conf
# {"status":"already-configured","interface":"wg0","hub_endpoint":"203.0.113.1:51820","handshake_seen":false}
```

## Cuando usarla

Cuando necesites conectar un nuevo peer al mesh WireGuard en el flow 0009. Úsala justo después de `wg_client_config` (que genera el .conf) para instalarlo en el device peer. Es el paso final del onboarding de un nodo: config generada → instalada → verificada con handshake.

## Gotchas

- **Requiere root/sudo** para escribir en `/etc/wireguard/`, hacer `chmod 600`, y ejecutar `systemctl`. El operador debe tener `sudo` sin password para estos comandos, o ejecutar la función como root.
- **Idempotente por contenido**: si `/etc/wireguard/<iface>.conf` ya existe con el mismo contenido, retorna `status=already-configured` sin tocar nada. Si el contenido difiere, hace backup automático con timestamp antes de sobreescribir.
- **NetworkManager**: si NM gestiona la interfaz wg0, `wg-quick` puede fallar con conflicto. Solución: crear `/etc/NetworkManager/conf.d/99-wg.conf` con `[keyfile]\nunmanaged-devices=interface-name:wg0` y reiniciar NM antes de ejecutar esta función.
- **WSL2 sin systemd** (variantes antiguas o sin `/etc/wsl.conf` con `[boot] systemd=true`): `systemctl` no está disponible. La función detecta esto, emite `status=installed-no-systemd` con instrucciones en stderr para levantar la interfaz manualmente con `sudo wg-quick up wg0`. Para autostart en WSL2 sin systemd: añadir `sudo wg-quick up wg0` al final de `~/.bashrc`.
- **WSL2 con systemd**: kernel WSL2 >= 5.6 (default en distros recientes) incluye WireGuard built-in. Habilitar systemd en WSL2 con `[boot]\nsystemd=true` en `/etc/wsl.conf` y reiniciar WSL. Luego esta función funciona igual que en Linux nativo.
- **Android / Termux**: NO usar esta función. Termux no tiene systemd ni `/etc/wireguard/`. En Android usar la app WireGuard oficial (F-Droid / Play Store) e importar el .conf generado por `wg_client_config` directamente desde la app.
- **handshake_seen=false con status=installed-no-handshake**: la interfaz está activa pero el hub no ha respondido en 10s. No es un error fatal — puede tardar más si el hub está ocupado o hay NAT traversal pendiente. Verificar: endpoint accesible por UDP, hub corriendo con `wg show`, claves public/preshared coincidentes.
- Los logs van siempre a stderr con prefijo `[wg_client_install]`; stdout es exclusivamente el JSON de resultado.

## Capability growth log

<!-- Rellenar solo cuando haya version bump real -->
