Skip to content
covey

A week ago, publishing this text would have meant writing a React component, adding an entry to a route table, running make build and restarting the control plane. A blog post would have been a deployment of the platform.

That was because the website was part of the product. Covey is a single Go binary, and the web interface is baked into it with //go:embed — the signed-in interface as well as the start page, the product pages and the documentation. For the interface that is exactly right: it belongs to the application, it is versioned with it, and an installation should not have to fetch anything.

For the marketing pages it was wrong, in two directions.

Anyone self-hosting Covey shipped our advertising

Covey is open source. Whoever installs it from GitHub gets a binary — and inside it were our start page, our product pages and, soon, this blog. On an instance in someone else's data centre that is not merely redundant, it is backwards: under / there belongs a sign-in, not an advertisement for a product you have just installed.

The second point is the more practical one. A website that is meant to carry content — posts, notes, case studies — changes more often than a platform does. Tying both to the same release moment means pacing the frequent thing by the rare one.

What lives where now

The cut follows one question: what does a third party's binary have to serve?

  • In the product: sign-in and the sign-up form. They talk to the API; they belong to the application.
  • On the website: start page, how it works, integrations, product pages, documentation and this blog.

The interface moves to app.covey.work; the website keeps covey.work. The other way round, every address that sits in a search engine today would have lost its position.

Documentation is the interesting case

It could have moved along — it gets read before installation and has to be findable. That is precisely what we did not do.

The documentation existed twice: eighteen operating runbooks as Markdown in the public repository, and thirteen explanatory pages as a 124 KB TypeScript file next to the website. The second one was what got served. Nobody sends a pull request against a TypeScript literal, and it showed: the runbooks were current, the website pages were not.

So the documentation now lives exactly once, as Markdown, in the public Covey repository — English as the source, German beside it under the same path. The website pulls it at build time. The dependency runs one way: the website knows the product repository, the product repository does not know the website. Whoever finds a mistake in the docs fixes it where the code is.

What we found while tidying up

Two things that had been wrong for a while:

One runbook named an environment variable that does not exist — the name had changed in a rename, and the sentence beside it additionally claimed it was "not a switch you need". It is one: it decides whether a sandbox may run on the control plane's machine at all.

And the website documentation did not mention the plugin catalogue even once, although since version 0.6.0 three target systems — Zammad, Kubernetes and the vulnerability database — are installed from there and nowhere else. Anyone reading the docs on the website and wanting to connect Zammad did not find the decisive step. It stood in the other copy.

Both are fixed. Both would probably have stood a while longer without the move — which is a fair piece of evidence for the point above: documentation nobody can change is documentation nobody changes.

Back to all posts