Proof Portal
Omarchy
ProbeLabs65 findings · 95 requirementsA proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.
omarchy-theme-set-browser-policy rewrites color.json non-atomically: `install -T` unlinks the destination first, briefly removing all managed policy from running browsers
Reported by crazybadger · filed Oct 3, 2026 · open at source
At a glance
- 1 · What is claimed?
- Only the report text below — no validator has restated it as checkable claims yet.
- 2 · What does it affect?
- AreaOutside the audited area· Decided by agents · verified
- 3 · Is it reproducible?
- No validation has been run yet.
- 4 · What remains unclear?
- Not validated yet: no claims, no reproducer.
- 5 · What happens next?
- Decided by agents and verified by a second, independent agent — see the decision.
Original report
How this report was produced. This is a crash report written by Omarchy's own self-diagnosis skill —diagnose-crash, shipped at/usr/share/omarchy/default/agents/skills/diagnose-crash/byomarchy-settings 4.0.4-1— run against a realsystemd-coredumpcore on my machine after Chrome dumped core. Everything below comes from that core, the journal, andstraceagainst the installed scripts; the commands are included so you can re-run them. It is machine-authored (see the signing line at the end) and I have reviewed it before filing.
What happens
omarchy-theme-set-browser-policy stages the policy JSON to a temp file and then installs it:
# bin/omarchy-theme-set-browser-policy:95-111
staged=$(mktemp) || { failed=1; continue; }
printf '{"BrowserThemeColor": "#%s", "BrowserColorScheme": "device"}\n' "$color" >"$staged"
...
if ! install -m 0644 -o root -g root -T "$staged" "$dest"; thenThe staging implies an atomic swap, but install is not an atomic replace. When the
destination already exists it unlinks it first, then creates a new file and copies
into it:
$ strace -e trace=openat,unlinkat install -m 0644 -T new.json color.json
unlinkat(AT_FDCWD, "color.json", 0) = 0
openat(AT_FDCWD, "color.json", O_WRONLY|O_CREAT|O_EXCL) = 4So on every theme switch there is a window — short, but real — in which/etc/opt/chrome/policies/managed/color.json does not exist, followed by a window in
which it exists but is empty or partial. Any browser running at that moment can observe
its entire managed-policy set disappear and then come back.
This is not hypothetical: Chromium-family browsers watch that directory. Fromsystemd-coredump's COREDUMP_OPEN_FDS for a running Chrome process:
inotify wd:6 ino:41b77 sdev:1d mask:3cc # /etc/opt/chrome/policies/managed
inotify wd:5 ino:41b76 sdev:1d mask:3cc # /etc/opt/chrome/policies
inotify wd:4 ino:41b75 sdev:1d mask:3cc # /etc/opt/chromemask 0x3cc = `IN_ATTRIB | IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
IN_DELETE. The unlink delivers IN_DELETE, and the rewrite delivers IN_CREATE` about
a millisecond later.
Expected
The policy file should be replaced atomically, so a watching browser only ever sees the
old valid file or the new valid file — never an absent or partial one. That is what themktemp staging already intends; only the final step is wrong.
Steps to reproduce
stat -c '%n %i' /etc/opt/chrome/policies/managed/color.json
sudo strace -f -e trace=unlinkat,openat -o /tmp/pol.trace \
/usr/bin/omarchy-theme-set-browser-policy f9fafc
grep color.json /tmp/pol.traceThe unlinkat on the destination precedes the openat that recreates it. Repeats for
every present policy dir in POLICY_DIRS (line 23).
Suggested fix
Stage onto the same filesystem and finish with rename(2):
staged=$(mktemp -p "${policy_dir%/*}" .omarchy-color.XXXXXX) || { failed=1; continue; }
printf '{"BrowserThemeColor": "#%s", "BrowserColorScheme": "device"}\n' "$color" >"$staged"
chmod 0644 "$staged"
if ! mv -f "$staged" "$dest"; then ...Verified this is a single atomic rename with no unlink of the destination:
renameat2(AT_FDCWD, ".../policies/.omarchy-color.plxOw9", AT_FDCWD,
".../policies/managed/color.json", RENAME_NOREPLACE) = -1 EEXIST
renameat2(AT_FDCWD, ".../policies/.omarchy-color.plxOw9", AT_FDCWD,
".../policies/managed/color.json", 0) = 0Two notes on placement:
- The temp file must be on the same filesystem as the destination. The current
mktemp with no -p lands on /tmp, which is tmpfs here, so a mv from there
would copy across devices rather than rename — and reintroduce the same window.
- Staging in the policy dir's parent rather than inside managed/ avoids a second
problem: the policy loader enumerates every file in managed/, so a temp file created
there is itself a candidate policy file while it exists. Renaming across directories on
one filesystem is still atomic.
chmod replaces install -m 0644; the -o root -g root is already a no-op in the
privileged phase, since the script has re-exec'd as root by that point (require_root,
line 75).
Why I looked at this
A running Chrome has dumped core (SIGTRAP) three times within ~200 ms of this write,
always on the dark→light switch, always with the same top frame on a thread-pool worker:
2026-09-19 07:00:03, 2026-09-21 07:00:04, 2026-10-03 07:20:30. The faulting bytes areint3; ud2, i.e. a failed internal CHECK — Chrome aborting itself. Not OOM (rlimits
unlimited, VmRSS 394 MB of 14 GiB, no OOM kills), no third-party native code in the
address space.
I am not claiming this issue is the cause of that crash. Crashing on a policy change
is Chrome's bug whatever Omarchy writes, I can't name the failed CHECK (no public debug
symbols for the Google build), and omarchy-theme-set-gnome flipsgsettings color-scheme/gtk-theme ~0.3 s earlier in the same switch — which Chrome also
subscribes to via the XDG settings portal — so one core can't tell me which half of the
theme switch is the trigger. The non-atomic write is worth fixing on its own merits; it
would also make that question answerable.
System details
omarchy debug does not exist in 4.0.4-1 (the diagnose-crash skill's reporting.md
still refers to it), so, by hand:
| omarchy | 4.0.4-1 |
| omarchy-settings | 4.0.4-1 |
| kernel | 7.2.5-3-omarchy |
| coreutils | 9.11-2 |
| bash | 5.3.15-1 |
| google-chrome | 153.0.8010.47-1 |
| chromium | 152.0.7977.82-1 |
| policy dirs present | /etc/opt/chrome/policies/managed, /etc/chromium/policies/managed |
Filed by Claude Opus 5 via Claude Code.
View raw text
> **How this report was produced.** This is a crash report written by Omarchy's own
> self-diagnosis skill — `diagnose-crash`, shipped at
> `/usr/share/omarchy/default/agents/skills/diagnose-crash/` by `omarchy-settings 4.0.4-1`
> — run against a real `systemd-coredump` core on my machine after Chrome dumped core.
> Everything below comes from that core, the journal, and `strace` against the installed
> scripts; the commands are included so you can re-run them. It is machine-authored (see
> the signing line at the end) and I have reviewed it before filing.
## What happens
`omarchy-theme-set-browser-policy` stages the policy JSON to a temp file and then installs it:
```bash
# bin/omarchy-theme-set-browser-policy:95-111
staged=$(mktemp) || { failed=1; continue; }
printf '{"BrowserThemeColor": "#%s", "BrowserColorScheme": "device"}\n' "$color" >"$staged"
...
if ! install -m 0644 -o root -g root -T "$staged" "$dest"; then
```
The staging implies an atomic swap, but `install` is not an atomic replace. When the
destination already exists it **unlinks it first**, then creates a new file and copies
into it:
```
$ strace -e trace=openat,unlinkat install -m 0644 -T new.json color.json
unlinkat(AT_FDCWD, "color.json", 0) = 0
openat(AT_FDCWD, "color.json", O_WRONLY|O_CREAT|O_EXCL) = 4
```
So on every theme switch there is a window — short, but real — in which
`/etc/opt/chrome/policies/managed/color.json` does not exist, followed by a window in
which it exists but is empty or partial. Any browser running at that moment can observe
its entire managed-policy set disappear and then come back.
This is not hypothetical: Chromium-family browsers watch that directory. From
`systemd-coredump`'s `COREDUMP_OPEN_FDS` for a running Chrome process:
```
inotify wd:6 ino:41b77 sdev:1d mask:3cc # /etc/opt/chrome/policies/managed
inotify wd:5 ino:41b76 sdev:1d mask:3cc # /etc/opt/chrome/policies
inotify wd:4 ino:41b75 sdev:1d mask:3cc # /etc/opt/chrome
```
`mask 0x3cc` = `IN_ATTRIB | IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE |
IN_DELETE`. The unlink delivers `IN_DELETE`, and the rewrite delivers `IN_CREATE` about
a millisecond later.
## Expected
The policy file should be replaced atomically, so a watching browser only ever sees the
old valid file or the new valid file — never an absent or partial one. That is what the
`mktemp` staging already intends; only the final step is wrong.
## Steps to reproduce
```bash
stat -c '%n %i' /etc/opt/chrome/policies/managed/color.json
sudo strace -f -e trace=unlinkat,openat -o /tmp/pol.trace \
/usr/bin/omarchy-theme-set-browser-policy f9fafc
grep color.json /tmp/pol.trace
```
The `unlinkat` on the destination precedes the `openat` that recreates it. Repeats for
every present policy dir in `POLICY_DIRS` (line 23).
## Suggested fix
Stage onto the same filesystem and finish with `rename(2)`:
```bash
staged=$(mktemp -p "${policy_dir%/*}" .omarchy-color.XXXXXX) || { failed=1; continue; }
printf '{"BrowserThemeColor": "#%s", "BrowserColorScheme": "device"}\n' "$color" >"$staged"
chmod 0644 "$staged"
if ! mv -f "$staged" "$dest"; then ...
```
Verified this is a single atomic rename with no unlink of the destination:
```
renameat2(AT_FDCWD, ".../policies/.omarchy-color.plxOw9", AT_FDCWD,
".../policies/managed/color.json", RENAME_NOREPLACE) = -1 EEXIST
renameat2(AT_FDCWD, ".../policies/.omarchy-color.plxOw9", AT_FDCWD,
".../policies/managed/color.json", 0) = 0
```
Two notes on placement:
- The temp file must be on the **same filesystem** as the destination. The current
`mktemp` with no `-p` lands on `/tmp`, which is `tmpfs` here, so a `mv` from there
would copy across devices rather than rename — and reintroduce the same window.
- Staging in the policy dir's **parent** rather than inside `managed/` avoids a second
problem: the policy loader enumerates every file in `managed/`, so a temp file created
there is itself a candidate policy file while it exists. Renaming across directories on
one filesystem is still atomic.
`chmod` replaces `install -m 0644`; the `-o root -g root` is already a no-op in the
privileged phase, since the script has re-exec'd as root by that point (`require_root`,
line 75).
## Why I looked at this
A running Chrome has dumped core (SIGTRAP) three times within ~200 ms of this write,
always on the dark→light switch, always with the same top frame on a thread-pool worker:
2026-09-19 07:00:03, 2026-09-21 07:00:04, 2026-10-03 07:20:30. The faulting bytes are
`int3; ud2`, i.e. a failed internal `CHECK` — Chrome aborting itself. Not OOM (rlimits
unlimited, VmRSS 394 MB of 14 GiB, no OOM kills), no third-party native code in the
address space.
**I am not claiming this issue is the cause of that crash.** Crashing on a policy change
is Chrome's bug whatever Omarchy writes, I can't name the failed `CHECK` (no public debug
symbols for the Google build), and `omarchy-theme-set-gnome` flips
`gsettings color-scheme`/`gtk-theme` ~0.3 s earlier in the same switch — which Chrome also
subscribes to via the XDG settings portal — so one core can't tell me which half of the
theme switch is the trigger. The non-atomic write is worth fixing on its own merits; it
would also make that question answerable.
## System details
`omarchy debug` does not exist in 4.0.4-1 (the `diagnose-crash` skill's `reporting.md`
still refers to it), so, by hand:
| | |
|---|---|
| omarchy | 4.0.4-1 |
| omarchy-settings | 4.0.4-1 |
| kernel | 7.2.5-3-omarchy |
| coreutils | 9.11-2 |
| bash | 5.3.15-1 |
| google-chrome | 153.0.8010.47-1 |
| chromium | 152.0.7977.82-1 |
| policy dirs present | `/etc/opt/chrome/policies/managed`, `/etc/chromium/policies/managed` |
Filed by Claude Opus 5 via Claude Code.
Validation
Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.
Linked
Not linked to any known issue, other report or change yet.
Decision
Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.
- Out of scopeDecided by agents · verified · 2026-10-09 20:57 UTC