menotify: Scripted User-Space Sensors in Lua

The menotify sensor family loads a Lua script from the shared library tree and runs it as a long-lived event sensor. This is meant for user-space and third-party integrations where a built-in Rust listener would be too specific or would require a rebuild for each new use-case.

The listener syntax is:

<id>:
    description: <description>
    listener: menotify.<module>
    [profile]:
      - <profile>
    [interval]: <interval override>
    [opts]:
      - <opaque option>
    [args]:
      key: <opaque value>
    [tag]: <optional tag>

Synopsis

Sensor configuration as follows:

github-public-issues:
    description: Poll issues on a public GitHub repository
    listener: menotify.githubissues
    args:
        owner: your-github-user-or-org
        repo: your-public-repo
        state: open
        per_page: 20
        user_agent: sysinspect-menotify-demo

listener

The listener family root is always menotify.

The part after the dot is the Lua module name. For example:

listener: menotify.githubissues

loads:

${SYSINSPECT_SHARELIB_ROOT}/lib/sensors/lua/githubissues.lua

opts

Optional

This is a list of opaque options passed to the Lua script unchanged. Rust does not interpret them.

args

Optional

This is a dictionary of opaque arguments passed to the Lua script unchanged. Rust does not interpret them.

In current implementation, secrets are not a special API. If a scripted sensor needs a token or another secret, it is passed through args.

interval

The effective polling interval used for tick(ctx) scripts.

If the Lua script exports loop(ctx), it is called once and manages its own blocking behavior.

tag

Optional

If defined, the tag is included in the listener portion of the generated event ID.

Example:

github-public-issues|menotify.githubissues@demo|opened@42|0

Lua Contract

The script must return a table with exactly one entrypoint:

return {
    tick = function(ctx)
    end
}

or:

return {
    loop = function(ctx)
    end
}

The current ctx fields and helpers are:

  • ctx.id

  • ctx.listener

  • ctx.module

  • ctx.opts

  • ctx.args

  • ctx.interval

  • ctx.emit(data, meta?)

  • ctx.sleep(seconds)

  • ctx.now()

  • ctx.timestamp()

  • ctx.state.get(key)

  • ctx.state.set(key, value)

  • ctx.state.has(key)

  • ctx.state.del(key)

Global Lua helpers:

  • log.error(...)

  • log.warn(...)

  • log.info(...)

  • log.debug(...)

  • http.get(url, opts?)

  • http.request({...})

  • packagekit.available()

  • packagekit.status()

  • packagekit.history(names, count?)

  • packagekit.packages()

  • packagekit.install(names)

  • packagekit.remove(names)

  • packagekit.upgrade(names)

packagekit.*

Optional Linux-oriented helper

packagekit is a convenience namespace for polling PackageKit over D-Bus from Lua. It is intentionally not part of the portable core contract. If a script uses it, that script is making a Linux-specific choice.

packagekit.available()

Returns true if the PackageKit root service can be reached on the system bus.

packagekit.status()

Returns a table with current daemon state, including:

  • available

  • backend_name

  • daemon_state

  • distro_id

  • locked

  • network_state

  • transactions

  • version_major

  • version_minor

  • version_micro

packagekit.history(names, count?)

Returns PackageKit package history converted to JSON-compatible Lua tables. This is meant for polling scripts that want to detect package installs, removals, upgrades, or other recent transactions.

packagekit.packages()

Returns the current installed-package snapshot known to PackageKit. This is useful for scripts that want to diff installed packages across polling cycles and emit add/remove events.

packagekit.install(names)

Installs packages by name through PackageKit and returns the operation result as a Lua table.

packagekit.remove(names)

Removes packages by name through PackageKit and returns the operation result as a Lua table.

packagekit.upgrade(names)

Upgrades packages by name through PackageKit and returns the operation result as a Lua table.

Event Shape

ctx.emit(data, meta?) builds a standard Sysinspect sensor envelope.

Example:

ctx.emit({
    number = 42,
    title = "New issue"
}, {
    action = "opened",
    key = "42"
})

This becomes:

github-public-issues|menotify.githubissues|opened@42|0

meta currently accepts:

  • action

  • key

If omitted:

  • action defaults to emitted

  • key defaults to -

Packaging and Sync

menotify scripts are shipped as normal library/sharelib artefacts.

Current layout:

lib/
  sensors/
    lua/
      <module>.lua
      site-lua/
        ...

Publish them with the standard library upload command:

sysinspect module -A --path ./lib -l

Then sync the cluster:

sysinspect --sync

Example

The repository ships a working demo:

examples/demos/menotify/

It contains:

  • lib/sensors/lua/githubissues.lua

  • sensors.cfg

  • README.md

That example polls GitHub issues on a public repository and emits one event per newly opened issue.