Bondy Connect
Skip to content

Alarms ​

Read access to the alarm subsystem: the conditions currently true across the cluster, one condition by id, this node's transition history, and the catalogue of everything this build can raise.

Read-only by construction. There is no acknowledge, no silence and no clear. An alarm states a condition that is true now; clearing one without fixing the condition would make the surface lie, and silencing belongs in Alertmanager rather than in the router.

Master realm only. All four procedures require admin authority. A class = realm alarm still carries realm_uri, but that field names the affected tenant for an operator — it does not grant that tenant access.

Authorisation is the ordinary wamp.call permission the dealer applies to every call, so these procedures are grantable and revocable like any other.

Procedures ​

Reading the current state ​

bondy.alarm.list() -> [envelope] ​

Every alarm raised across the cluster.

Returns one envelope:

KeyTypeMeaning
alarmslistEvery raised alarm, from every node that answered.
nodes.answeredlist of stringThe members that replied.
nodes.silentlist of stringThe members that did not, within a 5 second budget for the whole fan-out.

answered and silent partition the cluster membership; no member falls out of both. An empty alarms with an empty silent means the cluster is clean. An empty alarms with a non-empty silent means nothing is known about those nodes — the two are different answers and a caller that conflates them eventually pages on the wrong one.

CALL.Options._deadline (milliseconds from now) caps the fan-out budget. A caller can only shorten the wait: a deadline says when to give up, not how long the router may take. The same applies to bondy.alarm.get.

This node's own alarms are read directly and never depend on the fan-out, so a total cluster-transport failure still answers for the node you are talking to and reports every peer silent.

bondy.alarm.get(id) -> [envelope] ​

The same envelope, filtered to one alarm id.

id is the wire id: a string for an atom id, or a list of strings for a tuple id (["mail_relay_down", "smtp1"]). The argument is compared against the rendered id rather than decoded back into a term, which avoids creating atoms from caller-supplied input.

One id may be raised on several nodes at once, so the reply says where the condition holds. A miss is an ordinary empty alarms list rather than an error: with a non-empty silent set, "no node reports it" is genuinely uncertain, and an error would state the opposite.

bondy.alarm.history() -> [page] ​

Alarm transitions across the cluster, newest first, as a page.

Answers the standard page shape — values, has_more, and cursor only when there is more — like every paginated Bondy procedure, plus one key of its own:

KeyTypeMeaning
not_reachedlist of stringThe members this walk asked for history and did not hear from. Always present, [] when the walk heard from everything it asked.

Each value is a History event, and each names the node whose ring recorded it.

Keyword ArgTypeMeaning
limitintegerPage size, default 100, maximum 1000. An out-of-range value falls back to the default.
cursorstringResume from a previous page's cursor. A cursor this procedure did not mint is refused.
CALL OptionTypeMeaning
receive_progressbooleanStream instead of paging — see below.
_deadlineintegerMilliseconds from now after which to give up. Caps the walk's own time budget — see below.

limit and cursor are keyword arguments, not CALL.Options: they are arguments to the procedure, and Bondy is the callee for bondy.*. receive_progress and _deadline stay options because they are instructions to the router about how the reply is delivered and how long it may take. Bondy does not read the _limit and _cursor options.

Raises wamp.error.invalid_argument if this procedure did not mint cursor, or if an incompatible release minted it. The error details.reason is stale or malformed. In both cases, restart from the first page: omit cursor. A resume position cannot be guessed.

The walk is node-at-a-time, starting with the node serving the call and reaching its peers only when that node's ring does not fill the page. That is what makes the common case free: asking for 100 transitions on a node whose ring holds 100 contacts no peer at all, and the peers are reached only if you ask for the next page.

It also means a busy node can crowd a quiet peer out of the first page. The absence of a node from a page is not evidence that nothing happened on it — the walk may simply not have reached that node yet. For a cluster-wide answer in a single reply, use bondy.alarm.list.

A node the walk asked and did not hear from is named in not_reached. A peer that cannot answer contributes nothing and the walk continues, but it does not do so silently: a page that is short because a node was unreachable is not the same answer as a page that is short, and only the reply can tell you which you have.

not_reached accumulates. A node named on one page stays named on every later page of the same walk — the set rides in the cursor — so the last page of a walk states the whole truth about it. Nodes the walk has not yet asked are not in the set: they are asked on a later page, and naming them now would mean un-naming them later, which an accumulating set cannot do.

A page is bounded in time as well as in size. The whole page shares one 5 second budget, and CALL.Options._deadline caps it. Whatever is left of the budget is the timeout for the next node, so a walk across several unreachable peers costs one budget rather than one each.

A page whose budget runs out stops early, and its cursor resumes at the nodes it did not get to — nothing is skipped. The first node of a page is always contacted, so a page can be cut short but never emptied, and paging always advances.

A node still to be walked that leaves the cluster is dropped, not reported. A cursor names the nodes the walk has yet to reach, and they are intersected with the membership when it is resumed. Nothing failed — history is never replicated, so a departing node takes its ring with it — and the position the cursor held in that node's ring goes with it, because a seq means nothing on another node's ring. A node already named in not_reached stays named: leaving does not retract what an earlier page reported.

A page may report has_more and be followed by an empty one — the walk cannot know a node is exhausted without asking it.

A restatement that changes nothing is not a transition and does not appear. The ring bounds transitions, not time, and is not merged across nodes: within a page the transitions of one node are contiguous, so sort by at if you want a cluster-wide timeline.

Streaming instead of paging

A caller that announced progressive_call_results in its HELLO and sets receive_progress on the CALL receives the whole walk as progressive results — one WAMP RESULT per page, the last one final — and never handles a cursor. Each chunk carries the same values, has_more and not_reached keys a page does, so one parser serves both, and no cursor: there is nothing to resume.

Set _deadline on a stream. CALL.Options.timeout is an inactivity window that every chunk restarts, so a slowly-dripping stream is otherwise unbounded; _deadline is what bounds the whole of it. The first page always runs and the deadline is checked only after a chunk has gone out, so it can shorten a stream and never empty one. A stream that runs out of deadline settles with wamp.error.timeout rather than a final result: a truncated stream marked has_more: false would claim to be complete. The chunks already delivered are good as far as they go.

bondy.alarm.catalogue() -> [catalogue] ​

Every alarm condition this build can raise, as {"entries": [...]}. This is the runtime form of the Alarm Catalogue and is authoritative for the node answering.

Takes no arguments and contacts no peer.

Payloads ​

Alarm ​

The shape returned by list and get, and published on the three event topics. One shape for both, so a subscriber and a poller parse the same map.

KeyTypeMeaning
idstring | listThe concrete alarm id.
catalogue_idstring | list | nullThe id pattern of the catalogue entry this alarm matches — the join key into the catalogue. null only for an id no entry declares.
nodestringThe node the condition holds on.
descriptionanyThe producer's description. Rendered readable if it is not directly encodable.
severitystringwarning, major or critical.
classstringnode, cluster, realm or integration.
affects_readybooleanWhether this alarm takes the node out of rotation.
detailsmapStructured detail. The keys a catalogue entry declares are delivered; a producer may carry more.
raised_atintegerMilliseconds since the epoch, of the first raise. Survives every restatement.
updated_atintegerMilliseconds since the epoch, of the last content change.
realm_uristringPresent on class = realm alarms; names the affected tenant.
onset_trace_idstringPresent when the raising occurrence carried a W3C trace. Names the first occurrence and survives restatement.

Any value a producer supplies that a JSON encoder could not represent is rendered readable rather than allowed to reach the encoder, so a reply about one fault cannot fail on another.

History event ​

KeyTypeMeaning
idstring | listThe alarm id.
actionstringraised, updated or cleared.
severitystringThe severity at the moment of the transition.
atintegerMilliseconds since the epoch.
nodestringThe node whose ring recorded the transition.
seqintegerPosition in that node's ring: strictly increasing, newest highest. Comparable only against transitions from the same node.

seq is what makes the ring pageable — at is a millisecond timestamp and is neither unique nor monotonic. It restarts when a node restarts, along with the ring itself.

Catalogue entry ​

KeyTypeMeaning
id_patternstring | listThe pattern this entry matches. "_" in a list position matches any value.
severity, class, affects_readyJoined onto every alarm this entry matches, at raise time.
summarystringWhat the condition is.
detail_keyslist of stringThe details keys this entry promises.
config_keyslist of stringThe configuration governing the condition.
observe_withlist{"kind": "procedure"|"metric", "ref": ...} — where to look. The task catalogue uses the same field name and shape.
taskslist of stringSanctioned remediations, as task catalogue ids.
readiness_viastringPresent only when readiness is reported by a mechanism other than this alarm.

Events ​

Three topics carry every transition, published in the master realm. The payload is the Alarm shape above, so an agent reacting to an event can act on it without a follow-up call.

TopicPublished when
bondy.alarm.raisedAn alarm not currently raised is raised.
bondy.alarm.updatedA raised alarm's content changes. A producer restating an unchanged alarm publishes nothing.
bondy.alarm.clearedA raised alarm is cleared. The payload is the alarm as it was, so a subscriber learns how urgent the resolved condition had been.

The three feed from the same transitions as the history ring, so a subscriber's view and bondy.alarm.history cannot disagree.

Publication is demand-gated: with no subscriber, nothing is produced. Transitions of one alarm id are ordered relative to each other.

Tenant realms do not receive alarm events

Publishing into an affected tenant's own realm is not done. The details map is operator-oriented and is exactly where an internal error string would reach an audience it was not written for.