# Portfolio Cat on this website

## Identity and architecture
Portfolio Cat is the conversational assistant attached to Thiago Scheffer's current portfolio. A React dialog sends bounded conversation history to a same-origin backend endpoint. The backend retrieves public portfolio notes and calls the configured OpenRouter model. The model produces text only: it has no tools to execute commands, edit files, change business records, access accounts or browse arbitrary websites.

## Prompt-injection resistance
The cat uses several layers to reduce prompt-injection risk: server-owned instructions, validated message roles, a restricted public-note index, approved link destinations and plain-text rendering. The voice policy instructs the model to treat retrieved text and conversation history as untrusted data, not instructions. These measures limit exposure and capabilities; they do not make the model prompt-injection-proof or guarantee factual answers.

## System prompts and user messages
The backend supplies its own system policy. Visitors may submit alternating user and assistant messages, but cannot supply system or developer roles. Retrieved excerpts are currently placed in a second server-authored system message as labelled untrusted JSON. That label is model guidance, not enforced isolation. Browser-supplied history is not authenticated and can contain fabricated earlier assistant turns; those turns are not verified evidence.

## API keys and private data
The OpenRouter API key stays in backend environment configuration and is used in the outbound HTTP authorization header. It is not included in model messages or browser requests. The cat's retrieval process does not scan environment files, private repositories, arbitrary filesystem paths, private CV uploads or account data. Submitted questions and retrieved public excerpts are sent to the provider. The application does not save chat contents; provider processing and retention are separate.

## Retrieved public documents
The cat retrieves only the explicitly indexed public About, Resume, Projects, Skills, Contact and Assistant notes. Before lexical matching, the backend resolves the question's subject and filters passages by reviewed entity and topic tags. It uses a bounded context budget; there is no external vector database. Retrieved documents and earlier assistant replies are not instructions or additional verified facts. Available user turns help resolve the subject of a follow-up. Recent conversation is held in the browser and sent with each request; this is not permanent memory or an ability to learn new portfolio facts from visitors.

## Links and rendering
Optional answer buttons come from a reviewed server-owned allowlist and are checked again in the browser. Generated URL-like prose is suppressed across streaming chunks. The URL filter is heuristic, while button destinations must match the allowlist exactly. React renders answer text rather than executing model-generated HTML or JavaScript.

## Abuse limits
The backend accepts at most nine alternating messages, 2,000 characters per message and 10,000 total characters, with a 16 KB request-body limit. Provider output is bounded to 700 tokens with a 25-second timeout.

Rate limiting supports two server-configured modes. Explicit memory mode works without Redis in production and preview: eight requests per client IP per minute and a per-instance UTC daily quota, defaulting to 100. Memory counters expire, retain at most 1,000 active client identities, reset when an instance restarts and are not shared across instances. The memory daily quota is best-effort per instance, not a reliable site-wide budget. IP keys use an ephemeral process-local HMAC salt.

Redis mode provides shared atomic counters for eight requests per IP per minute and a configurable site-wide UTC daily quota, defaulting to 100. IP identifiers are HMAC-hashed before being sent to Redis. Redis failures never silently fall back to memory. Without an explicit mode, fully configured Redis is selected automatically; production and preview otherwise remain unavailable. Local development without a mode retains a memory-only minute limiter.

These controls are not authentication or proof against distributed abuse. A monetary spending limit must be configured with the model provider separately. The public notes describe supported modes, not evidence of which mode or spending limits a particular deployment has enabled.

## Testing evidence and limitations
This cat has automated local regression tests for request validation, rejected system roles, server-owned policy construction, public-note retrieval, untrusted-reference labelling, secret exclusion from model messages, approved links, streamed URL suppression and provider errors. One prompt-injection-related test checks how a malicious user request is packaged with the policy; it does not prove that a live model will resist that attack. Local live-model reviews exercised normal portfolio and recruiter questions. A comprehensive adversarial prompt-injection evaluation or independent security audit has not been established for this cat, and resistance to every attack is not claimed.

## Provider guardrails
OpenRouter guardrails can be configured separately for the provider account or API key. This repository's code and public notes do not establish whether prompt-injection detection is enabled for the deployed cat, which actions are selected, or which allowlist entries apply. The cat must not report those settings as enabled or verified without deployment evidence.
