Saltar a contenido

Instructivo — Admin del server (Lucas)

Vos sos el único con acceso físico al mm-testserver y sos quien da/saca acceso a los demás. Este instructivo es tu runbook.

Tu setup (ya hecho, para referencia)

  • FileVault deshabilitado (para que reinicie sin pedir password).
  • Auto-login al usuario mercadomayormacmini.
  • SSH server (Sesión remota) activado.
  • Firewall de macOS activado.
  • Displaysleep 10 min, systemsleep nunca, autorestart on power loss.
  • Node 24 (fnm + brew), Python 3.12, OrbStack, Terraform, Playwright instalados.
  • SSH key del server cargada a GitHub (RoyalNoa).
  • Tailscale logueado al tailnet tail52b275.ts.net, IP 100.116.16.34.
  • Multi-user: grupos mmdev + mmpentest, proyecto en /Users/Shared/MercadoMayor, postgres pentest_ro read-only, wrappers mm-restart-*, scripts mm-add-teammate / mm-remove-teammate.
  • LaunchAgents auto-start: orbstack, bootstrap (postgres+redis), backend, frontend, ai.

Dar acceso a un teammate

El dev/pentester te manda: 1. Su clave SSH pública (cat ~/.ssh/id_ed25519.pub en su laptop) — una línea que empieza con ssh-ed25519 AAAA.... 2. Qué username quiere tener en el server (ej. juan-dev, luis-pt).

Vos corrés un solo comando:

```bash mm-add-teammate juan-dev dev 'ssh-ed25519 AAAA... juan@laptop'

o

mm-add-teammate luis-pt pentest 'ssh-ed25519 AAAA... luis@mac' ```

Te pide tu password sudo una vez. Después:

  1. Invitarlo al tailnet por email en https://login.tailscale.com/admin/users/invites (o hacer "Share" del mm-testserver si es un email fuera de tu dominio — ver más abajo).
  2. Mandarle Instructivos/desarrollador.md o pentester.md según rol.

Revocar acceso

bash mm-remove-teammate juan-dev

Borra el user Unix y su home (te confirma antes). Después: 1. Tailscale: https://login.tailscale.com/admin/machines → si compartiste un device, sacalo del "Sharing" en "..." → "Stop sharing". 2. Si era miembro del tailnet: https://login.tailscale.com/admin/users → "Suspend" o "Delete".

Invitar a alguien fuera de tu dominio

Tailscale free plan no deja invitar cross-domain como miembros. Para @gmail.com u otros:

  1. https://login.tailscale.com/admin/machines → mm-testserver → "Share..."
  2. Pegás el email → "Generate share link" → mandás ese link.
  3. Al teammate le aparece mm-testserver sin ver el resto de tu tailnet.

Los links caducan a 30 días si no se usan, pero el acceso una vez aceptado es permanente hasta que lo revoques.

Cloudflare Tunnel para UX reviewers externos

Para mostrarle la app a alguien que no puede/quiere instalar Tailscale (cliente, diseñador, stakeholder). cloudflared ya está instalado y hay un wrapper listo:

bash mm-share # default: frontend (localhost:3000) mm-share backend # puerto 3002 mm-share ai # puerto 8010 mm-share 5555 # puerto arbitrario

El comando: - Arranca un tunnel efímero de Cloudflare (sin cuenta, sin dominio, sin auth). - Imprime una URL pública https://xxx.trycloudflare.com que cualquiera con el link puede abrir. - Loggea la sesión en /Users/Shared/MercadoMayor/logs/tunnel.log (quién abrió qué y cuándo). - Se cierra con Ctrl+C → la URL queda muerta.

Caveat importante del frontend: el CSP del Next.js tiene connect-src http://localhost:3002 hardcoded. Un reviewer externo verá la UI cargar pero los API calls del browser van a fallar porque su browser no puede llegar a localhost:3002 (es el servidor). Para fix permanente hay que:

  1. Agregar rewrite en frontend/next.config.js: js async rewrites() { return [{ source: '/api/backend/:path*', destination: 'http://localhost:3002/:path*' }] }
  2. Cambiar el cliente para usar /api/backend/* en lugar de http://localhost:3002/*.

Hasta que ese cambio esté hecho, mm-share sirve para revisión visual / marketing pages, no para flujos completos.

Si querés gated por email (Cloudflare Access) o URL estable, se requiere una cuenta Cloudflare, un dominio en su DNS y un named tunnel — no está armado porque mezclaría con el dominio productivo *.mercadomayor.com.ar. Si lo necesitás, avisame y lo armamos sobre un dominio de testing separado.

Mantenimiento común

Auto-start y restart (LaunchAgents)

Los 3 servicios + OrbStack + docker-compose corren como LaunchAgents con KeepAlive: si crashean, macOS los reinicia solo; si reiniciás la Mac, arrancan solos en ~40s.

```bash

Ver estado

launchctl list | grep mercadomayor

Restart de un servicio puntual (vos como admin)

launchctl kickstart -k gui/$(id -u)/com.mercadomayor.backend launchctl kickstart -k gui/$(id -u)/com.mercadomayor.frontend launchctl kickstart -k gui/$(id -u)/com.mercadomayor.ai

Logs en vivo

tail -f /Users/Shared/MercadoMayor/logs/backend.log tail -f /Users/Shared/MercadoMayor/logs/frontend.log tail -f /Users/Shared/MercadoMayor/logs/ai.log tail -f /Users/Shared/MercadoMayor/logs/mm-bootstrap.log ```

Los plists viven en ~/Library/LaunchAgents/com.mercadomayor.*.plist y los scripts wrappers en ~/Library/Application Support/MercadoMayor/scripts/.

Venv de Python en ~/Library/ (no junto al código): TCC (sandbox de macOS) bloquea a los LaunchAgents leer pyvenv.cfg desde ~/Desktop. Node/npm no disparan ese check. Si instalás un paquete nuevo, hacelo contra ese venv:

bash "$HOME/Library/Application Support/MercadoMayor/venv/bin/pip" install <paquete>

Restart de la BD

bash cd /Users/Shared/MercadoMayor/backend docker-compose restart postgres redis

Re-cargar datos de testing

Si alguien ensució la BD y queda inusable:

bash ~/Desktop/Main/scripts/bootstrap-db.sh

Dropea cargo_db, recrea schema + placeholders + postgres pentest_ro, y restaura el dump que vive en ~/Desktop/Main/data/mm_backup_*.dump. Termina en ~30 seg.

Spotlight (importante tras moves grandes)

Si movés o renombrás el proyecto, Spotlight re-indexa node_modules y la Mac se vuelve lenta 5-15 min. Para evitarlo:

bash touch /Users/Shared/MercadoMayor/node_modules/.metadata_never_index touch /Users/Shared/MercadoMayor/backend/node_modules/.metadata_never_index touch /Users/Shared/MercadoMayor/frontend/node_modules/.metadata_never_index touch /Users/Shared/MercadoMayor/frontend/.next/.metadata_never_index

Ya hecho — solo si agregás otros node_modules en el futuro.

Ver quién está conectado por SSH

bash who last | head -20

Logs de auth SSH

bash log show --predicate 'process == "sshd"' --last 1h

Si reiniciás la Mac

Auto-login entra al usuario mercadomayormacmini, OrbStack arranca solo (LaunchAgent com.mercadomayor.orbstack), el LaunchAgent com.mercadomayor.bootstrap levanta postgres + redis via docker-compose, y los LaunchAgents backend/frontend/ai arrancan sus servicios en paralelo con KeepAlive. Todo listo en ~40 segundos después del boot, sin intervención manual.

Si algo no levantó:

bash tail -50 /Users/Shared/MercadoMayor/logs/mm-bootstrap.log launchctl list | grep mercadomayor # ver estados y LastExitStatus

Estructura multi-user (para que te orientes)

``` /Users/Shared/MercadoMayor/ # proyecto, owned por mercadomayormacmini:mmdev ├── backend/ # Node + TS ├── frontend/ # Next 16 ├── ai-service/ # Python (venv vive aparte en Library) ├── logs/ # logs de los servicios, mmdev RW, mmpentest R ├── node_modules/ # hoisted (npm workspaces), .metadata_never_index └── Instructivos/ # estos mismos docs

/Users// # home propio por cada teammate ├── .ssh/authorized_keys # su clave SSH └── .zshrc # alias mm-cd, mm-logs

~/Desktop/Main/MercadoMayor -> /Users/Shared/MercadoMayor (symlink para vos) ~/Desktop/Main/logs -> /Users/Shared/MercadoMayor/logs (symlink) ~/Desktop/Main/System/ (admin del server: provisioning + users) ~/Desktop/Main/scripts/ (operación del producto: start/stop/bootstrap-db) ~/Desktop/Main/data/ (dumps de BD, no código) ~/Library/LaunchAgents/com.mercadomayor..plist (5 agents) ~/Library/Application Support/MercadoMayor/scripts/ (wrappers de los agents) ~/Library/Application Support/MercadoMayor/venv/ (venv python) /usr/local/bin/mm-{restart-,add-teammate,remove-teammate} (comandos accesibles a todos) /etc/sudoers.d/mercadomayor (NOPASSWD para mm-restart-* y mm-{add,remove}-teammate) ```

Matriz de permisos por rol

Acción admin mmdev mmpentest
Leer código en /Users/Shared/MercadoMayor RW RW R
Leer logs RW RW R
Reiniciar servicios (sudo mm-restart-*) sí (NOPASSWD) sí (NOPASSWD)
sudo arbitrario no no
Postgres cargo_db postgres (superuser) mmdev_rw (R/W sin DDL) pentest_ro (SELECT)
Docker CLI directo no no
psql (cliente postgres)
Leer /Users/mercadomayormacmini (tu home) no no
Instalar brew / npm global no no
Modificar LaunchAgents no no

Passwords postgres de testing (documentadas, no son secretas): - mmdev_rwmmdev_rw_local (R/W sobre tablas, sin DDL) - pentest_ropentest_ro_local (SELECT only) - postgres (superuser, solo admin) → postgres (viene del .env)

Secretos importantes

  • Los .env de los servicios tienen JWT/cert keys random solo para testing. Son los únicos que pueden quedar en el disco.
  • API keys de OpenAI / Mercado Pago / Resend no están cargadas. Si las pedís (usando cuentas de Mercado Mayor SAS), ponelas en los .env correspondientes — ver MercadoMayor/SECRETS.md.
  • La SSH key del server a GitHub (~/.ssh/id_ed25519_github) es de la cuenta RoyalNoa. Si alguna vez cambiás de cuenta, regenerala y actualizá el remote en los repos.
  • Postgres pentest_ro password (documentada, no es secreta): pentest_ro_local. Solo tiene SELECT, nadie puede escribir con eso.