systemd: the journal verb reads a window, and failed is the seat's verb
mesh/delivery-group group feat/journal-window-on-the-seat delivering: 0 of 2 delivered
mesh/merge-gate pass: builds systemd → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery held for a person: its group feat/journal-window-on-the-seat's composed check did not pass for the heads that merged; a person decides that…
mesh/delivery-group group feat/journal-window-on-the-seat delivering: 0 of 2 delivered
mesh/merge-gate pass: builds systemd → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery held for a person: its group feat/journal-window-on-the-seat's composed check did not pass for the heads that merged; a person decides that…
An incident is read for the minutes it happened in (the operator's direction 2026-10-07): journal takes since, until, priority and a fixed-string match. Every value is one word of journalctl's argv, held to the forms journalctl reads, so nothing reaches a shell or is read as an option under sudo. What the unit printed of a secret is redacted, as docker_logs does, before the match is applied, so a match cannot find one. systemd_failed becomes the seat's failed, with an optional scope. Needs the controller's seat with these verbs (mesh-controller, same branch): an older controller refuses a claim serving a verb its seat does not promise.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// systemd's tools: the node-service-manager seat's eight verbs — the units on this machine in both scopes,
|
||||
// read and acted on by name — and the module's own reading of what has failed (novox/hq ADR 0177). The node
|
||||
// systemd's tools: the node-service-manager seat's nine verbs — the units on this machine in both scopes,
|
||||
// read and acted on by name, their journal, and what has failed (novox/hq ADR 0177). The node
|
||||
// tools runtime launches this bundle as a process of its own and serves what it serves (ADR 0188, ADR 0193);
|
||||
// it runs as the operator account, so acts on the system manager, and reads of its journal, escalate with
|
||||
// sudo -n, and the user scope is the account's own manager (client.go). The host applies units; this answers
|
||||
@@ -98,9 +98,16 @@ func tools(m *Manager) []stdio.Tool {
|
||||
act("enable", "Make one unit start at boot (or at the account's login, in user scope)."),
|
||||
act("disable", "Stop one unit starting at boot (or at login, in user scope)."),
|
||||
{Name: seat + ".journal",
|
||||
Description: "The last lines of one unit's journal (at most 2000) — a system service's included: the read is escalated, so it is the service's own lines and not only the operator account's.",
|
||||
Description: "The last lines of one unit's journal (at most 2000), in a time window and narrowed to a priority " +
|
||||
"and to lines holding a text when asked — a system service's included: the read is escalated, so it is the " +
|
||||
"service's own lines and not only the operator account's. A secret the unit printed is shown as " +
|
||||
"[redacted: <what it was>].",
|
||||
Input: map[string]any{"scope": scopeArg, "unit": unitArgS,
|
||||
"lines": map[string]any{"type": "number", "description": "how many lines from the end (default 100, at most 2000)"}},
|
||||
"lines": str("how many lines from the end of what matches (default 100, at most 2000)"),
|
||||
"since": str("the window's start: an RFC 3339 time (2026-10-07T09:30:00Z) or relative to now (-30min, -2h, yesterday) (optional)"),
|
||||
"until": str("the window's end, in the same forms (optional; now when absent)"),
|
||||
"match": str("only the lines holding this text, as written — a fixed string, not a pattern (optional)"),
|
||||
"priority": str("only entries this severe or more: 0-7 or emerg, alert, crit, err, warning, notice, info, debug (optional)")},
|
||||
Run: func(a map[string]any) (any, error) {
|
||||
scope, err := scopeOf(a)
|
||||
if err != nil {
|
||||
@@ -110,16 +117,27 @@ func tools(m *Manager) []stdio.Tool {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Bounded so the answer stays well below what the runtime carries back in one reply.
|
||||
n := 100
|
||||
if v, ok := a["lines"].(float64); ok && v >= 1 {
|
||||
n = min(int(v), 2000)
|
||||
q, err := journalQuery(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m.Journal(scope, unit, n)
|
||||
return m.Journal(scope, unit, q)
|
||||
}},
|
||||
{Name: "systemd_failed",
|
||||
// Was the module's own systemd_failed; on the seat since the operator's direction of 2026-10-07, so
|
||||
// whatever holds the role answers it and every machine is asked the same way.
|
||||
{Name: seat + ".failed",
|
||||
Description: "Every failed unit on this machine, in the system manager and in the operator account's; a manager that does not answer is reported with its error, not as nothing failed.",
|
||||
Run: func(map[string]any) (any, error) { return m.Failed(), nil }},
|
||||
Input: map[string]any{"scope": str(`"system" or "user": only that manager (both when absent)`)},
|
||||
Run: func(a map[string]any) (any, error) {
|
||||
if s, _ := a["scope"].(string); s == "" {
|
||||
return m.Failed(), nil
|
||||
}
|
||||
scope, err := scopeOf(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m.Failed(scope), nil
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user