Resume-as-code. Structured YAML data, Go API server, Astro frontend with D3 visualizations, and PDF export — in a single binary.
Website · Live Demo · Quickstart
- Website — responsive Astro site with dark/light mode, driven entirely by your YAML
- PDF — hyperlinked, multi-page PDF generated server-side (no browser, no Puppeteer)
- API — JSON endpoint for your resume data with privacy filtering
- Comet Timeline — D3 interactive experience visualization with company grouping, promotions, and recency fading
- Skills Ecosystem — radial network graph connecting skills, certifications, and projects
Same engine, different careers. Each is just a YAML file — see examples/ for the source.
| Product Designer 5 companies · design tools & methods |
ML Researcher PhD + DeepMind · publications as projects |
Freelance Engineer consulting + startups · deep infra stack |
![]() |
![]() |
![]() |
Try one:
cp examples/designer.yaml data/resume.yaml && make build && ./bin/semblant
# 1. Create your resume
cp data/resume.example.yaml data/resume.yaml
# Edit data/resume.yaml with your info
# 2. Install frontend dependencies
cd web && npm install && cd ..
# 3. Build and run
make build
./bin/semblant
# → http://localhost:5173Your resume lives in data/resume.yaml. See data/resume.example.yaml for the full schema with comments.
personal:
name: "Jane Doe"
title: "Senior Software Engineer"
bio: "..."
website: "https://janedoe.dev"
links:
- platform: github
url: "https://github.com/janedoe"
contact:
- type: email
value: "jane@example.com"
visibility: private # stripped from public API
experience:
- company: "Acme Corp"
url: "https://acme.example.com" # hyperlinked in PDF
role: "Senior Software Engineer"
start: "2023-01"
end: null # null = current
highlights:
- "Reduced p99 latency by 40%"
technologies: [Go, PostgreSQL, Kubernetes]
skills:
- category: "Languages"
items:
- name: "Go"
level: 85 # 0-100, drives node size in ecosystem graph
- name: "Python"
level: 80
# ... education, certifications, languages, projects
layout:
accent: "#8b5cf6" # custom accent color (default: teal)
experience: list # timeline | list
skills: grid # ecosystem | grid
sections: # which sections to render, in order
- experience
- skills
- education
# omit a section to hide it, reorder to rearrangeThe layout block is optional. If omitted, all sections render in the default order with the default teal accent.
accent— hex color applied to the title, links, buttons, tags, section lines, PDF highlights, and the Download button. One value themes everything.sections— ordered list of sections to render. Available:experience,education,languages,skills,certifications,projects. Omit a section to hide it. Reorder to rearrange.experience—timeline(default) orlist. Timeline is the interactive D3 comet visualization. List is a clean static card layout grouped by company with promotion indicators — zero JavaScript.skills—ecosystem(default) orgrid. Ecosystem is the interactive D3 radial graph connecting skills, certs, and projects. Grid is a two-column layout with level bars — zero JavaScript.
Fields with visibility: private are stripped from the public /api/resume endpoint and the frontend. The PDF includes all fields (it's behind auth).
All configuration via environment variables:
| Variable | Default | Description |
|---|---|---|
SEMBLANT_PORT |
5173 |
Server port |
SEMBLANT_RESUME_PATH |
data/resume.yaml |
Path to resume YAML |
SEMBLANT_WEB_DIR |
web/dist |
Path to built frontend |
SEMBLANT_PDF_AUTH_SECRET |
(empty) | Bearer token for PDF endpoint. If empty, PDF returns 401 |
SEMBLANT_CORS_ORIGINS |
* |
Comma-separated allowed origins. Set to your domain in production |
ANTHROPIC_API_KEY |
(empty) | Anthropic API key. Enables /api/chat when set |
ANTHROPIC_MODEL |
claude-sonnet-4-20250514 |
Claude model for chat responses |
| Endpoint | Auth | Description |
|---|---|---|
GET /health |
No | {"status":"ok"} |
GET /api/resume |
No | Public resume JSON (private fields stripped) |
GET /api/resume?full=true |
Bearer | Full resume including private fields |
GET /api/resume/pdf |
Bearer | Download PDF |
POST /api/chat |
No | Chat with the resume (requires ANTHROPIC_API_KEY + layout.chat: true) |
Enable conversational access to the resume — visitors can ask questions and get grounded answers powered by Claude.
# resume.yaml
layout:
chat: trueexport ANTHROPIC_API_KEY=sk-ant-...The chat widget appears as a floating button in the bottom-right. Suggested questions on first open, 500 char input limit, 10 req/min rate limit per IP. Answers are grounded in the resume data only — the LLM is instructed to say "I don't know" for anything not in the resume.
# Run Go server + Astro dev server concurrently
make dev
# Or separately:
make go-run # Go server only
make web-dev # Astro dev server only
# Build
make build # Builds both frontend and Go binary
# Test
make test # Go tests + Playwright e2e
make test-go # Go tests onlycmd/semblant/ Entry point
internal/
api/ HTTP handlers, middleware, routing
config/ Environment-based configuration
pdf/ PDF renderer (theme, helpers, per-section methods)
resume/ Schema, YAML loader, privacy filtering
data/
resume.yaml Your resume data
resume.example.yaml Schema reference with placeholder data
web/
src/
pages/ Astro pages
components/ Static Astro components
islands/ Interactive React + D3 islands
lib/ Shared constants, types, utilities
docker build -t semblant .
docker run -p 8080:8080 \
-e SEMBLANT_PDF_AUTH_SECRET=your-secret \
-e SEMBLANT_CORS_ORIGINS=https://your-domain.com \
semblantThe image runs as non-root (UID 1001), with a read-only root filesystem.
Semblant includes production-grade security defaults:
- Auth: Constant-time token comparison (no timing attacks)
- Rate limiting: Per-IP limits on all endpoints (5 req/min on PDF)
- Security headers: HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy
- CORS: Configurable origin allowlist (defaults to
*for development) - Privacy filtering: Private fields never leak through the public API
- Container: Non-root user, read-only filesystem, all capabilities dropped
Apache License 2.0. See LICENSE.




