Personal site and portfolio. Hugo static site with a brutalist monospace aesthetic, hosted on GitHub Pages.
Includes an AI chat widget that lets visitors ask questions about my background - powered by a Cloudflare Worker backend with LLM completions.
@startuml
title ostaps.net - chat architecture
skinparam componentStyle rectangle
skinparam padding 4
package "ostaps.net (Hugo + GitHub Pages)" as frontend #E8F4FD {
[chat-widget.html] as widget
[chat.html (mobile)] as chatpage
[Turnstile Widget] as tswidget #F3E8FD
}
package "Cloudflare Worker" as worker #E8F5E9 {
[Validation + CORS] as handler
[Turnstile Verify] as tsverify #F3E8FD
[Rate Limiter] as ratelimit
[FallbackProvider] as fallback
[SSE Stream] as sse
}
cloud "LLM APIs" as llm #F3E8FD {
[Cerebras (primary)] as cerebras
[Groq (fallback)] as groq
}
database "D1 (logs)" as d1 #FFF8E1
database "KV (rate limits)" as kv #FFF8E1
cloud "Turnstile API" as tsapi #F3E8FD
widget --> handler : POST /chat
chatpage --> handler
tswidget ..> tsapi
handler --> tsverify
tsverify --> tsapi
tsverify --> ratelimit
ratelimit --> fallback
ratelimit --> kv
fallback --> cerebras
fallback ..> groq : on failure
sse --> handler : SSE
handler --> d1 : log
@enduml- Site: Hugo 0.134.2 (extended), custom layouts, plain CSS with custom properties
- Hosting: GitHub Pages, deployed via GitHub Actions on push to main
- Chat backend: Cloudflare Worker + D1 (SQLite) + KV (rate limiting) + Turnstile (bot protection)
- LLM: Cerebras (primary) + Groq (fallback), both free tier, swappable via LLMProvider interface
- Analytics: Simple Analytics
- Fonts: IBM Plex Mono (homepage), Verdana (content pages)
hugo server -D # local dev with drafts
hugo --gc --minify # production buildOn desktop, the chat opens as a floating panel. On mobile, it navigates to a dedicated /chat/ page to avoid iOS Safari keyboard issues.
The backend lives in a separate repo (ostap-chat Cloudflare Worker).