Files
mesh-catalog/modules/slack/README.md
T
jochen 75f25fca21 Add slack and jetbrains-toolbox: one start each, the apps kept as found
Slack comes from the AUR and Toolbox from JetBrains' self-updating tarball,
so neither is declared: the host installs official packages only, and a
pinned archive would fight Toolbox's own updates. Slack's start and the
operator's i3 window rules become one node-display-session contribution,
because Slack's own launch-on-login is a symlink in ~/.config/autostart.
Toolbox keeps its own autostart entry as its one start. The shared
desktop.go header now names all four bundles.
2026-10-05 15:05:55 +02:00

163 lines
12 KiB
Markdown

# slack
The Slack desktop app on the workstations, as a module (novox/hq ADR 0208). One Electron process is
both Slack's window and its tray icon. The module requires `x11-display`, so it is assigned only where
a display server is held on the same machine.
## Owns
| what | where |
|---|---|
| Slack's one start at login, and its window rules | a `config` contribution to `node-display-session` (ADR 0212), which the seat's holder (`i3`) places in its configuration |
Nothing else. It holds no seat and writes no file.
- **No package.** Both workstations run `slack-desktop` 4.51.191-1, installed explicitly, from the
AUR: `pacman` counts it as foreign (no sync repository has it, and its packager is "Unknown
Packager"). The host installs packages from the official repositories only, so the module does not
declare it, and **installing and upgrading Slack stays the operator's** (an AUR helper, by hand).
`slack_status` says whether it is outside the official repositories, and `slack_check` says when it
is missing.
- **Why not a pinned archive (ADR 0205):** Slack is a binary release of about 330 MB under Slack's own
licence. ADR 0205 vendors free software the distribution lacks; redistributing Slack's binary from
the mesh's store is not the module's to do. ADR 0205 does not apply, and the package stays as found.
- **Slack's own files are found** (ADR 0182): `~/.config/Slack/` holds the sessions (cookies, local
storage, the encryption key in `Local State`), the settings (`storage/root-state.json`), the caches
and the logs. The module never declares or writes any of it. The tools read only the settings'
switches and the logs, and never the session files.
## How it starts: the module's line in i3's configuration, and nothing else
One process has one starter (the rule `picom` states for the desktop modules). Slack's starter is
**this module's contribution**: `exec --no-startup-id /usr/bin/slack --gtk-version=3 -s`, which `i3`
runs once when the session starts. Those are the arguments of the package's own desktop entry. `-s`
starts Slack hidden, to the tray.
**Why not an XDG autostart entry**, as `nextcloud-client` and `blueman` start:
- Slack's own setting *Launch app on login* is the existence of `~/.config/autostart/slack.desktop`.
Slack reads the file's presence back into the setting at every start. Ticking the setting makes the
file **a symbolic link** to `/usr/share/applications/slack.desktop`, and unticking it deletes the
file. This is in Slack 4.51's own code, not a guess.
- If the module owned that path as a file, two writers would hold it: the mesh writing it, and Slack
deleting it whenever the setting is unticked.
- If the entry were left to Slack, the start would be a link that Slack makes in the operator's home,
which this mesh's rules do not want there.
- A contribution is a start the mesh owns, beside the window rules it belongs with, in the window
manager that runs it. Under sway, `sway` holds the same seat with the same grammar.
**Excluded, and named by `slack_check` as a second start:**
- any `~/.config/autostart/slack.desktop`: the predecessor's file, or Slack's link if *Launch app on
login* is ticked again (untick it; Slack removes the link);
- an `exec … slack` of the operator's in `~/.config/i3/config.d/`;
- `/etc/xdg/autostart/slack.desktop`, which the package does not ship.
**Not a start:** systemd's XDG autostart generator makes a unit `app-slack@autostart.service` from the
entry while the entry exists. Only a desktop that starts `xdg-desktop-autostart.target` runs it, and
i3 does not. The unit goes when the entry goes.
**Slack's cgroup does not say who started it.** Slack moves its main process into a scope of its own
(`app-slack-<pid>.scope`) whoever starts it. `slack_status` therefore names the starts from the
configuration, not from the cgroup.
## The window rules
The operator's `~/.config/i3/config.d/50-slack.conf` (2026-08) became this contribution as it was:
- Slack's chat window goes to workspace 3 (`$ws3`, i3's variable, in scope where the contributions are
placed);
- it is tiled, not floating;
- it has a 2-pixel border.
The criteria are those of the operator's file. They were verified live then, and the window tree of
both workstations on 2026-10-05 still agrees:
- Slack owns four X windows. The only one i3 manages has the class `slack` in lower case.
- The three of class `Slack` (the packaged entry's `StartupWMClass`) are unmanaged helpers and the tray
icon. A rule on `class="Slack"` therefore matches nothing.
- `window_role="browser-window"` keeps a call or screen-share window out of the rules.
**Once the module is assigned, the operator deletes `50-slack.conf`** (below). i3 accepts the same
`for_window` twice, so nothing breaks while both are there. But the rules belong in one place, and
`slack_check` names the file until it is gone.
## Tools
They are served by the node's runtime as the operator account (ADR 0175), and are read-only except
`restart`. **No answer carries a token, a cookie, a message, or the name of a person, channel or
workspace.** Accounts and workspaces are counted, never named.
| tool | does |
|---|---|
| `slack_status` (r) | <ul><li>whether Slack runs: the main process's pid, since and scope, and how many helper processes it has</li><li>the installed version, and whether it is outside the official repositories</li><li>where its output goes (fd 1 and 2): the journal, `/dev/null`, a pipe someone reads, or a pipe nobody reads</li><li>whether its icon sits in a tray, and whose (from the X window tree)</li><li>who owns the session bus's notification name (dunst)</li><li>the switches: launch on login, hide on start, run from the tray, the notification method, hardware acceleration; and the Electron version</li><li>how many accounts and workspaces it is signed in to since its last start, counted from the reports Slack logs</li><li>what starts it at login</li></ul> |
| `slack_log` (r) | the last `lines` (default 100, at most 2000) of Slack's main-process log (`source: browser`, across its rotations) or of the web app's console (`source: webapp`). `problems: true` keeps `error` and `warn` entries. Tokens (`xox…`), the `d` cookie, anything shaped like a credential, notification and message text, and the names of people, channels and workspaces become `<hidden>`. Answers are capped at 256 KiB |
| `slack_restart` (a) | ends Slack (SIGTERM, forced after 8 s) and starts `/usr/bin/slack --gtk-version=3 -s` in the operator's session. The start is a transient user unit `mesh-slack`, so it outlives the tools runtime, and its output goes to the journal. Answers the pids. Refused plainly when nobody is logged in to the desktop |
| `slack_check` (r) | <ul><li>Slack is installed (if not: from the AUR, by the operator)</li><li>exactly one start: the module's line is in i3's configuration, with no autostart entry and no other exec</li><li>the window rules are placed, and no file of the operator's repeats them</li><li>one Slack runs in the desktop session</li><li>**its output is read**: a pipe nobody reads is the session's dead output (below)</li><li>its icon is in a tray (closing the window otherwise leaves it unreachable)</li><li>a notifier owns `org.freedesktop.Notifications`</li></ul>Each finding says what to do |
**The output check (EPIPE).** A session started before the `i3` module's login entry sent the
session's output to the journal (`systemd-cat -t x-session`) gives every program it starts a stdout
pipe whose reader is gone. An Electron app's write there fails with EPIPE, and an unhandled one is the
"write EPIPE" dialog. Slack's own logger works around it: it silences its console output on EPIPE. Any
other write still fails. So `slack_check` finds the pipe by its reader: it looks for a process of the
account holding the pipe open for reading (`/proc/<pid>/fdinfo`). It names it when there is none.
`slack_restart` cures it, and every later login does too.
**Where the tools read:**
- the processes, and the fd links and fdinfo of Slack's main process, in `/proc` (the account's own
only);
- the settings' switches, from `~/.config/Slack/storage/root-state.json`, and the Electron version from
`local-settings.json`;
- the logs in `~/.config/Slack/logs/default/`. The accounts are counted from the
`STORE_USER_WORKSPACES` entries since the last `INITIALIZE`;
- the window tree from `xwininfo -root -tree`, with the session's `DISPLAY`. The session is found from
the window manager's own environment, as the other desktop modules find it;
- the notifier from `busctl --user list`.
Every command has a timeout and capped output. Everything runs through an injected runner, a fake root
and a fake link reader in the tests.
## What changes when it is assigned
| | g14 | shanks |
|---|---|---|
| package | none: `slack-desktop` 4.51.191-1, AUR, installed explicitly | the same |
| start | i3's configuration gains the module's start. **Until the predecessor's `~/.config/autostart/slack.desktop` is deleted, the next login starts Slack twice.** The second start hands over to the first and exits, because Slack is single-instance. Running now: started by dex from that entry at the login of 2026-10-04 16:26 | the same entry, the same until deleted. Running now: since 2026-10-04 17:05, **with its stdout on a pipe nobody reads** (that session predates the `i3` module's login entry), so `slack_check` names it until `slack_restart` or the next login |
| window rules | i3's configuration gains them. The operator's `50-slack.conf` repeats them until deleted | the same |
| settings | *Launch app on login* reads on (the entry exists), start hidden, run from the tray, default notifications (to dunst); 1 account, 1 workspace | the same switches |
| tray, notifications | icon in i3bar's tray; dunst owns the notification name | the same |
## Migration (ADR 0182), on each workstation, once the module is assigned and pushed
1. **Delete `~/.config/autostart/slack.desktop`.** It is the predecessor's file (its comment names the
retired desktop module), the second start. At its next start Slack reads *Launch app on login* as
off. Leave the setting off: ticking it makes the entry again, and `slack_check` names it.
2. **Delete `~/.config/i3/config.d/50-slack.conf`.** It is the operator's own file, and its rules are
now the module's.
3. **shanks only:** run `slack_restart`, or log out and in, so that Slack's output is read.
`slack_check` then answers `ok`. Deleting both files before the module is assigned would leave Slack
without a start and without its rules until it is.
## Leaves as found
- `~/.config/Slack/`: the sessions, the settings, the caches, the logs, more than a dozen
`.org.chromium.Chromium.*` leftovers and `StaleCookies-*` files from past upgrades. They are Slack's
to keep and the operator's to delete.
- The package and its desktop entry, `/usr/share/applications/slack.desktop`.
- The `x-scheme-handler/slack` default, which is the `xdg` module's.
## Relies on
- **`i3` (the holder of `node-display-session`), which places the contribution.** The contribution is a
dependency on that seat (ADR 0210 §3). Assigning `slack` where no module holds it is refused.
- **`dunst` (the holder of `node-notifier`)** for Slack's notifications, which Electron sends to
`org.freedesktop.Notifications`. Nothing in the mesh declares this dependency, because the module
makes no contribution to that seat. `slack_check` reports a missing notifier instead.
- **A tray in the bar** (i3bar's `tray_output`). Without one, Slack runs with no icon, and closing its
window leaves it unreachable. `slack_check` reports it.
- **`xwininfo` (`xorg-xwininfo`)** for the tray question. No module declares it, and this one does not
install it for a status line. Without it, `slack_status` answers the tray as unknown.
- A display server on the same machine (`x11-display`, ADR 0208 §3).