Project · 2026

papke-page

This very site — a Next.js portfolio, bilingual, with an AI agent grounded in an anonymized base of 49 verified facts, 42 tests, a strict CSP and a custom domain. Built in public, phase by phase, since June 2026.

A portfolio that is also a product: bilingual, with an AI agent grounded in my own content (and real safety guardrails), audited from the outside, built in public and shipped on every merge.

Screenshot of the papke-page home: hero “I build products that solve.”, About section with photo and the experience highlights strip, dark theme with amber.

You are looking at it. papke-page is my portfolio — and a project in its own right, built in public since the first commit on June 28, 2026: 82 commits, 11 pages in two languages, 123 translation keys per language and 8 hand-drawn SVG covers. The blog is the record of that build.

A Next.js 16 site (App Router, TypeScript, Server Components), bilingual PT/EN with next-intl — from content to routes — and Tailwind v4 with custom theme tokens (dark + amber). Content lives in versioned TypeScript files (content as code): pages, projects, experience and this very text come from the same typed source, which also feeds the agent.

The "Ask about me" agent answers grounded only in my content plus an anonymized skills base — 49 verified facts and 14 prepared answers, retrieved by relevance for each question, in the visitor's language and streamed. Each fact can carry the HOW (the mechanics of the solution) and an illustrative code snippet of up to 3 lines, already anonymized. Real guardrails: a relevance check before generating, per-IP rate limiting, prompt-injection resistance and an automated test that fails if a snippet carries a real host, schema or user. It runs on a free tier (Gemini via an OpenAI-compatible API).

Security treated as a feature: a strict CSP (the only external resource is Google Analytics — with a first-visit notice and a footer opt-out, allow-listed by name in the policy), X-Frame-Options, nosniff, Referrer-Policy, Permissions-Policy and HSTS; API keys server-side only; locale cookie with the Secure flag; npm audit at zero vulnerabilities. I audited the site from the outside, the way an attacker would: no open route, no internal data, no key in the bundle — and Vercel's bot protection blocked my own burst scan.

LGPD in practice: a plain-language privacy page, a contact form with a honeypot, per-IP limits, size caps and zero PII in logs; the agent keeps no conversation history; Google Analytics runs with a first-visit notice and a real footer opt-out — switched off, nothing loads.

SEO and a custom domain: Open Graph, sitemap, robots and static generation, with the canonical URL centralized in a single module. roger.papke.com.br points from Cloudflare (DNS only) straight to Vercel, with automatic SSL and a 308 redirect from the .vercel.app address.

Day-to-day engineering: changes via Pull Request with tsc, eslint and 42 tests (Vitest) green before shipping; two machines pushing to the same branch, with fetch and rebase before every push; merging is a human decision. Continuous deployment on Vercel — every merge ships.

By the numbers

82
Commits since Jun 2026
11 × 2
Pages × languages
123
Translation keys per language
8
Hand-drawn SVG covers
49
Verified facts in the agent base
14
Prepared answers (Q&A)
42
Automated tests
6
Security headers
0
Vulnerabilities (npm audit)
0
Leaks in the external audit

Engineering decisions

  1. 01

    Content as code

    Everything — pages, projects, experience, blog — lives in versioned TypeScript files, read through a single access module. One typed source feeding both the site and the agent: change the content, both change.

  2. 02

    A grounded agent, with guardrails

    The agent only speaks about me. Before generating, a cheap call decides whether the question is relevant; then the base of 49 facts and 14 prepared answers is retrieved by relevance and enters the prompt with the rules that accompany it (caveats, self-reported confidence, code as illustration only). Per-IP rate limiting, prompt-injection resistance and no sensitive data — all on a free tier.

  3. 03

    Anonymization as a rule — and as a test

    The agent base comes from a career archive anonymized on purpose: no fact names an institution, system, schema or colleague. The rule is not just in a document — an automated test fails if any snippet carries a real host, schema, user or object name. And the external audit confirmed it: nothing internal reaches the served bundle.

  4. 04

    Bilingual end to end

    PT/EN via next-intl from content to routes — 123 keys per language, every project and every role written in both — and the agent replies in the question's language.

  5. 05

    Security verified from the outside

    The site loads a single external resource in the browser — Google Analytics, with a first-visit notice and a footer opt-out — so the CSP could stay strict, allow-listing only Google domains by name. Keys server-side only, POST-only APIs with closed CORS and errors with no stack traces. After hardening, I audited it from the outside like an attacker would: sensitive routes, bundle, source maps, cookies. Zero leaks — and Vercel's bot protection blocked my own burst scan.

  6. 06

    Hand-drawn SVG covers

    Instead of generic images, every project has a hand-drawn SVG cover in the same visual language (dark + amber): architecture diagrams, pipelines, the database ecosystem. Lightweight, crisp on any screen, validated as XML before going in — and, when the project has real material, with photos and screens embedded.

  7. 07

    Custom domain, no proxy

    roger.papke.com.br sits on Cloudflare as DNS only (grey cloud): a CNAME straight to Vercel, which issues and renews SSL on its own. No proxy in between, no cache fighting Next. The .vercel.app address redirects with a 308, and the canonical URL lives in a single module feeding metadata, sitemap and robots.

  8. 08

    Continuous deployment via PR — two machines, one branch

    Every change goes through a Pull Request with tsc, eslint and 42 tests green. Two machines work on the same branch: fetch and rebase before every push, and history stays linear. Merging is a human decision; from there, Vercel ships on its own.

Architecture

Next.js

App Router · [locale]

11 pages in PT/EN, static generation and Server Components.

Conteúdo (TS)

Single source

content/data in TypeScript feeds pages, projects, experience and the agent.

i18n

next-intl

123 keys per language, from content to routes.

Agente de IA

API route + Gemini

Relevance check → retrieval by relevance → streamed answer, with guardrails.

Base anonimizada

49 facts · 14 Q&A

Generated from the career archive; an automated test blocks identifier leaks.

Segurança

Headers · rate limit

Strict CSP, HSTS, nosniff; keys server-side only; per-IP limits on the APIs.

APIs

/api/chat · /api/contact

POST only, closed CORS, terse errors; contact via Resend with no PII in logs.

Deploy & domínio

Vercel · Cloudflare

PR + gate → Vercel; DNS only on Cloudflare; 308 from .vercel.app.

Stack

Next.jsTypeScriptnext-intlTailwind CSSVitestAI Agents (Gemini)VercelCloudflare