Lectura de placas con Bun: la lección del youtuber vigilado
Un youtuber montó una cámara que lee placas de autos, al estilo de las que Flock Safety instala por todo Estados Unidos. Solo que él apuntó la cámara hacia la policía. Poco después, según él, unos policías aparecieron en su puerta. La historia salió en Gizmodo y me hizo pensar en algo muy práctico: hoy, montar lectura de placas con Bun, una cámara barata y un modelo de OCR es un proyecto de fin de semana. Y justamente ese es el problema.
Qué pasó, en pocas líneas
Flock vende cámaras de lectura automática de placas (ALPR, por sus siglas en inglés) a municipios, policías y hasta condominios. La cámara fotografía cada auto que pasa, extrae la placa y lo manda todo a una base consultable. Ya son miles de ciudades usándolas, y organizaciones como la ACLU critican el modelo desde hace años: mucha gente con acceso, reglas de retención poco claras y casi nada de transparencia.
El youtuber decidió darle la vuelta al juego. Montó su propio equipo, reconoció patrullas y registró por dónde pasaban. O sea, hizo con la policía exactamente lo que la policía hace con todo el mundo. La reacción llegó rápido.
No voy a entrar en el fondo jurídico del caso, que depende del estado y de detalles que el relato no aclara. Lo que me interesa es otra cosa: la herramienta es la misma en los dos lados. Solo cambia quién está frente al lente.
Cuando el vigilado monta la misma cámara, de repente todo el mundo descubre que existe la privacidad.
Por qué esto le importa a quien escribe código
Porque la parte difícil de este sistema dejó de ser difícil. Hace diez años, ALPR exigía hardware dedicado y software caro. Hoy tienes:
- una cámara IP o una Raspberry Pi con módulo de cámara;
- un modelo open source de detección de placas;
- un backend que recibe las lecturas y las guarda en una base de datos.
Esa última parte es lo que cualquier dev full stack hace cada semana. Un endpoint, un insert, una búsqueda por placa. Yo escribiría esto en Bun en media hora, casi sin instalar nada, porque el runtime ya trae servidor HTTP, SQLite y crypto de fábrica.
Y ahí está la lección. El código de vigilancia es idéntico al código de una app de estacionamiento, de un control de acceso, de un sistema de flotas. La diferencia está en las decisiones que tomas sobre los datos.
Cómo queda la lectura de placas con Bun, de forma responsable
Voy a mostrar el backend que yo haría para un caso legítimo: un estacionamiento que necesita saber si el auto que entró ya salió. Nada de rastrear personas. La idea es ver dónde están las decisiones que separan un sistema útil de una máquina de vigilancia.
import { Database } from "bun:sqlite";
import { createHmac } from "node:crypto";
const db = new Database("lecturas.db");
db.run(`CREATE TABLE IF NOT EXISTS lecturas (
placa_hash TEXT NOT NULL,
porton TEXT NOT NULL,
leido_en INTEGER NOT NULL
)`);
const SECRETO = process.env.PLACA_SECRET!;
const RETENCION_MS = 72 * 60 * 60 * 1000; // 72 horas y se acabó
const hash = (placa: string) =>
createHmac("sha256", SECRETO).update(placa.toUpperCase()).digest("hex");
Bun.serve({
routes: {
"/lectura": {
POST: async (req) => {
const { placa, porton } = await req.json();
db.run("INSERT INTO lecturas VALUES (?, ?, ?)", [hash(placa), porton, Date.now()]);
return new Response(null, { status: 204 });
},
},
},
});
setInterval(() => {
db.run("DELETE FROM lecturas WHERE leido_en < ?", [Date.now() - RETENCION_MS]);
}, 60 * 60 * 1000);
Hay tres decisiones en este fragmento, y ninguna es realmente técnica:
- La placa no se guarda en texto plano. Va como HMAC con un secreto. Se puede comprobar "¿este auto entró y salió?", pero nadie que filtre la base puede listar placas.
- El dato tiene fecha de caducidad. 72 horas y se borra. Un sistema de estacionamiento no necesita recordar dónde estaba tu auto en marzo.
- No hay endpoint de búsqueda. Si nadie pidió "historial de por dónde pasó la placa X", esa ruta no existe. Lo que no existe no se filtra ni se puede requerir judicialmente.
Fíjate que el código queda más corto con estas decisiones. Aquí la privacidad no es una feature extra, es recorte de alcance.
"Pero el hash de la placa no resuelve nada, se puede hacer fuerza bruta"
Es una buena objeción, y tiene parte de razón. Las placas tienen un formato conocido. En Brasil, el estándar Mercosur tiene algo del orden de 450 millones de combinaciones. Con SHA-256 puro, una GPU doméstica prueba todo eso en minutos.
Por eso el ejemplo usa HMAC con secreto, y no un hash simple. Sin la clave, la fuerza bruta no sirve de nada. Con la clave, el atacante ya tiene acceso al servidor y tú tienes problemas mayores. Si quieres ir más allá, guarda el secreto fuera de la máquina (en un gestor de secretos) y rota la clave junto con el ciclo de retención. Así las lecturas antiguas se vuelven imposibles de correlacionar.
No es perfecto. Pero es mucho mejor que el estándar del mercado, que suele ser placa en texto plano, foto del auto y retención de 30 días o más, "por si acaso".
Lo que deja en evidencia el caso del youtuber
Lo que más me llamó la atención de la historia no fue la visita de la policía. Fue la incomodidad. Cuando alguien monta el mismo sistema y apunta a quien normalmente opera este tipo de herramienta, la sensación de invasión aparece al instante. La tecnología es la misma. La molestia es la misma. Solo que de un lado se vuelve noticia y del otro se vuelve contrato público.
Para nosotros, los devs, esto deja una pregunta incómoda, pero útil para revisar cualquier sistema que guarda datos de ubicación:
- ¿Estaría tranquilo si esta base tuviera mi historial de desplazamientos?
- ¿Quién puede ejecutar un
SELECTaquí sin pedirle permiso a nadie? - Si se filtra mañana, ¿qué se puede descubrir sobre una persona concreta?
Si la respuesta a la primera es "no", estás construyendo una Flock en miniatura. Y no necesitas estar en un proyecto de cámaras para caer en esto. App de delivery, app de envíos, rastreador de flotas, check-in del gimnasio: todos generan un rastro de ubicación. Y casi todos guardan más de lo que necesitan.
Qué cambia en tu flujo de trabajo
En la práctica, me llevo tres hábitos de este caso a cualquier proyecto:
- La retención entra en el schema, no en el backlog. Columna de fecha y job de limpieza en el mismo PR que crea la tabla. Si queda para después, nunca pasa.
- El dato sensible se convierte en un identificador opaco lo antes posible. HMAC en la entrada, no en un script de "anonimización" que corre una vez por trimestre.
- Toda ruta de búsqueda necesita un motivo escrito. Si el motivo es "puede ser útil algún día", no entra.
Bun ayuda aquí de una forma medio aburrida: como SQLite, servidor y crypto ya vienen en el runtime, quedan menos excusas del tipo "después lo configuro". El ejemplo de arriba no tiene ninguna dependencia que instalar. Se puede hacer bien desde el primer commit.
Mi opinión
Creo que el youtuber hizo, sin querer, la mejor demostración posible de lo que venimos diciendo sobre ALPR: el problema nunca fue la cámara, fue la base de datos detrás de ella. Y quienes diseñamos esa base somos nosotros. La policía, Flock o el chico de YouTube solo eligen hacia dónde apuntar.
Así que la próxima vez que crees una tabla con placa, número de documento o coordenadas, piensa en este caso unos diez segundos. Fecha de caducidad, identificador opaco, nada de búsquedas sin motivo. Cuesta unas veinte líneas. Si quieres ver cómo aplico esto en proyectos reales, échale un vistazo a mis proyectos.
Resumen para LinkedIn
Un youtuber apuntó una cámara de lectura de placas hacia la policía. Poco después, según él, la policía tocó a su puerta. La herramienta es la misma en los dos lados. Solo cambia quién está frente al lente. Hoy, montar lectura de placas es un proyecto de fin de semana. El backend es un endpoint, un insert y una búsqueda. En Bun lo hago en media hora. Por eso el problema nunca fue la cámara. Es la base de datos que está detrás, y quienes diseñamos esa base somos nosotros. Tres hábitos que llevo a cualquier proyecto con placas, documentos de identidad o ubicación: el plazo de retención entra en el schema, el dato sensible se convierte en HMAC apenas entra y ninguna ruta de búsqueda entra sin un motivo escrito. Aquí la privacidad no es una feature extra. Es recorte de alcance. Escribí el artículo completo con el código. Si este tema te interesa, vale la pena leerlo. #Privacidad #DesarrolloDeSoftware #Bun #ProtecciónDeDatos #SeguridadDeLaInformación