Why I built aryrabelo.com from scratch
Linktree is ugly and expensive for what it does. Every link shortener I tried stored click data in their database, not mine. My portfolio sat on a static page I rarely updated because updating meant a deploy. And when I ran Instagram campaigns asking people to “COMMENT: [keyword]”, the lead arrived in my DM and disappeared: no history, no pipeline, no way to measure what actually worked.
That’s the context. I built aryrabelo.com to solve all four of those problems in the same place.
This is the same reasoning I apply when I architect systems for clients: own your data, avoid vendor lock-in, and make the stack answer to the business instead of the other way around.
Four surfaces, one codebase
Lead capture. Pages at /g/[keyword] that deliver the content magnet (PDF, template, article) and record the contact in the database. When I post on Instagram “COMMENT: tools” and send the link in the DM, the person lands on one of these pages, fills in their email, and the lead goes into Cloudflare D1, not into a SaaS platform that charges me to export it later.
Trackable shortener. Every link I share passes through /r/[slug]. Each click is logged with source, timestamp, and slug. If I post the same content on Instagram and LinkedIn with different slugs, I know which channel converted, without relying on manual UTMs that anyone can strip from the URL.
Self-hosted link-in-bio. At /l/[slug] I have pages with configurable sections managed from the admin panel, replacing Linktree and Liinks. No watermark, no link limit, analytics in my own database.
Bilingual portfolio and this blog. Portuguese for Brazilian audiences, English for international clients and partners. Same content, written natively in each language. Not word-for-word translated: each version was written from scratch in the right language.
A “link-in-bio” is the page you put in your Instagram profile because Instagram only lets you have one link. “Lead capture” is when you ask for someone’s email in exchange for something useful, like a PDF or guide. “Self-hosted” means the site runs on infrastructure I control directly, not on a platform that can change its pricing, change its policies, or shut down tomorrow.
Why not use existing tools
The issue isn’t just cost. Linktree Pro, a shortener with analytics, and a lead capture platform together add up considerably per month, and on Cloudflare’s free tier that same set of features costs zero in infrastructure. But the more important point is dependency: when I rely on three separate platforms to run my funnel, any pricing change, API change, or product shutdown breaks my flow. With my own codebase, I control what changes and when.
There’s another thing that becomes clear over time: every platform you use keeps your users’ data in their database, not yours. Lead portfolio, click history, access behavior. All of it locked inside another company’s ecosystem.
Every lead that comes in through /g/[keyword] goes into your database, not a SaaS platform’s. That seems like a minor detail until the platform changes its export policy, raises prices, or shuts down. Owning your funnel data is concrete competitive advantage, especially once you start connecting capture, qualification, and CRM into a single pipeline without depending on third-party integrations that charge per zap in the middle.
The stack, and why each choice
Astro 6 in SSR mode with Tailwind v4 and MDX for the blog and content pages. Hono running in the same Cloudflare Worker for dynamic routes: admin, shortener, lead magnets. oRPC connecting front and back with shared type contracts, no separate schema, no generated client. Drizzle ORM over Cloudflare D1 (SQLite at the edge). Better Auth for session and OAuth.
The monorepo runs on Turborepo with pnpm. One turbo build and everything ships.
The central decision was keeping everything in the same repository and the same runtime. When I need a new API route, it lives next to the component that consumes it, with shared types, no intermediate OpenAPI contract, no code generation step. That’s not elegance for its own sake: it’s what makes maintenance viable when you’re the only developer on the project.
Cloudflare was the natural choice given that’s where the Worker runs, D1 lives, KV lives, and deployment is one command. The free tier handles reasonable traffic at no infrastructure cost.
Astro 6 uses output: 'server' with selective prerender per route. Hono sits on the same Worker entrypoint and handles /admin, /r/*, /g/*, /l/*, and the internal API. oRPC generates contract types directly from TypeScript procedures, no code generation step, no intermediate schema. Drizzle over D1 gives versioned migrations and type-safe queries without a heavy ORM. Better Auth handles session and OAuth without a separate database dependency: one sessions table in D1 covers it.
Runtime-editable surfaces
The /admin panel lets me create and edit short links, lead magnet pages, and link-in-bios without any deploy. I log in, create a slug, and in 30 seconds the URL is live. That was a hard requirement from the start: I don’t want to push a deploy every time I need a new link for a new campaign.
This is the beginning, but it’s the core of what grows from here. Posts on applied AI and automation coming soon. In the meantime, have a look at the projects.
Every choice here answered to the business: own the data, dodge the lock-in, keep the runtime one push away. If you want that same lens on a system you are building, the first move is scoping what it actually needs. Start a scoping conversation.
I write Em Paralelo once a week about the engineering behind AI products, developer tooling, and performance. Real numbers, no hype. Subscribe to Em Paralelo.