RedCheck, código abierto
Por qué elegí AGPLv3, y todo lo que tuvo que cambiar en el código antes de que lo pudieran leer desconocidos.
At the beginning of September, RedCheck’s repositories went public. The backend, the AI engine, the frontend and the landing page can now be read by anyone on GitHub.
Flipping the visibility switch takes one click. Getting to the point where I was comfortable flipping it took a couple of days of work that had nothing to do with features, and taught me more about my own code than most features do.

Why open source at all
Three reasons, in order of honesty:
- It’s my portfolio. I’m looking for my first role as an engineer. “I built a production app with an AI service” is a claim. A public repository is evidence.
- It changes how I write code. Knowing someone might read a file makes you name things better, delete dead code and write the comment that explains why. That effect starts the moment you decide to go public, not when someone actually reads it.
- It’s a productivity tool. People are understandably careful about where their task data goes. Being able to point at the code that handles it is worth something.
Why AGPLv3 and not MIT
MIT was the default I considered first. It’s simple and lets anyone do almost anything. The problem is the “almost anything” part for a web application: someone could take RedCheck, modify it, run it as a hosted service and never share a line of their changes. Under most licences that’s fine, because they never distribute the software, they just run it.
The GNU Affero GPL closes exactly that gap. If you run a modified version as a network service, you have to offer its source to the users of that service. For a self-hostable SaaS that’s the property I want: anyone can learn from it, self-host it or contribute to it, but nobody can quietly turn it into a closed competitor.
The trade-off is real. Some companies have a blanket policy against AGPL code, so it reduces how much it’ll be reused commercially. For this project, that’s acceptable.
Public release prep: the checklist
Before anything went public, I went through each repository as if I were a reviewer seeing it for the first time.
Secrets and configuration
- The JWT signing key was hardcoded in the source. It’s now read from an environment variable. Moving it isn’t enough on its own, though: the old value is still in the git history, which is about to be public. A secret that has ever been committed has to be treated as leaked and never used again.
- Database credentials, the AI engine URL and the allowed origins lost their convenient defaults in the configuration file. If a variable is missing, the app should fail at startup, not quietly connect to something with a default password.
- Hibernate’s schema mode now defaults to
validateinstead ofupdateunless an environment explicitly asks for more.
CORS
Every controller had @CrossOrigin(origins = "*"), left over from the
day the React app first needed to call the API. Each one now reads the
allowed origins from configuration. Allowing any origin is a habit that
makes local development easy and is easy to forget, so I went through
every controller rather than trusting a global setting.
Code a stranger can read
- Request and response DTOs became Java
records: immutable, with no Lombok boilerplate, and the intent is obvious at a glance. System.out.printlnbecame proper logging.- Entity
equals,hashCodeandtoStringnow exclude lazy relationships like the owning user. Including them is a classic way to trigger unexpected database loads or infinite recursion between two entities that reference each other. - Developer comments and log messages, a lot of them in Spanish, were translated to English. The user-facing text stayed bilingual, of course.
- Build output had been committed to the repository at some point. It’s
gone, along with stale files, and
.gitignorewas cleaned up.
The frontend’s turn
The frontend had 24 ESLint errors and 11 TypeScript errors. Getting both to zero wasn’t cosmetic: one of them was a real bug, a function typed as returning a single object while every caller treated the result as an array. Strict tooling only helps if it’s actually green.
The same pass split the single 697 kB JavaScript bundle into per-route chunks and lazy-loaded the modals that only appear after a deliberate click. The dashboard’s own chunk went from 121 kB to 84 kB, and someone who goes straight to the dashboard no longer downloads the login page’s animated background.
Docs for humans
Each repository now has a README with an architecture diagram, setup
instructions and the licence, plus a CONTRIBUTING.md explaining the
standards and the pull-request workflow.
What I took away
- Pick the licence for how your software is used, not just how it’s distributed. For a web service, AGPL and MIT protect very different things.
- Treat any secret that was ever committed as public, even in a private repository.
- Defaults are decisions. A “convenient” default password is a vulnerability that looks like a feature.
- The best code review I’ve done on this project was the one where I imagined someone else reading it.
The code is at github.com/redcheckapp. Issues, suggestions and pull requests are welcome.
A principios de septiembre, los repositorios de RedCheck pasaron a ser públicos. El backend, el motor de IA, el frontend y la landing los puede leer cualquiera en GitHub.
Cambiar la visibilidad es un clic. Llegar al punto de estar tranquilo haciendo ese clic me llevó un par de días de trabajo que no tenían nada que ver con funcionalidades, y me enseñaron más sobre mi propio código que la mayoría de funcionalidades.

Por qué abrir el código
Tres razones, ordenadas de más a menos sincera:
- Es mi portfolio. Estoy buscando mi primer puesto como ingeniero. “He construido una app en producción con un servicio de IA” es una afirmación. Un repositorio público es una prueba.
- Cambia cómo escribo código. Saber que alguien puede leer un fichero hace que nombres mejor las cosas, borres el código muerto y escribas el comentario que explica el porqué. Ese efecto empieza en cuanto decides publicarlo, no cuando alguien lo lee de verdad.
- Es una herramienta de productividad. Es lógico que la gente mire con cuidado a dónde van sus tareas. Poder señalar el código que las gestiona vale algo.
Por qué AGPLv3 y no MIT
MIT fue lo primero que consideré. Es sencilla y deja a cualquiera hacer casi cualquier cosa. El problema es ese “casi cualquier cosa” en una aplicación web: alguien podría coger RedCheck, modificarlo, ofrecerlo como servicio y no compartir ni una línea de sus cambios. Con la mayoría de licencias eso está permitido, porque nunca distribuyen el software, solo lo ejecutan.
La GNU Affero GPL cierra exactamente ese hueco. Si ofreces una versión modificada como servicio en red, tienes que ofrecer su código a los usuarios de ese servicio. Para un SaaS que se puede autoalojar, es la propiedad que quiero: cualquiera puede aprender de él, alojarlo o contribuir, pero nadie puede convertirlo discretamente en un competidor cerrado.
El coste es real. Algunas empresas tienen una política general en contra del código AGPL, así que se reutilizará menos comercialmente. Para este proyecto, es aceptable.
Preparar la publicación: la lista
Antes de publicar nada, repasé cada repositorio como si fuera un revisor que lo ve por primera vez.
Secretos y configuración
- La clave para firmar los JWT estaba escrita en el código. Ahora se lee de una variable de entorno. Pero moverla no basta: el valor antiguo sigue en el historial de git, que está a punto de hacerse público. Un secreto que ha estado alguna vez en un commit hay que darlo por filtrado y no volver a usarlo jamás.
- Las credenciales de la base de datos, la URL del motor de IA y los orígenes permitidos perdieron sus valores por defecto “cómodos” en el fichero de configuración. Si falta una variable, la app debe fallar al arrancar, no conectarse en silencio a algo con una contraseña por defecto.
- El modo de esquema de Hibernate pasa a ser
validatepor defecto en lugar deupdate, salvo que un entorno pida explícitamente otra cosa.
CORS
Todos los controladores tenían @CrossOrigin(origins = "*"), herencia
del día en que la app de React necesitó llamar a la API por primera vez.
Ahora cada uno lee los orígenes permitidos de la configuración. Permitir
cualquier origen es una costumbre que facilita el desarrollo en local y
es fácil de olvidar, así que repasé los controladores uno a uno en lugar
de fiarme de un ajuste global.
Código que pueda leer un desconocido
- Los DTO de petición y respuesta pasaron a ser
recordde Java: inmutables, sin el boilerplate de Lombok y con la intención clara a primera vista. System.out.printlnpasó a ser logging de verdad.equals,hashCodeytoStringde las entidades ahora excluyen las relaciones perezosas, como el usuario propietario. Incluirlas es una forma clásica de provocar cargas inesperadas en la base de datos o una recursión infinita entre dos entidades que se referencian.- Los comentarios y mensajes de log, muchos en español, se tradujeron al inglés. El texto que ve el usuario sigue siendo bilingüe, claro.
- En algún momento se habían subido al repositorio los ficheros
compilados. Ya no están, igual que otros ficheros obsoletos, y el
.gitignorequedó limpio.
Le toca al frontend
El frontend tenía 24 errores de ESLint y 11 de TypeScript. Dejar ambos a cero no fue cosmético: uno era un bug real, una función tipada como si devolviera un objeto mientras todos sus usos trataban el resultado como un array. Las herramientas estrictas solo ayudan si están en verde.
En la misma pasada, el único bundle de JavaScript de 697 kB se dividió en chunks por ruta, y los modales que solo aparecen tras un clic deliberado pasaron a cargarse bajo demanda. El chunk del dashboard pasó de 121 kB a 84 kB, y quien entra directamente al dashboard ya no descarga el fondo animado de la pantalla de login.
Documentación para personas
Cada repositorio tiene ahora un README con un diagrama de arquitectura,
instrucciones de instalación y la licencia, además de un
CONTRIBUTING.md que explica los estándares y el flujo de pull requests.
Con qué me quedo
- Elige la licencia según cómo se usa tu software, no solo cómo se distribuye. Para un servicio web, AGPL y MIT protegen cosas muy distintas.
- Trata como público cualquier secreto que haya estado en un commit, aunque el repositorio fuera privado.
- Los valores por defecto son decisiones. Una contraseña por defecto “cómoda” es una vulnerabilidad con aspecto de funcionalidad.
- La mejor revisión de código que he hecho en este proyecto fue la que hice imaginando que la leía otra persona.
El código está en github.com/redcheckapp. Issues, sugerencias y pull requests son bienvenidos.
- Backend
- DevOps