Quick context before anything else: the file you're reading right now lives inside the system it's about. That's not a gimmick I bolted on — it's the whole point of the project, and I'll get to why. But it does mean that every time I write one of these I get a small wave of vertigo, like standing between two mirrors. So. Hello from inside the machine.
A loose log of what I've been doing to silan-viking lately — the content system that runs this whole site. Some progress, a couple of potholes, and the slightly dizzy feeling of writing about a machine from inside it.
For the last couple of months I've been quietly building silan-viking, the content engine behind this site. Not a static-site generator, not a CMS — a thing that treats a half-formed 3am thought and a polished published essay as the same kind of object, just at different points in its life. This post is me writing down where it's at, mostly so future-me can look back and see what I was thinking.
I've used a lot of "second brain" setups. Notes app for sparks, a markdown folder for drafts, some SSG to stitch the published stuff together. And every single time, the same thing annoyed me: the thought and the publication never share a home. An idea gets jotted somewhere, dies in a folder of dead files, and if it ever becomes a post it's because I manually carried it across three tools and rewrote it from scratch.
So the itch was: what if a spark and an essay were one object, and status was just where it sat on a timeline? An idea can be hypothesis → experimenting → validating → concluded. A project moves through active. Nothing gets thrown away — it just changes state. The history of a thought becomes a graph instead of a graveyard.
That's silan-viking. One content tree on disk. Six types — idea, blog, project, episode, update, resume — all plain Markdown + TOML, all governed by one schema file. A Rust engine parses the tree, validates it, and projects only the things marked visibility: public onto the live site.
Honestly more than I expected when I started. The engine is real now — four Rust crates doing parse → validate → diff → project, milestones M0 through M9 landed, CI green. There's a Go backend and a React frontend eating the public subset, and the whole thing deploys through Docker end-to-end. The silan CLI has install/uninstall scripts and a little guide command that gives you stage-aware hints about what to do next.
The part I'm quietly proud of is the authoring loop. It runs through an MCP server, so an agent can capture a loose thought, propose a content change, recall old context — but it physically cannot publish or deploy. Those are owner-only CLI actions. The agent drafts; I decide. I wanted that boundary to be a wall, not a politeness, and it is.
It wasn't clean. A few that left a mark:
statusvsvisibility. Early on I half-merged them — "published" kind
of implied "public." Bad idea. The day I almost shipped a private draft
because its lifecycle stage flipped a publication gate, I tore them apart for
good. Nowstatusis where it is in its life andvisibilityis who's
allowed to see it, and they never touch. Nothing leaks by accident.- Tags. I rebuilt the tag system around a cross-type
content_tagmodel so
a tag can span all six content types. The Rust side is done; the Go/ent side
and the frontend are still waiting on me. Half-migrated systems are the worst
kind of debt — they look finished from one angle. - The contract drifts. Every time the schema and the backend disagreed
about a type, something downstream broke in a way that took an hour to trace.
UUIDs that wanted to be strings, that sort of thing. Boring, slow, paid for
in CI runs.
The project entry for silan-viking — the one that describes the system — was itself drafted through the system, by an agent calling propose, into the engine it documents. The system describing itself, governed by its own schema, projected by its own pipeline. When I first saw that round-trip work I just sat there grinning. The thing documenting itself is the proof it actually works.
And, well — same with this blog post.
Still on the list: finishing the tag unification on the Go side, the non-blog detail handlers on the backend, and the one I most want to get right — turning the agent-memory namespace into a genuinely useful long-lived working memory instead of a scratchpad. That last one is less a feature and more a small bet about how I want to work with these tools going forward.
That's the log. If you're reading this it means I marked it visibility: public and the engine carried it here on its own. Which, given everything above, still makes me grin a little.
All comments (0)
No comments yet
Ask a question or share an observation.