aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs/2026-10-21-sb-builder-settings-design.md
blob: e40a1d3c6850de4b00512ca30b15d076be63e2bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# sb-builder settings pages

sb-builder (a PyQt6 GUI in its own repo, `../sb-builder`) has two TOML
configs and no UI to edit either: the GUI's own
`$XDG_CONFIG_HOME/sb-builder/config.toml` on this desktop, and the helper's
`/etc/sb-builder/config.toml` on the build VM. Both get settings pages here,
in the `desktop/` settings window, under a group "SB Builder", the way
qtmaildir's four pages sit under "Mail".

## Pages

`desktop/modules/sbbuilder/`:

- `Sbb.qml`, the singleton both pages share: two drafts (desktop, VM), each
  with the text last read, `dirty`, `invalid`, `error`, `save()`,
  `discard()`, `refresh()`, as `Qtm.qml` does for one.
- `Toml.js`, a line editor for the TOML these two files use: top-level and
  `[section]` keys whose values are strings, integers, booleans and arrays of
  strings, one key per line. It reads a value, sets one (in place, or
  appended to its section, the section appended when missing), unsets one
  (commented out with `#` where it stands, reactivated when set again), and
  lists a section's keys. Comments and order survive. Values it writes are
  TOML basic strings with `\` and `"` escaped. `test_toml.js` is its check,
  run with `node`.
- **Desktop** page (`SbbDesktopModule.qml`), over the GUI config through a
  watched, atomic `FileView`: remote target and ssh options, the two refresh
  intervals, notifications on/off and one switch per event, the VM's libvirt
  URI, domain and autostart, the initial publish state, the nvchecker
  keyfile, the repository path.
- **Build VM** page (`SbbVmModule.qml`): the helper's thirteen keys and the
  `[manifests]` table as rows, removable, with an add row (package, URL).

Every field follows the settings window's rules (`desktop/README.md`): it
edits the draft as typed, Save in the page header writes, Discard drops,
a save over a file changed since the draft began is refused. An emptied
field unsets its key and the placeholder names the default. Numbers are
range-checked (refresh intervals: whole seconds, at least 1), a refused one
holds Save back. Lists (`ssh_options`, `overlays`) are one field, entries
separated by spaces; an entry holding a space is a `ponytail:` ceiling,
editable only by hand.

The desktop defaults are mirrored from sb-builder's `config.py` DEFAULTS
(`ponytail:` a key added there needs adding here). The VM defaults come
from the helper itself.

## The VM side

The helper's config is root's file on another machine, and the helper is a
trust boundary: it runs a fixed command table and validates what reaches it.
So the page never writes the file by hand over ssh. sb-builder's helper gains
two commands:

- `sbb-remote config-get`: JSON `{"text": <file text, "" when missing>,
  "defaults": <DEFAULTS>, "path": <config path>}`.
- `sbb-remote config-set`: the new file text on stdin. Parsed with
  `tomllib`; refused, with the reason as its error, on a parse error, an
  unknown key or a value of the wrong type (the checks `load_config` already
  makes, `[manifests]` values strings). Accepted, it copies the old file to
  `.bak` and replaces it atomically (temp file, rename). stdin input has a
  precedent in `gpg-unlock`.

The page runs `ssh <ssh_options> <target> sbb-remote config-get` with the
target and options from the desktop page's saved config (not its draft), on
every open of the window and from a reload button. Save pipes the draft to
`config-set`. The helper's error, or ssh's, lands in the SaveBar. With the
VM down the page says so and its fields are read-only. The helper reads its
config on every call, so a saved change applies to the next one.

The VM draft's "changed on disk" check compares the text config-get last
returned against a fresh config-get just before config-set.

## sb-builder's side

Owned by that repo, built there:

- the two helper commands above, with `test_remote.py` cases: accepted text
  written and `.bak` made, bad TOML, unknown key, wrong type refused with the
  file untouched; helper VERSION bumped in `remote/sbb-remote` and
  `sb_builder/__init__.py`; deploying with `./install.sh --remote` is asked
  first.
- the GUI watches its config file (`QFileSystemWatcher`, re-added after each
  change since an atomic write replaces the inode) and calls its existing
  `reload_config`, so a save from the settings page applies live. File,
  Reload config stays.
- a note in its README that both configs have settings pages in Keystone,
  and that the files remain hand-editable.

## Out of scope

The phantom deps file, hint files and anything else the helper reads besides
its config. Editing configs of a VM other than the one `remote.target`
names.