Skip to main content

max / makenotwork

8.3 KB · 166 lines History Blame Raw
1 # magicmirror: which daemons to watch.
2 #
3 # Install at ~/.config/magicmirror/magicmirror.toml, or pass a path as the first
4 # argument, or set $OPS_VIEWER_CONFIG.
5 #
6 # Adding a service is an edit to this file and nothing else. magicmirror knows
7 # nothing about tiers, gates, apps or targets; it renders whatever the source
8 # emits. A new producer gets a UI by emitting the payload.
9 #
10 # Three tabs, and there are three however many sources are configured:
11 #
12 # 1 live every source at once, worst first, each source's nodes indented
13 # under it, with a detail pane for whatever the cursor is on.
14 # 2 logs what every source has reported lately, grouped by which one said
15 # it and newest first within each.
16 # 3 store the series a [[store]] below names. Empty unless one is declared.
17 #
18 # tab/shift-tab or 1-3 to switch, up/down to move, enter to run the selected
19 # node's action, q to quit.
20 #
21 # A source sets exactly one of `url` and `path`. `url` is a daemon to poll, with
22 # /status.json appended. `path` is a payload on disk, which is what a batch
23 # producer leaves behind: something that runs for hours and then stops has
24 # nothing to poll, and making it grow an HTTP listener would be paying a
25 # daemon's cost for a producer that is not one. Either way magicmirror keeps the
26 # last payload it read across a failed poll, so a producer that finished at 03:20
27 # still reads at 15:00.
28 #
29 # magicmirror is read-only until a source sets allow_actions = true. A source
30 # with it off still shows the actions its nodes declare; it just refuses to
31 # fire them. With it on, Enter on a node row in the live tab opens a picker of
32 # that node's actions;
33 # a plain action fires, a confirm action asks for 'y', and a danger action
34 # (sando's rollback-b) asks you to type its key. This keeps magicmirror that is
35 # merely pointed at a daemon from ever moving it by accident.
36
37 # Age past which a source's answer stops counting as current, for any source
38 # that does not set its own. A source that answers with something hours old is
39 # reported as degraded no matter how green its contents: the failure this
40 # whole surface exists to catch is a check that kept saying "ok" about a
41 # snapshot nobody had refreshed in forty days.
42 stale_after_secs = 60
43
44 # Which theme to render in. Unset means follow the terminal: magicmirror reads
45 # COLORFGBG and picks a light or dark theme to match, so it does not paint a
46 # light palette onto a night session. Set it to a theme id to pin one instead,
47 # e.g. "makenotwork", "carbonfox", "nord". Own themes go in
48 # ~/.config/magicmirror/themes/ and outrank the bundled set of the same id.
49 #
50 # theme = "system"
51
52 # Each URL below is the address that daemon's own deploy example binds, which is
53 # the file to check when a source reads as unreachable. A daemon bound to
54 # loopback is only visible to magicmirror on the same host.
55 #
56 # A tailnet bind is necessary but not sufficient for magicmirror on another machine:
57 # the tailnet ACL decides separately whether that machine may open the port. As
58 # of 2026-07-29 the ACL is one-directional between the user-owned hosts and the
59 # tagged ones, so magicmirror on astra cannot reach fw13's daemons at any port even
60 # though both sit on the tailnet. Check the ACL, not just `listen`, when a source
61 # reads unreachable from one host and fine from another.
62 [[source]]
63 name = "sando"
64 # Matches `listen` in sando/deploy/sando-daemon.toml.example: fw13's tailnet
65 # address, deliberately not 0.0.0.0, and reachable from fw13 itself.
66 url = "http://100.103.89.95:7766"
67 # Sando gates its reads, so a token is required. The token is NAMED here, never
68 # pasted: this file describes topology and has every reason to be readable,
69 # while sandod already takes the same value from its environment.
70 token_env = "SANDO_API_TOKEN"
71 poll_secs = 5
72 # Sando declares promote and rollback actions that move production. Leave this
73 # off to watch read-only; turn it on deliberately to drive deploys from here.
74 # allow_actions = true
75
76 [[source]]
77 name = "bento"
78 # Matches `listen` in bento/deploy/bento-daemon.toml.example. Loopback, so this
79 # source only works for magicmirror running on the same host as bentod. magicmirror
80 # elsewhere needs bentod moved to its tailnet address, which turns its auth on.
81 url = "http://127.0.0.1:8765"
82 # Bento leaves its reads open, so no token is needed. Set token_env anyway if
83 # that ever changes; an unset variable is treated as no token.
84 poll_secs = 10
85
86 # PoM polls its own targets on a slower cycle, so hold it to a slower staleness
87 # limit rather than flagging it degraded between its own checks. Each target PoM
88 # watches is a node; the rollup shows the worst of them. PoM gates /status.json
89 # behind the same bearer token as its /api/* reads, so name the token here — it
90 # is the value in pom's serve.api_token, supplied to magicmirror via the
91 # environment, never pasted into this file.
92 [[source]]
93 name = "pom"
94 # PoM listens on 9100, never 9000, and there is no host named "pom" to resolve.
95 # Point this at the instance you want: astra's tailnet address below, or
96 # 127.0.0.1:9100 for a pom running on this machine. See pom/deploy/pom-*.toml.
97 url = "http://100.106.221.39:9100"
98 token_env = "POM_API_TOKEN"
99 poll_secs = 30
100 stale_after_secs = 600
101
102 # witchbroom: the nightly repo x check sweep on astra. A batch producer, so this
103 # is a file rather than a URL — it writes its payload at the end of a run and
104 # stops. The path is witchbroom's own `status_path` default; change both or
105 # neither. Only useful for a magicmirror on astra, since the file is local.
106 #
107 # A file source takes no token_env and no allow_actions: there is nothing to
108 # authenticate to and no base to resolve an action's URL against. Both are
109 # refused at load rather than ignored.
110 #
111 # stale_after_secs is a day and a half, not sixty seconds. The sweep runs
112 # nightly, so anything shorter reports it degraded for twenty-three hours out of
113 # every twenty-four; a day and a half is "it missed a night", which is the thing
114 # actually worth seeing.
115 [[source]]
116 name = "witchbroom"
117 path = "~/.local/state/witchbroom/status.json"
118 poll_secs = 60
119 stale_after_secs = 129600
120
121 # ---------------------------------------------------------------------------
122 # Stores: the third tab.
123 # ---------------------------------------------------------------------------
124 #
125 # Optional, and normally absent. Everything above is the `ops-status` contract,
126 # which is what lets a new daemon arrive with a UI already written. A store is
127 # the one exception: a producer's own SQLite file, opened read-only, for exactly
128 # the series named below. The break is deliberate and stays narrow.
129 #
130 # Why the meaning is here rather than in the data. witchbroom's store is one
131 # `(series, labels, value, at)` table with an `f64`, on purpose: a new metric is
132 # a new string rather than a migration, and the accepted cost is that the schema
133 # cannot say what a number is or what unit it is in. So either every series gets
134 # rendered unlabelled, which is a table browser, or the operator names the ones
135 # worth watching and says what each is. The second was chosen.
136 #
137 # The consequence, and it is the point rather than a limitation: A SERIES WITH NO
138 # ENTRY BELOW IS NOT SHOWN. Silence over noise. If a number is missing from the
139 # tab, the fix is a [[store.series]] here, not a fallback in the code.
140 #
141 # A named series the store holds nothing for still gets a row, saying so. A soak
142 # target that has never reported is exactly the thing worth noticing, and
143 # omitting it would look identical to never having configured it. A store that
144 # is missing or unreadable shows an `unavailable` row above whatever it last
145 # said, so old numbers are never mistaken for current ones.
146 #
147 # [[store]]
148 # name = "witchbroom"
149 # # witchbroom's own observations.db default. Opened read-only, never created:
150 # # a typo here stays a visible error rather than becoming an empty database
151 # # that reads as a producer which has never recorded anything.
152 # path = "~/.local/state/witchbroom/observations.db"
153 # # Slower than a source poll by an order of magnitude, because a batch producer
154 # # writes once at the end of a run that took hours. Defaults to 60.
155 # poll_secs = 300
156 #
157 # [[store.series]]
158 # series = "soak.coverage_edges" # the store's own series name, verbatim
159 # label = "Coverage reached" # what goes on screen
160 # unit = "edges" # optional; some numbers count nothing in particular
161 #
162 # [[store.series]]
163 # series = "cache.size_bytes"
164 # label = "Compiler cache"
165 # unit = "bytes"
166