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.

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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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