Make your first capsule
You will build a capsule called scale that multiplies each integer it
receives by a configured factor, then wire it between a simulator and a logger
and run the result. The finished code is in the repository: the capsule in
capsules/scale/scale_capsule.c and the project in examples/first_capsule/.
You need CMake 3.14 or newer, a C11 compiler and pthreads, on Linux or macOS.
1. Build the host with the capsule
Section titled “1. Build the host with the capsule”The SIG_CAPSULES option lists the capsules compiled into the host. Setting it
replaces the default list, so name every capsule you need: the simulator
produces values, scale changes them, and the logger prints them.
cmake -S . -B build -DSIG_CAPSULES="counter;simulator;logger;scale"cmake --build build2. The capsule
Section titled “2. The capsule”A capsule is a sig_capsule value: a name, tables of input and output
terminals, and a table of the properties it accepts. scale has one property,
factor, an integer that defaults to 2 and must be at least 1.
enum { SCALE_FACTOR };
static const sig_property scale_properties[] = { [SCALE_FACTOR] = SIG_PROP_INT_MIN("factor", 2, 1, "Number each incoming int is multiplied by"),};The input terminal’s handler runs on the service’s worker thread for each signal. It reads the incoming value, multiplies it by the property and sends the result on the output terminal. A value that is not an int, or whose product does not fit in an int, is dropped with a warning.
static sig_status_t scale_signal(sig_capsule_instance *instance, const sig_value *signal, const char *terminal_name) { sig_int_t n; sig_int_t product; sig_value scaled;
(void) terminal_name;
if (sig_value_get_int(signal, &n) != SIG_OK) { sig_log_warn("scale %s: dropping a signal that is not an int", instance->name); return SIG_OK; }
if (__builtin_mul_overflow(n, sig_capsule_instance_int(instance, SCALE_FACTOR), &product)) { sig_log_warn("scale %s: dropping a signal whose scaled value overflows", instance->name); return SIG_OK; }
scaled = sig_value_int(product); return sig_capsule_instance_notify(instance, SIG_TERMINAL_DEFAULT, &scaled);}Finally the capsule itself ties the pieces together: one default input terminal wired to the handler, one default output terminal, and the property table.
const sig_capsule scale_capsule = { "scale", 1, scale_input_terminals, 1, scale_output_terminals, NULL, NULL, sizeof(scale_properties) / sizeof(scale_properties[0]), scale_properties, .category = "flow",};Writing capsules covers terminals, properties, start and stop hooks and scheduled work in full.
3. Register it
Section titled “3. Register it”The host only knows the capsules compiled into it. The repository already has
scale registered; for a capsule of your own, do the same three things:
- Put it in
capsules/<name>/<name>_capsule.cwith a header that declaresextern const sig_capsule <name>_capsule;. Here that iscapsules/scale/scale_capsule.h. - Add
<name>to theSIG_CAPSULESlist, as in step 1. - Add a block to
capsules/sig_builtin_capsules.cthat includes the header and registers the capsule. Forscalethe block is:
#ifdef SIG_CAPSULE_SCALE status = sig_capsule_registry_register(reg, &scale_capsule); if (status != SIG_OK) { return status; }#endifwith a matching #include "scale_capsule.h" under #ifdef SIG_CAPSULE_SCALE
near the top of the file.
4. Wire it
Section titled “4. Wire it”A project is a directory with a configurations/ folder and a services/
folder. Each configuration names a capsule type and sets its properties. The
file name is the configuration name.
configurations/Sim.cfg is a simulator that counts from 0 to 10 and then wraps
back to the start, one value every 500 ms:
{"type":"simulator","attr_value":{"end":10,"interval_ms":500}}configurations/Scale.cfg is a scale capsule with a factor of 10:
{"type":"scale","attr_value":{"factor":10}}configurations/Log.cfg is a logger:
{"type":"logger"}services/first.cfg is the service. It starts automatically and lists the three
configurations as instances. Each instance’s receivers connect an output
terminal to an input terminal of another instance: Sim feeds Scale, and
Scale feeds Log.
{"auto_start":true,"execution":[{"name":"Sim","receivers":{"__default_terminal_value":[{"input":"__default_terminal_value","name":"Scale"}]}},{"name":"Scale","receivers":{"__default_terminal_value":[{"input":"__default_terminal_value","name":"Log"}]}},{"name":"Log"}]}Configuration describes every field.
5. Run it
Section titled “5. Run it”sighost loads the project and runs it until it receives SIGINT or
SIGTERM. Its default log level is trace, which also prints a line for every
signal, so this run uses -l info:
build/host/posix/sighost -p examples/first_capsule -l infoThis is the output of a run that was stopped with SIGTERM after about three
seconds. The simulator emits 0, 1, 2 and so on; scale multiplies each by 10;
the logger prints the result. Timestamps will differ.
2026-10-26T16:05:47.836Z INFO host/sig_host.c:53: sig_host: opened file store "examples/first_capsule"2026-10-26T16:05:47.837Z INFO src/runtime/sig_runtime.c:393: runtime: starting (project in the 'file' store, 0 module(s))2026-10-26T16:05:47.837Z INFO src/project/sig_project.c:419: loading project from the 'file' store2026-10-26T16:05:47.838Z INFO src/project/sig_project.c:448: loaded project: 3 capsule(s), 1 service(s)2026-10-26T16:05:47.838Z INFO src/runtime/sig_service.c:662: service 'first' started (3 instances)2026-10-26T16:05:47.838Z INFO src/project/sig_project.c:513: auto-start complete: 1 service(s) started2026-10-26T16:05:47.838Z INFO src/runtime/sig_runtime.c:437: runtime: running2026-10-26T16:05:48.344Z INFO capsules/logger/logger_capsule.c:28: logger Log: 02026-10-26T16:05:48.843Z INFO capsules/logger/logger_capsule.c:28: logger Log: 102026-10-26T16:05:49.340Z INFO capsules/logger/logger_capsule.c:28: logger Log: 202026-10-26T16:05:49.842Z INFO capsules/logger/logger_capsule.c:28: logger Log: 302026-10-26T16:05:50.343Z INFO capsules/logger/logger_capsule.c:28: logger Log: 402026-10-26T16:05:50.843Z INFO capsules/logger/logger_capsule.c:28: logger Log: 502026-10-26T16:05:51.043Z INFO host/posix/sig_host_posix.c:62: host: received signal 15, requesting shutdown2026-10-26T16:05:51.043Z INFO src/runtime/sig_runtime.c:467: runtime: stopping2026-10-26T16:05:51.043Z INFO src/project/sig_project.c:538: stopping all services2026-10-26T16:05:51.043Z INFO src/runtime/sig_service.c:753: service 'first' stopped2026-10-26T16:05:51.043Z INFO src/runtime/sig_runtime.c:490: runtime: stoppedChange factor in configurations/Scale.cfg and run again to see the logged
values change.
Next steps
Section titled “Next steps”- Writing capsules: terminals, properties, hooks and scheduled work.
- Management API: inspect and control a running host. Build with
-DSIG_NET=ON -DSIG_HTTP_SERVER=ON -DSIG_MGMT=ONand startsighostwith-m 8080to use it over REST. - Examples: fan-in, fan-out, shared configurations and editing in the web UI.