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, IP100.116.16.34. - Multi-user: grupos
mmdev+mmpentest, proyecto en/Users/Shared/MercadoMayor, postgrespentest_roread-only, wrappersmm-restart-*, scriptsmm-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:
- 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).
- Mandarle
Instructivos/desarrollador.mdopentester.mdsegú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:
- https://login.tailscale.com/admin/machines →
mm-testserver→ "Share..." - Pegás el email → "Generate share link" → mandás ese link.
- Al teammate le aparece
mm-testserversin 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:
- Agregar rewrite en
frontend/next.config.js:js async rewrites() { return [{ source: '/api/backend/:path*', destination: 'http://localhost:3002/:path*' }] } - Cambiar el cliente para usar
/api/backend/*en lugar dehttp://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/
~/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í | sí (NOPASSWD) | sí (NOPASSWD) |
sudo arbitrario |
sí | no | no |
Postgres cargo_db |
postgres (superuser) |
mmdev_rw (R/W sin DDL) |
pentest_ro (SELECT) |
| Docker CLI directo | sí | no | no |
| psql (cliente postgres) | sí | sí | sí |
Leer /Users/mercadomayormacmini (tu home) |
sí | no | no |
| Instalar brew / npm global | sí | no | no |
| Modificar LaunchAgents | sí | no | no |
Passwords postgres de testing (documentadas, no son secretas):
- mmdev_rw → mmdev_rw_local (R/W sobre tablas, sin DDL)
- pentest_ro → pentest_ro_local (SELECT only)
- postgres (superuser, solo admin) → postgres (viene del .env)
Secretos importantes¶
- Los
.envde 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
.envcorrespondientes — verMercadoMayor/SECRETS.md. - La SSH key del server a GitHub (
~/.ssh/id_ed25519_github) es de la cuentaRoyalNoa. Si alguna vez cambiás de cuenta, regenerala y actualizá el remote en los repos. - Postgres
pentest_ropassword (documentada, no es secreta):pentest_ro_local. Solo tiene SELECT, nadie puede escribir con eso.