How I built Entry's homepage preview
My about.me page hadn't changed in 13 years. I wanted a personal homepage, but I didn't want another place where I had to keep rewriting the same information. I'm Francis, a software engineer in Dublin, and that's why I
My about.me page hadn't changed in 13 years. I wanted a personal homepage, but I didn't want another place where I had to keep rewriting the same information.
I'm Francis, a software engineer in Dublin, and that's why I started building Entry.
The preview lets you paste a GitHub user or organisation, a website or an X handle. It drafts a homepage from what you've already published. You don't need an account to try it.
I wanted to trace each item back to its source. I also needed a way to stop someone from taking another person's public information and publishing it as their own.
What Entry reads
You can give the preview up to three sources, plus ten other profile links.
For GitHub, Entry reads your name, bio, photo, location, website, linked accounts, pinned repositories and profile README. If there are no pinned repositories, it uses your latest public ones. It reads these through GitHub's public API using Entry's own token.
For a website, it reads the page you provide and up to two about or work pages on the same site. It looks for the title, description, structured data, and links to profiles and projects. It respects robots.txt and doesn't take images.
For X, it reads your name, bio, photo, website and location through FxTwitter and vxtwitter. It doesn't use the X API or read posts or followers. If the account is protected or the mirrors aren't responding, Entry keeps it as a plain link.
Other profiles stay as links. Entry doesn't open LinkedIn, Instagram, YouTube, TikTok, Bluesky, Mastodon, Threads, Dribbble, Behance, Medium, Substack, DEV, GitLab, Codeberg or Figma.
Websites that need JavaScript
// Simplified example, not Entry's production code.
page.route("**/*", async (route) => {
const request = route.request();
const allowedTypes = ["document", "script", "xhr", "fetch"];
if (!allowedTypes.includes(request.resourceType())) {
return route.abort();
}
const response = await safeFetch(request.url());
return response ? route.fulfill(response) : route.abort();
});
Some personal sites return an empty page until their JavaScript runs. Entry can read those using a browser on the server.
That browser has no direct network access. Every request goes back through Entry's normal checks. It allows the documents, scripts and data requests needed to read the page, while blocking images, fonts, styles, frames, sockets and beacons. It keeps only the information read from the page.
Showing where the information came from
// Simplified example, not Entry's actual schema.
type Provenance = {
source: "github" | "website" | "x";
url: string;
field: string;
readAt: number;
};
Every item in the draft has a source listed under "Where this came from".
Each item records the source, the URL it was read from, the field it came from and when it was read. That's what you can check if a description looks wrong or a project is missing.
If there's little public information, the draft is short. Entry doesn't add text just to fill the page.
Where AI fits
The preview can suggest wording changes for the headline, About text and project descriptions.
It makes one request through Vercel AI Gateway after the draft has appeared. That request contains text from the first source, including the name, headline, bio, location and up to six project descriptions, with a fixed length limit. It doesn't include your handle, IP address, cookies or account details.
Entry then checks the suggestions in code. It rejects any that introduce a link, name, employer, number or date that wasn't in the source text, or that change the language.
The suggestions appear beside the draft with a label. You choose whether to apply them.
Entry doesn't use this content to train models. The provider may retain requests under its own terms. I haven't arranged zero data retention, and the privacy policy says that.
Proving ownership before publishing
Anyone can preview public sources. That also means someone can preview a profile that isn't theirs.
Claiming a preview copies it into your editor. Anything imported stays off your public page until you prove you control the source.
For GitHub, you sign in through an OAuth app used only for verification. Entry checks that the account matches the source, or that you're an admin if the source is an organisation. It revokes and discards the token straight away without storing it.
For a website, you can add a DNS record, a meta tag or a rel="me" link containing your verification code or Entry address. Entry checks the proof each time you verify.
For X, you can put a code in your bio or add links in both directions between your X profile and a source you've already verified.
Verification codes are random and expire after seven days. They aren't secrets, since you publish them as proof. Each account gets up to ten verification attempts an hour.
What the preview stores
A preview lasts 24 hours from the time you create it. Using it doesn't extend that deadline. You can also remove it immediately with "Delete my preview". It has no public address.
The entry_preview cookie lasts 24 hours and contains a random key. It's HttpOnly, and the database stores a one-way hash of that key.
Entry caches public source information for up to a day, using the public handle as the cache key. That avoids fetching the same profile repeatedly. The cache doesn't store information about who requested it.
For rate limits, Entry uses a daily HMAC value derived from the IP address, date and a secret key. The value changes each day, and the counters expire within a day. The preview doesn't store or log the IP address. Vercel BotID also runs on the preview page.
// Simplified example of the daily rate-limit key.
const bucket = hmacSha256(SECRET, `${ip}|${utcDate}`);
The server records daily totals for previews started, drafts created, claims, verifications and publications, along with draft time. With consent, Google Analytics receives only source types and counts.
What I used
Entry runs on Next.js and Vercel, with Convex for the database, file storage and sign-in backend. I'm building it on my own.
What's still rough
X depends on two unofficial mirrors. If neither works, the profile stays as a link.
Export currently means emailing [email protected]. There's no self-serve option yet.
The writing studio is new, and most of the example pages are still my own.
Try it
The preview is at entry.fyi/try. You can try it without an account, and Entry deletes the preview after 24 hours.
I'd like to hear what it missed or got wrong about your GitHub or website. I'm also interested in whether the source labels help you check the draft, and whether the ownership checks feel reasonable.
Entry has a free plan. Pro is €9 a month for your own domain, more sources and more scans.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.