Quick Start
Get up and running in under 2 minutes.
1. Download & install
Download rukkie-setup.exe from the latest GitHub release. Run it — it installs the binary and adds it to your system PATH automatically.
2. Create rukkie.yaml in your project
project: my-backend observability: jaeger: url: http://localhost:16686 environments: dev: services: - name: auth-service url: http://localhost:3000 type: REST endpoints: - path: /health method: GET expect_status: 200
3. Log in and scan
$ rukkie login Password: •••••••• ✅ Logged in to RukkiePulse $ rukkie scan
Configuration
rukkie.yaml lives in the root of your project.
Full example
project: my-backend observability: jaeger: url: http://localhost:16686 environments: dev: services: - name: auth-service url: http://localhost:3000 type: REST endpoints: - path: /login method: POST body: '{"email":"test@test.com","password":"test"}' expect_status: 200 - name: graphql-api url: http://localhost:4000/graphql type: GRAPHQL endpoints: - query: '{ __typename }' expect_no_errors: true production: services: - name: auth-service url: https://auth.myapp.com type: REST
| Field | Description |
|---|---|
| project | Display name shown in CLI output |
| observability.jaeger.url | Jaeger Query UI URL for rukkie trace |
| environments.<name> | Named environment (dev, staging, production…) |
| services[].name | Display name |
| services[].url | Base URL of the service |
| services[].type | REST or GRAPHQL |
| endpoints[].path | URL path to probe (REST) |
| endpoints[].method | HTTP method — default: GET |
| endpoints[].body | JSON request body |
| endpoints[].expect_status | Expected HTTP status — default: 200 |
| endpoints[].query | GraphQL query string |
| endpoints[].expect_no_errors | Fail if response contains errors |
Commands
All commands require a rukkie.yaml in the current directory, except login and logout.
rukkie login
Prompts for a password, generates a local JWT, and stores the session at ~/.rukkie/config.yaml. Sessions last 30 days.
rukkie scan rukkie scan --env production rukkie scan --errors-only
| Flag | Default | Description |
|---|---|---|
| --env, -e | dev | Environment to use |
| --errors-only | false | Show only failing or degraded services |
rukkie status
rukkie status --errors-only
rukkie inspect auth-service
rukkie inspect auth-service --env production
Shows health status, dependency connections, and a per-endpoint breakdown with latency and status codes.
# Latest trace for a service rukkie trace auth-service # Filter to a specific endpoint rukkie trace auth-service /login # Specific trace by ID rukkie trace auth-service --trace-id abc123def456 # Show last 5 traces rukkie trace auth-service --last 5 # Render as flame graph rukkie trace auth-service /login --flame
| Flag | Default | Description |
|---|---|---|
| --trace-id | — | Fetch a specific trace by ID |
| --last | 1 | Number of recent traces to show |
| --flame | false | Render as horizontal flame graph |
⚠️ Requires Jaeger to be running and observability.jaeger.url set in rukkie.yaml.
rukkie watch rukkie watch --interval 5s rukkie watch --env production --interval 30s
| Flag | Default | Description |
|---|---|---|
| --interval | 10s | Refresh interval. Accepts 5s, 1m, etc. |
| --env, -e | dev | Environment to use |
Press Ctrl+C to stop.
rukkie logout
rukkie init
Run inside any backend project to create rukkie.yaml and print the integration snippet for that language.
$ cd my-backend-project $ rukkie init ✅ Created rukkie.yaml for "my-backend-project" (node) Replace YOUR_API_KEY with a key from the dashboard: https://rukkiepulse-dashboard.netlify.app ── ESM ──────────────────────────────────────────────── import { initRukkie } from 'rukkie-agent' initRukkie({ serviceName: 'my-backend-project', apiKey: 'YOUR_API_KEY', }) ── CommonJS ─────────────────────────────────────────── const { initRukkie } = require('rukkie-agent') initRukkie({ serviceName: 'my-backend-project', apiKey: 'YOUR_API_KEY', }) Install: npm install rukkie-agent
Auto-detects Node.js (package.json), Python (pyproject.toml / requirements.txt), and Go (go.mod).
The generated rukkie.yaml:
service: name: my-backend-project language: node apiKey: YOUR_API_KEY observability: jaeger: url: http://localhost:16686 collector: http://localhost:4317
Agent SDKs
Drop one line into your service. The agent auto-instruments requests, exposes /__rukkie/health, pushes traces to Jaeger, and pings the dashboard so you can see it as Live.
Node.js
npm install rukkie-agent
// ESM (recommended) import { initRukkie } from 'rukkie-agent' initRukkie({ serviceName: 'auth-service', apiKey: 'rk_live_xxx', }) // CommonJS const { initRukkie } = require('rukkie-agent') initRukkie({ serviceName: 'auth-service', apiKey: 'rk_live_xxx', })
Auto-detects Express and Fastify. Pass app as the second argument to enable middleware and GET /__rukkie/health.
Python
pip install rukkie-agent
# FastAPI / Flask from rukkie_agent import init_rukkie init_rukkie( service_name="auth-service", api_key="rk_live_xxx", )
Auto-detects FastAPI and Flask. Same capabilities as the Node.js agent.
Jaeger Setup
Required for rukkie trace. Runs locally in Docker — no account or API key needed.
Start Jaeger (all-in-one)
docker run -d --name jaeger \ -p 16686:16686 \ -p 4317:4317 \ jaegertracing/all-in-one:latest
| Port | Purpose |
|---|---|
| 16686 | Jaeger UI + Query API (used by the CLI) |
| 4317 | OTel gRPC endpoint (used by agents) |
Add to rukkie.yaml:
observability: jaeger: url: http://localhost:16686
Set collectorUrl in your agents to http://localhost:4317 (this is already the default).
API Keys & Service Registration
Each backend service you monitor needs an API key so RukkiePulse can identify it. Keys are generated in the dashboard — they are shown only once, then stored as a secure hash.
Register services, generate & revoke API keys, view all connected backends.
Steps
- Open the dashboard and sign in.
- Click + New Service and fill in the service name and language.
- On the service page click + Generate API Key — copy the key immediately, it won't be shown again.
- Add the key to your service using the agent SDK:
Node.js
import { initRukkie } from 'rukkie-agent' initRukkie({ serviceName: 'auth-service', apiKey: 'rk_live_…', })
Python
from rukkie_agent import init_rukkie init_rukkie( service_name="auth-service", api_key="rk_live_…", )
To rotate a key: go to the service page → click Revoke on the old key → click + Generate API Key for a new one.
Scanning Without rukkie.yaml
rukkie scan and rukkie watch now work from any directory.
If no rukkie.yaml is found, they fall back to the cloud and fetch all services
you have registered in the dashboard — no config file needed.
$ rukkie scan RukkiePulse connected services ──────────────────────────────────────────────────────────────── 🟢 Clockee Server node Live last seen 1m ago 2 key(s) 🔴 My API python Inactive last seen 2026-03-10 14:30 ⚫ New Service go Never connected ──────────────────────────────────────────────────────────────── 🟢 1 live · 🔴 1 inactive · ⚫ 1 never connected
rukkie watch opens a live-updating TUI that refreshes from the cloud on the configured interval:
$ rukkie watch --interval 30s # live dashboard, refreshes every 30s
The connection status is determined by the most recent heartbeat received from the service's API key:
- 🟢 Live — heartbeat received < 5 minutes ago
- 🟡 Recent — heartbeat received < 1 hour ago
- 🔴 Inactive — no heartbeat for > 1 hour
- ⚫ Never connected — API key created but no heartbeat ever received
Full Integration Guide — Express + PostgreSQL
A step-by-step walkthrough of every change made to wire RukkiePulse observability deep into an existing Express server. All examples are taken from the Clockee Server integration (Node.js / Express 5 / PostgreSQL / Socket.io).
Step 1 — Install the agent
$ npm install rukkie-agent
Step 2 — Register the service & get an API key
- Sign in at the dashboard.
- Click + New Service — enter name and language.
- On the service page click + Generate API Key and copy it immediately.
- Add to your
.env:RUKKIE_PULSE_API_KEY=rk_live_…
Step 3 — Bootstrap in app.js (before any middleware)
const { initRukkie } = require('rukkie-agent'); // Call BEFORE cors(), body-parser, and routes initRukkie({ serviceName: 'Clockee Server', apiKey: process.env.RUKKIE_PULSE_API_KEY, dependencies: { // Probed on every GET /__rukkie/health request database: async () => { const client = await pool.connect(); await client.query('SELECT 1'); client.release(); }, }, }, app);
This single call:
- Boots the OTel SDK (HTTP + Express auto-instrumentation)
- Fires a startup heartbeat so the dashboard shows the service as Live
- Attaches per-request span middleware to Express
- Registers
GET /__rukkie/healththat probes all listed dependencies
Step 4 — Create the observability tracer utility
Create middleware/observability/tracer.js — thin wrappers around the OTel API for custom spans:
const { withSpan, recordError } = require('./middleware/observability/tracer'); // Wrap any async operation in a named span const result = await withSpan('my-operation', { 'custom.attr': 'value' }, async () => { return await doSomething(); }); // Record an error on the current active span (call from catch blocks) catch (err) { recordError(err); throw err; }
Step 5 — Wrap the database pool for per-query tracing
In config/database.js, monkey-patch pool.query() so every SQL call creates an OTel span:
const { withSpan } = require('../middleware/observability/tracer'); const _originalQuery = pool.query.bind(pool); pool.query = function tracedQuery(textOrConfig, values, callback) { // callback-style: pass through unchanged if (typeof values === 'function' || typeof callback === 'function') { return _originalQuery(textOrConfig, values ?? callback, callback); } // promise-style: wrap in a span const sql = typeof textOrConfig === 'string' ? textOrConfig : textOrConfig.text; return withSpan(`db.query ${sql.slice(0, 120)}`, { 'db.system': 'postgresql', 'db.statement': sql, }, () => values ? _originalQuery(textOrConfig, values) : _originalQuery(textOrConfig)); };
Every query through the pool now appears as a child span in Jaeger — you can see the exact SQL, duration, and any errors.
Step 6 — Record errors in the global error handler
Add recordError() to the top of your Express error handler:
const { recordError } = require('../observability/tracer'); const errorHandler = (err, req, res, next) => { recordError(err, { 'http.route': req.path, 'http.method': req.method, 'error.user_id': req.user?.id || 'anonymous', }); // ... rest of your handler };
Step 7 — Add a periodic heartbeat in server.js
The agent pings once on startup. Add a 2-minute interval so the dashboard stays Live even between requests:
function startHeartbeat() { const apiKey = process.env.RUKKIE_PULSE_API_KEY; if (!apiKey) return; const ping = () => fetch(HEARTBEAT_URL, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}` }, }).catch(() => {}); ping(); setInterval(ping, 2 * 60 * 1000); } server.listen(PORT, () => { // ... existing startup logs startHeartbeat(); });
Step 8 — Create rukkie.yaml in the service root
service: name: Clockee Server language: node apiKey: rk_live_… observability: jaeger: url: http://localhost:16686 collector: http://localhost:4317
What gets traced after integration
- Every HTTP request — method, route, status code, duration
- Every PostgreSQL query — operation, SQL statement, database name
- Every outgoing HTTP call — to Twilio, Cloudinary, Paystack, etc.
- Every unhandled error — exception + stack recorded on the active span
- Socket.io DB calls — because they use the same traced pool, all their queries appear in traces automatically