Skip to content

Operations

Two features for background work and operator-controlled feature gates.

Jobs: cron, event and manual background work with run history

Status: ✅ Stable

What: Persistence and operator tooling for jobs registered with r.job(...). Built on BullMQ + Redis with lane routing (runIn: "api" | "worker"). Every run is written to store_job_runs / store_job_run_logs.

How it works: r.job(name, { trigger, runIn?, … }, handler) registers a worker. Handler signature is (payload, context), not (ctx) alone.

TriggerHow it fires
{ manual: true }jobs:write:trigger (operator / SystemAdmin) or JobRunner.dispatch(name, payload)
{ cron: "0 9 * * *" }Leader-elected schedule, read in the server’s zone
{ cron: "0 9 * * *", tz: "Europe/Berlin" }Same schedule, read in the given IANA zone
{ on: writeHandlerOrEvent }After the named write/event

There is no ctx.jobs.enqueue. Manual runs go through the trigger write handler or the job runner.

Cron time zone: Without tz, the cron fields are read in the zone of the server process, which is UTC in the standard containers. Set tz to an IANA name to schedule in local time, daylight saving included. An invalid zone aborts the boot. A wall-clock time that a clock change skips runs once, shifted by the size of the gap. A time that occurs twice runs once, at its first occurrence.

r.job(
"morning-digest",
{ trigger: { cron: "0 9 * * *", tz: "Europe/Berlin" }, runIn: "worker" },
async (_payload, ctx) => {
// runs at 09:00 Berlin time all year, 07:00 or 08:00 UTC
},
);

Example:

import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
defineFeature("ops", (r) => {
r.job(
"cleanup-old-attempts",
{ trigger: { cron: "0 3 * * *" }, runIn: "worker" },
async (_payload, ctx) => {
// use ctx.db / ctx.notify / …, payload first, context second
},
);
r.job(
"send-incident-email",
{ trigger: { manual: true } },
async (payload, ctx) => {
await ctx.notify!("incident-email", {
to: payload["recipientId"] as string,
data: { incidentId: payload["incidentId"] },
});
},
);
});
// Trigger a manual job (SystemAdmin), not ctx.jobs.enqueue
await ctx.dispatch.write("jobs:write:trigger", {
jobName: "ops:job:send-incident-email", // qualified name as registered
payload: { incidentId: id, recipientId },
});

Status: ✅ Stable

What: Global (not per-tenant) enable/disable for features that opt in with r.toggleable({ default }). Operators flip state via feature-toggles:write:set; the dispatcher gates disabled features (feature_disabled). State lives in store_global_feature_state.

How it works: Declare r.toggleable({ default: false }) once on the feature. There is no ctx.toggles, when a feature is off, its handlers are unreachable and cross-feature hooks owned by that feature are skipped. Load createFeatureTogglesFeature({ getRuntime }) and wire GlobalFeatureToggleRuntime into the server’s effectiveFeatures callback (see the recipe).

Best practice: when a toggle is fully rolled out, delete it from code. In tests set toggle state explicitly.

Recipe: samples/recipes/feature-toggles

Example:

import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
import { z } from "zod";
export const billingFeature = defineFeature("billing", (r) => {
r.toggleable({ default: false });
// When "billing" is globally off, this handler never runs :
// the dispatcher returns feature_disabled. No ctx.toggles check.
r.queryHandler(
"invoice-list",
z.object({}),
async (_query, ctx) => {
/* list invoices */
},
{ access: { roles: ["Admin"] } },
);
});

What: Dashboard data. Each metric declaration becomes a query that aggregates a table and returns a value, a trend and rows for dashboard panels. metrics serves the caller’s own tenant, metrics-system serves system admins across tenants. admin-shell builds its overview pages from them.

These are not the Prometheus metrics from r.metric. Setup, custom metrics and dashboard panels are in Metrics and dashboard data.

What: Read-only screens for plan limits. Tenant admins see a card per cap with usage and limit, system admins get a list of caps across tenants. admin-shell adds both to its navigation when the feature is mounted. Plans, caps and showLimit are in Billing plans and checkout, the options in the feature reference. The framework’s cap overview reference (German) covers the design decisions.