How the server hands out the kribble CLI, the editor extensions and the desktop app, how they update, how to build the desktop app, and how to run your own downloads site
Each Kribble server hands out the tools that go with it: the kribble command-line tool, the VS Code and JetBrains extensions and, if you add them, the desktop apps. They come from the same release as the server, so their versions match. How people use them is in The kribble CLI and The desktop app.
kribble-web serves the contents of [downloads].dir (by default downloads in its working directory: /opt/kribble/downloads with install.sh, /app/downloads in Docker):
Path | What |
|---|---|
| Shell installer for the CLI on macOS and Linux |
| PowerShell installer for the CLI on Windows |
| The release version and the list of files, with SHA-256 checksums |
| A file listed in the manifest. Anything not listed returns 404. |
| The file's checksum |
| A JetBrains plugin repository for the Kribble Markdown plugin |
Signed-in users find install commands and the desktop installers on the CLI & agents page of the web app.
The directory comes from the release:
Kind | Files | Built by |
|---|---|---|
CLI |
|
|
VS Code extension |
|
|
JetBrains plugin |
|
|
Desktop app | installers and updater files | Built per OS and added with |
install.sh replaces /opt/kribble/downloads with the tarball's copy on every install and update. The Docker image built from deploy/Dockerfile contains only the CLI. Without the directory the server works, but offers nothing to download.
On any computer:
curl -fsSL https://kribble.example.com/install.sh | sh # macOS, Linux
irm https://kribble.example.com/install.ps1 | iex # Windows PowerShell
The script downloads the build for the computer, checks its SHA-256, installs it (~/.local/bin, or /usr/local/bin as root, or %LOCALAPPDATA%\Programs\kribble on Windows; KRIBBLE_INSTALL_DIR overrides it) and saves the server's address, so kribble login needs no --server.
The scripts contain the server's address as the client used it: the Host (or X-Forwarded-Host) and X-Forwarded-Proto headers your proxy passes. If they print http:// or an internal host name, fix the proxy (Installing).
JetBrains IDEs can install and update the plugin from the server: add https://kribble.example.com/downloads/jetbrains/updatePlugins.xml under Settings → Plugins → ⚙ → Manage Plugin Repositories. kribble extension install installs the extensions from the server into VS Code-based editors and JetBrains IDEs.
The CLI. kribble update downloads the version the active profile's server offers, so the CLI matches the server. Without any server set up, it updates from https://getkribble.com. When the CLI and the server versions differ, the CLI prints a note asking the user to run kribble update.
The desktop app checks for updates at <server>/downloads/ of the active profile (electron-updater's generic provider, reading latest*.yml). Before any server is added, it uses https://getkribble.com. Updates download in the background and install when the app quits.
So when you deploy a new release, the CLIs and desktop apps connected to your server move to it. If the release has no desktop files, installed apps stay as they are.
The desktop app is built separately on each OS, then added to the release tarball. Build every OS from the same commit as the server release. The app's version comes from git tags: v1.4.0 builds 1.4.0, and later commits build 1.4.1-N.
At the repository root:
pnpm build:release
This writes dist/kribble-linux-amd64.tar.gz and, next to it, dist/add-desktop.mjs.
Each needs the repository, Node, pnpm and Go:
Command | Output in |
|---|---|
| dmg and zip for Apple silicon, signed and notarized when configured |
| the x64 installer |
| AppImage and deb, x64 and arm64 |
| the Linux build in a container, from a Mac with Docker |
Ship all the files, including the updater's (latest*.yml, .blockmap, the mac .zip).
Copy the files next to dist/add-desktop.mjs and run:
node dist/add-desktop.mjs
It adds them to the tarball's downloads/ and its manifest. Adding a platform again replaces that platform's files. Then deploy the tarball as usual.
The Release workflow in .github/workflows/release.yml does all of this on GitHub when started by hand (Actions → Release → Run workflow), and attaches the tarball to a draft release. It signs and notarizes the Mac app when these repository secrets are set: MAC_CERT_P12_BASE64, MAC_CERT_PASSWORD, APPLE_API_KEY_P8, APPLE_API_KEY_ID, APPLE_API_ISSUER.
pnpm build:desktop:mac reads .env at the repository root (it is git-ignored). Without these settings it still builds, unsigned or not notarized, and says so.
Variable | What it is |
|---|---|
| Which Developer ID Application certificate in the login keychain to use, if there are several, for example |
| On another machine or CI: the certificate exported as |
| Notarization with an App Store Connect API key: the path of the |
| Notarization with an Apple ID and an app-specific password instead |
Signing also needs the Xcode command line tools (xcode-select --install). desktop/DESKTOP.md in the repository has the step-by-step for getting the certificate and the API key.
kribble-www is a small separate server for a public downloads site like getkribble.com: a landing page with the CLI, its install commands and the desktop apps, plus /install.sh, /install.ps1, /downloads/*, llms.txt, sitemap.xml and /healthz. It has no database or config file and serves the downloads/ of the release it was installed from. It doesn't need a Kribble server.
Its install scripts don't set a server, so people installing from it sign in with kribble login --server <url>. The CLI and the desktop app use getkribble.com, which is built into them, for updates until a server is set. Your own downloads site is not used for those updates.
The release tarball includes it. Add the desktop apps to the tarball first if the site should offer them. Then:
tar xzf kribble-linux-amd64.tar.gz
sudo bash install-www.sh
This installs /opt/kribble-www/kribble-www and /opt/kribble-www/downloads, creates the user kribble-www and the unit kribble-www.service, and starts it on port 8080. KRIBBLE_WWW_PORT and INSTALL_DIR change the port and location. Run it again with a newer tarball to update.
kribble-www takes --addr (or KRIBBLE_WWW_ADDR, default :8080) and --downloads (or KRIBBLE_WWW_DOWNLOADS, default downloads). Put a TLS proxy in front that passes the host and scheme, so the install commands on the page carry the right address:
downloads.example.com {
reverse_proxy localhost:8080
}
kribble-www --export writes the site as static files instead: the page and install scripts for Cloudflare Pages, and the downloads, which are too big for Pages' 25 MB file limit, for an R2 bucket. /downloads/* on the site redirects to the bucket, and the install scripts, kribble update and the desktop app follow the redirect.
From a release tarball (with the desktop apps added, if wanted):
pnpm build:www # uses dist/kribble-linux-amd64.tar.gz
pnpm build:www ~/Downloads/kribble-linux-amd64.tar.gz --site https://downloads.example.com --files https://dl.example.com
--site and --files default to https://getkribble.com and https://dl.getkribble.com. The result is dist/www/files (for R2) and dist/www/site (for Pages, with _redirects and _headers). Without --files, kribble-www --export puts the downloads into the site itself, for static hosts that accept large files.
Set up Cloudflare once:
Move the domain's DNS to Cloudflare.
Create an R2 bucket and connect the files domain (dl.example.com) to it as a custom domain. The r2.dev address doesn't go through the cache.
Add a Cache Rule for the files domain that makes requests eligible for cache. Cloudflare doesn't cache files without an extension, like kribble-linux-amd64, otherwise. Downloads served from the cache don't count as R2 reads.
Create a Pages project for the site's domain.
Optionally add a rate-limiting rule on the files domain and a billing notification. R2 has no spending cap. Past the free tier it charges for reads and storage, not traffic.
For each release, upload the files before the site, so the new page never links to files that aren't there yet:
rclone sync dist/www/files r2:<bucket> --header-upload "Cache-Control: public, max-age=300"
npx wrangler pages deploy dist/www/site --project-name <pages-project>
Then purge the cache, because the CLI files keep the same names from release to release. rclone sync also deletes the previous release's files. Files uploaded from the Cloudflare dashboard have no Cache-Control, so set an Edge TTL (for example an hour) in the cache rule if you upload that way.