Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# Changelog

Todos los cambios publicados de **drop**, de más reciente a más antiguo.

El formato sigue [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/) y el versionado es
[SemVer](https://semver.org/lang/es/). Mientras la versión mayor sea `0`, una versión menor
puede romper compatibilidad; cuando pasa, se dice aquí.

Las notas de cada release, con los binarios, están en
[Releases](https://github.com/Oloxx/drop/releases).

## [Sin publicar]

### Añadido
- Tests en Linux, macOS y Windows antes de desplegar a producción, y la suite arranca su propio
servidor de señalización: `npm test` ya no necesita nada levantado a mano (#22, #23).
- `LICENSE` (Apache 2.0), `CONTRIBUTING.md` y este `CHANGELOG.md` (#31).
- Cabeceras de seguridad en todas las respuestas: CSP ajustada a lo que la web usa de verdad,
`X-Content-Type-Options`, `Referrer-Policy` y `Permissions-Policy`; HSTS en Caddy (#30).

### Cambiado
- La versión del CLI sale solo del `package.json` y esbuild la mete en el binario al compilar:
se acabó la constante escrita a mano que descuadraba `drop update` (#26).
- El contenedor corre como el usuario `node`, sin privilegios, y declara un `HEALTHCHECK` (#30).
- Una sola implementación de SHA-256, en `public/shared/sha256.js`, que comparten la web y sus
tests: antes el test validaba una copia del código (#25).

## [0.5.2] — 2026-09-11

### Añadido
- **Releases firmadas con Ed25519** en formato minisign. Cada release lleva `SHA256SUMS` y
`SHA256SUMS.minisig`: el hash dice que el binario llegó entero, la firma dice quién lo
publicó (#16).
- `drop update` **rechaza una release sin firma**; `--allow-unsigned` es el escape para las
anteriores a esta versión.

## [0.5.1] — 2026-09-10

### Añadido
- **TURN efímero**: `/config` firma credenciales que caducan (12 h por defecto) en vez de
repartir un usuario y una contraseña perpetuos, con lo que el relay dejaba de ser propio
(#2).
- **Cuotas en el servidor de señalización**: salas por IP y minuto, receptores por sala, salas
totales, caducidad por inactividad y un cubo de mensajes por socket. `maxPayload` baja de los
100 MiB que trae `ws` por defecto a 256 KiB (#7).
- **Huella corta de sesión (SAS)** para detectar a un intermediario comparando cuatro palabras
por otro canal, y confirmación explícita del emisor antes de servir a un receptor (#14, #15).

### Corregido
- El CLI cierra el descriptor del archivo en `finally`, y deja de avisar de `DEP0137`.

## [0.5.0] — 2026-09-06

### Corregido
- **Path traversal y sobrescrituras en el receptor**: los nombres que llegan en el manifiesto se
sanean antes de tocar el disco, cada archivo se escribe en `.part` y solo se renombra cuando
el SHA-256 cuadra (#17, #18, #19).
- Errores legibles en lugar de volcados de pila: `EADDRINUSE`, código equivocado, versión de
protocolo distinta.

## [0.4.2] — 2026-09-05

### Corregido
- El mapeo UPnP se renueva periódicamente, se limpia al salir y reintenta si el puerto está
cogido.

## [0.4.1] — 2026-09-05

### Corregido
- Path traversal en el receptor del CLI.
- El relay se colgaba al pasar de 8 MB: el receptor no acusaba recibo y el emisor se quedaba
esperando con la ventana llena.

## [0.4.0] — 2026-09-05

### Añadido
- **Códigos de sala memorizables** del tipo `4271-lemon-radar-tiger-orbit`, que se pueden dictar
por teléfono.
- **Releases automáticas** al empujar un tag: los cinco binarios se compilan y **se ejecutan** en
su plataforma nativa antes de publicarse, con checksums y firma ad-hoc en macOS.

### Corregido
- El blob SEA se genera con el Node exacto de los binarios base: mezclarlos producía ejecutables
que morían al arrancar en todas las plataformas a la vez.

## [0.3.5] — 2026-09-04

### Añadido
- **Mapeo automático de puertos por UPnP** y detección de la IP pública, para conexiones
directas por internet sin tocar el router.
- Sondeo concurrente al estilo *Happy Eyeballs*: se prueban en paralelo LAN, VPN y WAN y se
adopta la primera que responde.
- `-p, --port` para fijar el puerto de escucha.

## [0.3.4] — 2026-09-04

### Corregido
- Condiciones de carrera en enlaces de 1 Gbps: cola de mensajes de control persistente para que
el cambio de fase no se pierda bajo carga.

## [0.3.3] — 2026-09-04

### Cambiado
- El medidor de velocidad deja de cifrar el relleno, que era el cuello de botella en Raspberry
Pi y equipos ARM: ahora mide la red y no la CPU.

## [0.3.2] — 2026-09-04

### Añadido
- Broadcast UDP por todas las interfaces, para equipos con varias tarjetas de red.

## [0.3.1] — 2026-09-04

### Corregido
- Solapamiento de texto en la consola al reportar la latencia.

## [0.3.0] — 2026-09-04

### Añadido
- **Verificación de integridad SHA-256** de cada archivo en las dos puntas.
- **`drop speed`**: medidor de velocidad entre dos terminales, con RTT, medición en los dos
sentidos y elección automática entre TCP directo y relay.

## [0.2.4] — 2026-09-03

### Añadido
- Resumen y métricas al terminar una transferencia.

## [0.2.3] — 2026-09-03

### Añadido
- **`drop update`**: comprueba la última release en GitHub y sustituye el binario instalado.
- Los binarios llevan la versión en el nombre.

## [0.2.2] — 2026-09-03

### Cambiado
- El canal del emisor se queda abierto hasta `Ctrl+C`, para que varias personas descarguen a la
vez o una detrás de otra.
- Ejecutar `drop recv` desde `System32` redirige la descarga a la carpeta de descargas del
usuario, en vez de fallar con `EPERM`.

## [0.2.1] — 2026-09-03

### Corregido
- El receptor esperaba un mensaje con otro nombre que el que mandaba el emisor.
- Caída automática a relay por WebSocket cuando la ruta directa no es posible.

## [0.2.0] — 2026-09-03

Primera release con binarios autónomos.

### Añadido
- **CLI** de transferencia P2P por TCP con AES-256-GCM, a 100-115 MB/s.
- Ejecutables para las cinco plataformas, sin Node.js instalado, que se añaden solos al PATH.
- **Interoperabilidad CLI ↔ web**: lo enviado desde la terminal se descarga desde cualquier
navegador.
- Descubrimiento en la red local por broadcast UDP.

[Sin publicar]: https://github.com/Oloxx/drop/compare/v0.5.2...HEAD
[0.5.2]: https://github.com/Oloxx/drop/compare/v0.5.1...v0.5.2
[0.5.1]: https://github.com/Oloxx/drop/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/Oloxx/drop/compare/v0.4.2...v0.5.0
[0.4.2]: https://github.com/Oloxx/drop/compare/v0.4.1...v0.4.2
[0.4.1]: https://github.com/Oloxx/drop/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/Oloxx/drop/compare/v0.3.5...v0.4.0
[0.3.5]: https://github.com/Oloxx/drop/compare/v0.3.4...v0.3.5
[0.3.4]: https://github.com/Oloxx/drop/compare/v0.3.3...v0.3.4
[0.3.3]: https://github.com/Oloxx/drop/compare/v0.3.2...v0.3.3
[0.3.2]: https://github.com/Oloxx/drop/compare/v0.3.1...v0.3.2
[0.3.1]: https://github.com/Oloxx/drop/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/Oloxx/drop/compare/v0.2.4...v0.3.0
[0.2.4]: https://github.com/Oloxx/drop/compare/v0.2.3...v0.2.4
[0.2.3]: https://github.com/Oloxx/drop/compare/v0.2.2...v0.2.3
[0.2.2]: https://github.com/Oloxx/drop/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/Oloxx/drop/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/Oloxx/drop/releases/tag/v0.2.0
113 changes: 113 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Contribuir a drop

Gracias por querer echar una mano. Este documento es lo que hace falta saber para que un
cambio entre sin ida y vuelta: cómo montar el entorno, cómo probar lo que tocas y qué
convenciones sigue el repositorio.

## Montar el entorno

Solo hace falta **Node.js >= 18** (el CI y los binarios usan la 22).

```bash
git clone https://github.com/Oloxx/drop.git
cd drop
npm install

npm run dev # http://localhost:3000, recarga sola al guardar
```

No hay base de datos, ni servicios externos, ni claves que pedir: las salas viven en memoria
del proceso y los archivos nunca pasan por el servidor.

Para probar el CLI sin compilar nada:

```bash
npm run cli -- send fichero.zip
npm run cli -- recv 4271-lemon-radar-tiger-orbit
```

## Tests

```bash
npm test
```

La suite arranca su propio servidor de señalización en un puerto libre y lo apaga al terminar,
así que **no hay que levantar nada antes**. Un caso suelto:

```bash
node --test --test-name-pattern "NOT_FOUND" test/signaling.test.mjs
```

Todo cambio de comportamiento necesita un test. Los que hay ya marcan el tono: cada uno explica
en un comentario **qué se rompió o qué se rompería** si eso dejara de cumplirse, no lo que hace
la línea de abajo.

Si tocas rendimiento (troceado, contrapresión, el bucle de acuses), los números salen de los
benchmarks, y **hay que comparar medianas de tres ejecuciones**: la dispersión entre ejecuciones
es de ~1 MB/s y una sola no dice nada.

```bash
npm run bench # transferencia real entre dos pestañas de Chrome
npm run bench:cli # TCP nativo entre dos procesos del CLI
npm run bench:fanout # cadena de reenvío con varios receptores
```

Los benchmarks necesitan un servidor levantado (`npm run dev`) y, los de navegador, Chrome
instalado (`CHROME_PATH` si no está donde se espera).

## Convenciones

**Idiomas.** El código, los comentarios, los commits, las issues y los PR van en **español**.
Los textos de la interfaz web van en **inglés**, en minúscula y escuetos (`open channel`,
`transmitting…`, `delivered`). No es capricho: mezclarlo ya pasó y quedó a medias.

**Comentarios.** Se comenta el *porqué*, no el *qué*. Un comentario que repite el nombre de la
función sobra; uno que dice "esto no puede ir en el canal directo porque adelanta a los últimos
trozos y cierra el archivo a medias" es el que evita que alguien lo simplifique dentro de seis
meses. Si algo parece rebuscado y no lo es, explica el caso que lo justifica.

**Commits.** [Conventional Commits](https://www.conventionalcommits.org/es/), en español y en
imperativo:

```
fix(cli): cerrar el descriptor en finally para no filtrarlo al fallar

Cuerpo opcional: qué pasaba antes, por qué se arregla así y no de otra
forma. Las referencias a issues, al final.

Cierra #21.
```

Tipos en uso: `feat`, `fix`, `sec`, `refactor`, `test`, `docs`, `build`, `ci`, `chore`.

**Pull requests.** Uno por tema, contra `main`. En el cuerpo: qué problema resuelve, qué se ha
verificado y cómo. Si cierra issues, usa las palabras clave en inglés (`closes #21`) — GitHub no
entiende "cierra #21" y la issue se queda abierta.

El CI ejecuta la suite en Linux, macOS y Windows, y tiene que estar en verde antes de mezclar:
un push a `main` despliega a producción.

**Dependencias.** La web hace **cero peticiones a terceros** y así se queda: la fuente está
servida desde `public/fonts/`, no hay CDN ni analítica. En el servidor hay dos dependencias
(`express` y `ws`) y en el CLI ninguna. Añadir una tiene que justificarse; casi siempre la
respuesta es la biblioteca estándar de Node.

**Antes de tocar el protocolo o la cadena de reenvío**, lee las cabeceras de
[`public/app.js`](public/app.js) y [`cli/src/transfer.js`](cli/src/transfer.js). Documentan las
decisiones que parecen simplificables y no lo son -- por qué los trozos y los mensajes de
control van por canales distintos, por qué un relay tiene que volver a trocear lo que reenvía --
y cada una está ahí porque romperla costó una tarde.

## Seguridad

Si encuentras un fallo con impacto en seguridad, **no abras una issue pública**: escribe por
[advisory privado](https://github.com/Oloxx/drop/security/advisories/new). Lo que trata el
proyecto como parte de su modelo de amenazas está en el README; en resumen: el
servidor no ve los archivos, el token de sala es un secreto que viaja en el fragmento de la URL,
y las releases van firmadas.

## Licencia

Al contribuir aceptas que tu código se publique bajo la [Apache License 2.0](LICENSE), que es la
del proyecto.
11 changes: 11 additions & 0 deletions Caddyfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Caddy pide y renueva el certificado solo. Los WebSockets pasan sin configurar nada.
{$DROP_DOMAIN} {
encode gzip

# HSTS vive aqui y no en la aplicacion porque es lo unico que depende de donde
# acaba el TLS: anunciarlo desde un servidor que tambien habla http (el `npm
# run dev` de cualquiera) dejaria ese localhost inaccesible en el navegador
# durante meses. El resto de cabeceras (CSP, nosniff, Referrer-Policy) las
# pone server/index.js, para que viajen con la aplicacion y se prueben.
#
# Dos anos, subdominios incluidos. Sin `preload`: pedir la lista de precarga
# es un camino de ida, y el dominio tendria que servir HTTPS para siempre.
header Strict-Transport-Security "max-age=63072000; includeSubDomains"

reverse_proxy drop:3000
}
15 changes: 15 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,20 @@ RUN npm ci --omit=dev
COPY server ./server
COPY public ./public

# La imagen trae un usuario `node` sin privilegios que no se estaba usando: el
# proceso corria como root, asi que cualquier ejecucion de codigo dentro del
# contenedor empezaba con todo. Los ficheros se copian antes y quedan de root,
# que es justo lo que se quiere: el servidor solo tiene que leerlos, y no puede
# reescribir su propio codigo.
USER node

EXPOSE 3000

# `/healthz` ya existia y solo lo miraba fly.toml. Aqui hace que Docker sepa si
# el proceso sigue sirviendo, no solo si sigue vivo: un servidor colgado con el
# bucle de eventos bloqueado no responde y el contenedor pasa a `unhealthy`.
# Sin curl en la imagen, se pregunta con el propio Node.
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||3000)+'/healthz').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"

CMD ["node", "--env-file-if-exists=.env", "server/index.js"]
Loading
Loading