whoami

Juan Rodríguez — Full-stack development

Every project here started as a challenge I did not know how to solve. The excuse to learn it in depth is to build it: from the API to the server. I use AI to speed up my research, but the design, the architecture and the deployment are mine from start to finish.

cat skills.txt

frontend/
React TypeScript Astro Vite HTML/CSS Accessibility
backend/
Python FastAPI SQLAlchemy PostgreSQL Alembic REST
infra/
Docker Docker Compose Caddy nginx Linux Cloudflare Prometheus / Grafana
security/
OAuth/JWT Argon2id CSRF CSP UFW / fail2ban

ls ./projects

View projects Contact me

grep --tag

memories

2026

Private app for shared memories, with a gallery of 500+ photos.

A private SPA where photos and moments are organized by sections. There is no public sign-up: accounts are created through a CLI, a deliberate decision to avoid exposing unnecessary write endpoints. The gallery is built for volume: it mounts photos in batches as you scroll down and handles mixes of portrait and landscape images without cropping anything that matters.

  • Session only in HttpOnly cookies, with double-submit CSRF and refresh token rotation: reusing a revoked one closes all sessions.
  • Passwords hashed with Argon2id, rate limiting, and an identical response for a nonexistent email or a wrong password, so users cannot be enumerated.
  • Incremental gallery with IntersectionObserver, and a theme system whose test rebuilds the animated background to verify WCAG AA contrast.
  • Asynchronous backend end to end with SQLAlchemy 2.0 and asyncpg, with Alembic migrations.
  • Role model (admin/guest/love) stored as VARCHAR + CHECK instead of a native Postgres ENUM: adding or removing a role is a regular migration, not an ALTER TYPE.
  • React
  • TypeScript
  • Vite
  • GSAP
  • FastAPI
  • PostgreSQL
  • Docker
  • S3

infra-rdgzlabs

2026

The self-hosted platform these projects run on.

A single VPS hosting several full-stack applications under one domain, with zero marginal cost per new project. The server is built from scratch with idempotent scripts, so its state depends on neither manual steps nor memory. Caddy routes by subdomain using the Cloudflare origin certificate, and the whole origin sits behind its proxy so nobody can reach it by going around it. A self-hosted monitoring stack watches the host, the containers and the web traffic on the same VPS, and sends an email alert before something goes down.

  • Reproducible provisioning with idempotent Bash: SSH by key only, UFW, fail2ban and automatic updates applied from the first boot.
  • Caddy reverse proxy with TLS handled by the Cloudflare origin certificate (valid for 15 years, Full strict mode), routing by subdomain, on a single shared PostgreSQL with real per-project isolation: each project gets its own login role and database, with no PUBLIC permissions.
  • Origin locked down behind Cloudflare in two layers —a UFW firewall restricted to its IP ranges and iptables DOCKER-USER rules, because Docker can bypass UFW on its own— and HSTS/CSP headers centralized for the whole platform.
  • Visitor's real IP verified end to end (Cloudflare → Caddy → backend): I found and closed a spoofing vector in X-Forwarded-For that would have allowed evading each project's rate limiting.
  • Automated backups via cron with pg_dumpall to object storage, with configurable retention.
  • Observability with Prometheus, Grafana, Loki and Alertmanager on the same VPS, with memory limits per service: metrics and logs from every container, PromQL alerts (disk filling within 24 hours, OOM kills, restart loops, 5xx ratio) and no published ports, with Grafana behind two authentication layers.
  • Docker
  • Caddy
  • Cloudflare
  • PostgreSQL
  • Linux
  • Bash
  • Prometheus
  • Grafana

portfolio

2026

Static Astro landing page with zero framework hydration and a CSP that blocks any inline script.

The landing page of this domain, designed to show the work clearly: project cards with screenshots, stack and verifiable achievements, plus a technology filter. The frontend is static Astro with zero framework hydration; all interactivity is vanilla TypeScript and the page reads fully without JavaScript. Behind it, a minimal FastAPI backend that only receives the contact form messages, on the same self-hosted infrastructure as the rest of the projects.

  • Strict Content Security Policy with no inline scripts, verified at build time by a script that fails the deployment if a <script> with a body appears.
  • Content and design in a single source of truth (data-driven content + design tokens): adding a project or changing the palette touches no components and keeps WCAG AA contrast.
  • Baseline accessibility: modal and lightbox on native <dialog> with focus trapping, aria-live announcements and respect for prefers-reduced-motion.
  • Lead-capture endpoint that always answers 201 (even when the honeypot detects a bot), with per-IP rate limiting grouped by /64 on IPv6 and an email alert via Resend that can never bring the request down.
  • Frontend supply chain audited before building: ignore-scripts blocks any npm postinstall, and the lockfile is resolved and audited with a date quarantine before the build runs any third-party code.
  • Astro
  • TypeScript
  • GSAP
  • FastAPI
  • PostgreSQL
  • Docker
  • Caddy
  • Nginx

cat contact.txt

contact

exit

Built with Astro and served from my own VPS with Docker, Caddy and nginx. © 2026 Juan Rodríguez Privacy notice

Get in touch

Tell me who you are and how I can help. I usually reply within 24 hours.