Vibe Coding Linkwise - A Smart, Personal Link Aggregator
A brief walkthrough of my vibe coding experience building Linkwise, a smart, personal link aggregator.
I come across a lot of interesting articles online - open-source announcements, newsletter links, podcast references - way more than I can read in the moment. Most end up as open tabs in Chrome or links dumped into Notion or Apple Notes. The problem is I rarely go back to them. And when I do, I can't remember why I saved them in the first place. I tried apps like Pocket, but they felt like too much. I didn't want a full reading app, especially one that focused on discovery and exploration, taking me down even more rabbit holes. More unread links was the last thing I needed.
Recently, I kept seeing people talk about their vibe coding experiments - giving into the "vibes" and letting the tools do the building. I use large language models (LLMs) frequently, but haven't tried a completely hands-off approach. Figured this was as good an opportunity as any, so I built Linkwise - a simple yet smart link aggregator. You save a link, it reads the article and generates a summary and some tags. That's it. No fancy features. In this post, I'll walk through how I built it.

Version One - Simple, Local, and Self-Contained
The first version of Linkwise had one goal: simplicity. No bloated web frameworks - just vanilla HTML, CSS, and Javascript on the frontend. Flask, Flask-Login, and SQLite handled the backend, authentication, and database. The app manages user sessions with cookies, stores everything locally in SQLite, pulls article content with BeautifulSoup, and uses Gemini to generate summaries, titles, and tags. You can run the app locally in seconds, or deploy it to serverless platforms like Railway with a Dockerfile and a handful of environment variables. This works great for personal use, but does not quite scale beyond that.
Version Two - Cloud-Native, with Firebase Auth and Firestore
For the second iteration, I kept the frontend simple, but moved to a more scalable backend that could run on Google Cloud Run. I replaced Flask-Login and SQLite with Firebase Authentication and Firestore. Each user's links live in Firestore under their unique ID, and Firebase Security Rules ensure users can only access their own data. The AI summarisation now runs through Gemini on Vertex AI using the google-genai
library, which works out of the box on Cloud Run using default service credentials. No API key needed. Everything still runs in a single Flask container; Cloud Run handles HTTPS, scaling, and authentication context.
For setup, I enabled the Email/Password
provider in Build
> Authentication
, then added the Firebase config to static/app.js
. These keys are safe to expose publicly - they only identify your project but do not authenticate access. Still, it's good practice to restrict API key usage to your deployed domain under APIs & Services
> Credentials
.
const firebaseConfig = {
apiKey: "YOUR_FIREBASE_API_KEY",
authDomain: "YOUR_FIREBASE_PROJECT.firebaseapp.com",
projectId: "YOUR_FIREBASE_PROJECT",
};
In Build
> Firestore Database
, I created a Standard edition
database, picked a location, and enabled Production
mode. Each user’s saved link gets stored in a top-level links
collection using the data model below.
links/
<linkId> {
userId: "firebase-uid",
url: "https://example.com",
title: "Example Article",
summary: "Generated summary by Gemini",
tags: ["AI", "GoogleCloud", "Security"],
createdAt: <timestamp>
}
Under Indexes
, I created a Composite
index for the links
collection, with userId
in Ascending
order, and createdAt
in Descending
order, as the fields to be indexed. This speeds up queries when fetching a user's links sorted by date. Finally, under Rules
, I added these to ensure only authorised users could access and modify their own links.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /links/{linkId} {
allow read, delete: if request.auth != null && request.auth.uid == resource.data.userId;
allow create: if request.auth != null && request.auth.uid == request.resource.data.userId;
}
}
}
Takeaway - Simplicity Scales
What I learned from vibe coding Linkwise twice is that your design should match your intent. The first version was perfect for quick iteration - you can run it anywhere with zero external dependencies. The Firebase version trades some of that simplicity for better resilience, scalability, and authentication. In both cases, the goal was to build something fast that solves the core problem without bloat or distractions. Vibe coding was perfect for that. One thing I didn't cover here: security. That's a whole other beast, and probably worth a separate post.