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.
163 lines
12 KiB
Markdown
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).
|