---

title: hldr

tagline: Site and CLI for hvpaiva.dev.

status: active

highlight: 4

tags: [rust, axum, sqlite, kamal]

links:

repo: https://github.com/hvpaiva/hldr

github: hvpaiva/hldr

---

## The problem

A personal site whose deploy is a manual ritual contradicts a résumé

about delivery. The site had to be the argument, not a page describing

one.

## Constraints

One maintainer. No admin panel. No JavaScript required to read a page.

A content change must not be a deploy.

## What I built

A Rust workspace of three crates: the domain, the server and the CLI.

Content is not in the server. It lives in a second repository as YAML

manifests shaped like Kubernetes objects, `kind`, `metadata` and `spec`,

with the markdown of a page beside its manifest. The server resolves a

ref to one commit, downloads that commit and materializes it into SQLite.

Page types are content too: a blog is a manifest that declares its

fields, not a release of the server. Delete the database and nothing is

lost, because the next sync rebuilds it. A revision that fails to index

changes nothing, and the previous one stays served.

Administration is a CLI in the kubectl shape. `hldr get projects -o wide`,

`hldr describe project hldr`, `hldr explain project.metadata.status`,

`hldr apply -f`, `hldr diff`, `hldr patch`, `hldr edit`. Resource types

and their columns come from the server's discovery endpoint, so a page

type invented in content works in the CLI without a new binary. Writes

commit to the content repository the server reports it syncs from, so the

CLI cannot write where the site does not read.

The private API has no authentication of its own. It listens on a

separate socket that only the tailnet reaches, so the network is the

boundary instead of a token I would have to rotate. The public site

serves none of it.

A push to `main` that changes behavior is a release: checked, built,

deployed with Kamal, and tagged once production answers healthy. The tag

is the version and the history of production. `install.sh` checks the

SHA-256 of the binary and then the Sigstore attestation that ties it to

the workflow run that built it. A missing attestation fails the same as

a wrong one, because a checksum published beside a binary proves nothing

about who produced it.

The site counts what it serves without tracking anyone. No cookie, no

script, no address or user agent stored. A visitor is a hash of the day's

random salt, the client address and its user agent; when the day closes,

the salt and the hashes are deleted and only counts remain. Nobody can be

followed across days, including me.

Version skew follows Kubernetes' policy rather than matching numbers, and

the policy is a test. Every type the API serves or reads is rendered as

JSON Schema and compared against a snapshot on each build, so a breaking

change fails until it ships as a breaking release.

git clone github.com/hvpaiva/hldr

← projects/