No description
  • Go 76.7%
  • Shell 9.2%
  • CSS 4.9%
  • JavaScript 4.9%
  • TeX 4.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-14 19:33:08 -04:00
content Portfolio site: stdlib-only Go server with self-instrumentation 2026-09-28 22:42:37 -04:00
deploy deploy: one script instead of provision + deploy 2026-09-29 11:05:41 -04:00
internal demo: one button, and put the work above the interactive sections 2026-09-29 12:15:09 -04:00
scripts deploy: one script instead of provision + deploy 2026-09-29 11:05:41 -04:00
static demo: one button, and put the work above the interactive sections 2026-09-29 12:15:09 -04:00
.gitignore chore: keep CAREER-MASTER.md local-only (uncommitted working file) 2026-10-14 19:28:00 -04:00
go.mod Portfolio site: stdlib-only Go server with self-instrumentation 2026-09-28 22:42:37 -04:00
LICENSE Initial commit 2026-09-29 00:08:38 +00:00
main.go serve the resume from Caddy instead of the Go process 2026-09-29 10:07:28 -04:00
README.md deploy: one script instead of provision + deploy 2026-09-29 11:05:41 -04:00
resume-proposed.pdf resume: split Leadership section by era (Chase RFC/ADR, NetApp scrum+hiring) 2026-10-14 19:33:08 -04:00
resume-proposed.tex resume: split Leadership section by era (Chase RFC/ADR, NetApp scrum+hiring) 2026-10-14 19:33:08 -04:00

mdthomasel.dev

A personal site that is also its own proof of work. The server instruments itself and exposes Prometheus-format telemetry, and the homepage renders that telemetry live.

It has no third-party dependencies. go list -m all returns one module. No web framework, no Prometheus client, no markdown parser, no charting library. The exposition format, the histogram quantiles, the markdown renderer, and the chart are all implemented here, because depending on them would contradict the claim the page is making.

Why

Most portfolios assert competence. This one shows it. The homepage publishes a latency budget, and the histogram next to it either meets that budget or does not — there is no screenshot, and no number on the page that is not scraped from the running process.

The claim, measured

The site declares a p99 latency budget of 20ms and publishes its own histogram. Measured on a 2-vCPU Hetzner CX23:

Load p50 p99 max
5,000 requests, 32 concurrent 0.10ms 0.50ms —
30,000 requests, 64 concurrent 1.77ms 8.42ms 15.5ms

The bucket boundary at exactly le="0.020" is deliberate: it makes the budget directly readable from the exposed counts.

Endpoints

Path Purpose
/ Landing page with the live panel
/work/{slug} Project write-ups, rendered from markdown on disk
/metrics Prometheus text exposition format
/api/stats JSON for the homepage panel
/api/decide Idempotent decision endpoint (POST)
/healthz Liveness probe

The exposition output is validated against Prometheus's own reference parser (prometheus_client.parser), not only against the tests in this repo.

Architecture

main.go                  entrypoint, graceful shutdown, request deadlines
internal/telemetry/      hand-written counters, gauges, histograms, exposition
internal/content/        markdown loader and escaping-first renderer
internal/server/         routing, instrumentation middleware, idempotency store
content/*.md             page sources
static/                  CSS and the hand-rolled SVG chart

Notes on the interesting parts

Metric labels are bounded by route, not by URL. Every request to /work/anything is recorded as path="/work/{slug}". Labelling by r.URL.Path is the classic unbounded-cardinality bug, and it is what turns a bot's random probing into unbounded memory growth. There is a test that hammers 50 distinct URLs and asserts the series count does not move.

Content is escaped before markup is generated. The renderer emits no markup around unescaped text, and link URLs pass a scheme allowlist rather than merely being HTML-escaped — html.EscapeString leaves javascript:alert(1) completely intact, so escaping alone would not be a defence.

The histogram quantile accumulates buckets. A histogram stores per-bucket counts; the quantile walks them cumulatively. Getting that wrong yields a correct-looking /metrics output alongside a wrong p99, which is the worst possible failure mode for a page whose entire claim is latency.

The idempotency store is bounded. An unbounded map keyed by client input is a memory-exhaustion vector, so the oldest entry is evicted at capacity. It is in-memory, which does not survive a restart or span instances; that limitation is deliberate and documented rather than hidden.

Development

go run .                       # serve on :8080
go test ./...                  # 56 tests
go test -race ./...            # concurrency
go vet ./... && gofmt -l .
./scripts/build.sh             # static binary, version-stamped

Deployment

One script, on the server, from a checkout:

./scripts/deploy.sh

It is idempotent. On a clean Debian or Ubuntu host it installs everything and deploys; on a configured host it skips the host steps and goes straight to building. It needs root, because it writes to /etc.

./scripts/deploy.sh --skip-host   # skip package installation and host setup
./scripts/deploy.sh --help

The phases run in a deliberate order, and it matters:

  1. Host prerequisites — Go, Caddy, the portfolio user, directories, firewall, swap. Idempotent and cheap, so they run on every deploy.
  2. Build — go vet, gofmt, the full test suite, then a static -trimpath binary. Tests run before anything is installed.
  3. Configuration — the systemd unit and Caddyfile are installed from the repository, before the binary.
  4. Cross-check — every flag in the unit's ExecStart is checked against the binary's own usage output.
  5. Smoke test — the new binary runs on a spare port and must answer its health check, expose a valid histogram, and serve a decision, before it is allowed to replace the live one.
  6. Install and restart.
  7. Verify each layer separately — app, Caddy, the résumé, and the public TLS endpoint.

Steps 3 and 4 exist because the previous design split provisioning from deploying. One script installed the unit, the other replaced the binary, and nothing checked that they agreed — so a flag removed from the code stayed in the unit until it produced a restart loop. Installing configuration from the repository in a fixed order, then verifying it against the binary, removes that failure mode.

deploy/portfolio.service runs the binary as a non-root user with ProtectSystem=strict, a PrivateTmp, a syscall filter, and a 256MB memory cap. deploy/Caddyfile terminates TLS and proxies to loopback, so the Go server's port is never exposed, and serves the résumé directly from disk so it survives an app restart.

Licence

MIT. See LICENSE.