') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - rigbox-dev/rigbox-examples: Deployable rig.yaml + composition.yaml examples for the Rigbox platform · GitHub
Skip to content

Repository files navigation

rigbox-examples

A suite of small, recognizable products — each a different stack, each with a minimal UI in one shared design language, each demonstrating one Rigbox capability with the proper platform primitive. Every example is described by a single root rig.yaml and deploys in one command from a fresh clone:

cd<example>&& rig deploy

The suite

ExampleStackCapability it shows
quickstart/TypeScript (Hono)the canonical single-app rig.yaml, and both deploy sources — local rsync vs source: { kind: git }
ai-chat/Python · FastAPIthe managed AI proxy (ai: managed: true) + a portable model alias as a validated select param
todo-app/Next.js (React/TS) + TS APImulti-app loopback service discovery (dependsOn) + public/private visibility + volume-backed SQLite
bluegreen-blog/Ruby · Sinatrabluegreen + promote, and theme selection via a server-validated select param (not a raw env var)
webhook-receiver/Python · Flasksecrets: + a server-generated credentials: + a select param for the signing algorithm
scheduled-digest/TypeScripta background worker loop + /healthz + a number param for the schedule
url-shortener/Python · Djangothe full validated param set (url/string/number/boolean/select/email/secret/textarea) + SQLite migrations
markdown-notes/Python · Flaskworkspace volume-backed SQLite persistence + Markdown rendering

Every example deploys with the same command — rig deploy. Most rsync code and run install: on the VM. Several declare a Dockerfile, which makes rig deployfreeze the environment into an image and boot from it (see Docker builds & the hybrid deploy below). No flag: the Dockerfile is the signal.

Established products, run reproducibly

These run real, recognizable self-hosted products on Rigbox via a FROM rigbox-base Dockerfile that installs the product on top of the base. The image freezes the install; rig deploy builds it once, then reuses it. (Upstream images like postgres:16 can't be booted directly — they lack the rigbox agent + init — so each installs the product on the rigbox base instead.)

ExampleProductWhat it shows
code-server/code-server (VS Code)run an established product via a reproducible Dockerfile; settings/extensions persist under $DATA_DIR
gitea/Gitea (Git hosting)a headless single-binary service (install wizard locked) with SQLite + repos under $DATA_DIR
n8n/n8n (workflow automation)freeze a heavy npm install into the image (sizeMb bump); workflows persist under $DATA_DIR

Catalog apps

Standalone reproductions of every app in the Rigbox catalog, kept in catalog-apps/ so they don't crowd the curated example suite. Each is a regular rig deploy-able example you can fork, modify, and run on its own — see catalog-apps/README.md for the full index.

  • catalog-apps/service/ — 12 examples that expose a port + health probe (jupyter, marimo, streamlit, pgweb, excalidraw, filebrowser, openterminal, firecrawl, hermes-agent, t3code, open-webui, openclaw-gateway).
  • catalog-apps/cli/ — 6 SSH-first CLI examples (claude, codex, opencode, junie, kilocode, pi). Each declares kind: cli so there's no port, start, or health probe — the value is the CLI on SSH.

Single-app examples use the top-level name/port/start/install/health shape. Multi-app examples use a workspace: block + an apps: map, where each app carries its spec inline (port, start, install, env, health, params, dependsOn, visibility) plus a path: ./dir pointing at its code. rig deploy rsyncs and installs each app's path and brings them up in dependsOn order; rig deploy --app <name> redeploys just one.

Shared design language

All of them look like one product family. That comes entirely from design/:

  • design/tokens.css — the design tokens (iris accent, light + dark, rb-* utility classes: card / btn / input / select / pill / header / footer / badge). Every app ships a byte-identical copy and wires it up per stack (Next imports it globally; Django/Flask/Sinatra <link> it; the TS apps serve it static).
  • design/STYLE.md — the page skeleton + component rules.
  • design/CONTRACT.md — the technical contract every example follows (base-image runtime, the 0.0.0.0 + /healthz health gate, the rig.yaml schema, params/secrets/credentials, DATA_DIR persistence).

Proper primitives

The point of the suite is to model the right primitive for each job:

  • Validated config is a param with a fixed option set (type: select), not a free-form env var — the server validates it and it's live-editable with rig app param set <key>=<value>. Fixed infra (paths, base URLs) stays in env:.
  • Persistence uses a workspace.volumes declaration plus explicit app volumes: [data] opt-in. Apps write durable data under DATA_DIR=/home/developer/data, so SQLite DBs and files survive every redeploy and bluegreen cut-over.
  • Visibility is declared in rig.yaml (visibility: public / private / { emails: [...] }) so a redeploy keeps it — only an app's front door is public; siblings reach private apps over loopback via dependsOn.

Docker builds & the hybrid deploy

Most examples install their runtime on the VM with install:. The established products — code-server, gitea, and n8n — instead freeze their environment into an image with a Dockerfile (FROM rigbox-base). The command is the same — rig deploy — and a Dockerfile in rig.yaml is all it takes to switch on the image build (no flag):

  • the first deploy builds the image from the local Dockerfile (the CLI uploads the project dir as the build context — no git repo needed), boots from that frozen image, and rsyncs the code;
  • later deploys reuse the cached image when the Dockerfile/deps are unchanged and only rsync the changed code — no rebuild, no re-install.

That's the hybrid: build the slow, stable environment once; ride fast-changing code over it with rsync. See design/CONTRACT.mdReproducible builds for the full rules and when to pick which.

Layout convention

Each directory is self-contained:

single-app-example/
├── rig.yaml # the whole deploy spec, top to bottom
├── <source files>
└── README.md # what it is + what to look at after deploy
multi-app-example/
├── rig.yaml # workspace: block + apps: map (every app spec inline)
├── <app-name>/ # one dir per app — code only, no per-app manifest
└── README.md

rig.yaml's install: provisions whatever runtime the example needs at deploy time. The base VM ships python3, node, sqlite3, and build-essential; Ruby is apt-installed by the blog. Each example's own README.md covers what it demonstrates, the deploy command, what to look at afterward, and any required env.

Requirements

Latest CLI:

curl -fsSL https://rigbox.dev/install.sh | bash
rig --version

About

Deployable rig.yaml + composition.yaml examples for the Rigbox platform

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages