Colophon
Colophon
Updated: 2026-05-17
This page — how the site is built, deployed, and operated. Every component is open-source or free-tier; the whole stack runs on a personal Cloudflare account.
Stack
| Framework | Astro 5 — static site generator + content collections (markdown/MDX) |
| Runtime | Cloudflare Workers — single worker (khavan) serves HTML + API + cron |
| Static delivery | Workers Assets binding — no Pages, no outer CDN |
| Search | Pagefind (keyword) + Vectorize (semantic) — toggle at /en/search/ |
| Embeddings | bge-m3 via Workers AI — 1024-dim multilingual |
| RAG / "Ask the blog" | Vectorize top-5 → Bedrock Claude Opus 4.7 via OIDC federation |
Storage
D1 khavan-subscribers | subscribers + digests + contacts + post views + AI cache + webmentions + intel hub |
KV OIDC_CREDS_CACHE | Bedrock temp creds (45-min TTL) to avoid STS exchange per call |
R2 khavan-backups | Weekly D1 snapshot (NDJSON.gz, 84-day retention) |
Vectorize khavan-posts | 1,500+ vector chunks indexed from post archive |
AI integrations
- Workers AI bge-m3 — embed query for semantic search + RAG (free tier)
- Workers AI Llama 3.1 — fallback if Bedrock fails
- AWS Bedrock Claude Opus 4.7 — primary for RAG + post summarize
- OIDC federation — Worker mints JWT, AWS STS exchanges → temp creds. No long-lived AWS key in code. Details in this post.
- AI Gateway — caches Bedrock calls for 24h, observability
- Daily budget cap 500 calls/UTC day, kill switch on abuse
Cron schedule
0 */6 * * *— Intel Hub ingest RSS + LLM analyze (every 6h)0 2 * * SUN— Weekly digest + outgoing webmentions (Sun 02:00 UTC)0 3 * * SUN— D1 snapshot → R2 (Sun 03:00 UTC)0 9 * * *— GitHub Actions daily rebuild for drip-publish (Daily 09:00 UTC)
Security
- HSTS preload + DNSSEC active + DMARC
p=reject - Cloudflare WAF 5 custom rules (Free plan max), Bot Fight Mode bypasses verified bots
- Rate limit 5 req/10s at edge + per-IP sliding window at app layer
- Cloudflare Access JWT gates
/admin/*+ email allowlist - Worker Observability 100% sampling — query via Analytics Engine SQL API
- OIDC keys rotated via wrangler secret, never in git
Design
- Tokens: primary orange
#f48120(Cloudflare brand reference) + ink/muted hierarchy - Font: Inter variable, self-hosted woff2 (latin + vietnamese subsets, preloaded per locale)
- Theme: light/dark + system, theme-color meta for mobile browser chrome
- OG image per-post: SVG template rendered via resvg WASM → 1200×630 PNG
Open source acknowledgments
Direct dependencies in package.json:
- astro · @astrojs/mdx · @astrojs/rss · @astrojs/sitemap
- @cf-wasm/resvg — SVG → PNG for OG images
- aws4fetch — SigV4 sign for Bedrock invoke from Worker
- wrangler · pagefind
- vitest + @cloudflare/vitest-pool-workers for tests
Design inspiration: blog.cloudflare.com. Color contrast tuned for WCAG AA.
Hosting cost
Entirely on Cloudflare Workers Free Plan:
- Workers: 100k req/day free → site uses < 5%
- D1: 5 GB + 5M reads/day → 0.7 MB used, ~1000 reads/day
- KV: 100k reads/day → tens of reads/day for OIDC cache
- R2: 10 GB + 1M Class A ops/month → hundreds of ops/month
- Vectorize: 5M queried dims/month → ~100k/month
- Workers AI: free tier neurons/day → enough
Bedrock (AWS) pay-per-token, daily budget capped at 500 calls. Realistic spend < $5/month.
Source
Source code public at github.com/vanhoangkha/khavan.
CI: GitHub Actions → wrangler deploy. Each push lives within ~3 minutes.
License
- Content: All Rights Reserved. Short quotes + link OK; reuse requires permission. See terms.
- Code snippets in posts: MIT license.
- Repository code: no public license yet — read OK, fork OK, commercial use requires contact.
- AI training: NOT permitted. Content-Signal directives in
/robots.txt.