Proof Portal

Project overview

Omarchy

ProbeLabs65 findings · 95 requirements

A 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.

Inbox
ReportR-2206GitHub issuePublicDecided· Out of scope

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

Decided: Out of scope — decided by agents · verified 3 days ago.

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

omarchy-theme-set-browser-policy rewrites color.json non-atomically: `install -T` unlinks the destination first, briefly removing all managed policy from running browsersrevision 1 · captured 2026-10-03 09:47 UTC via poll
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:

# 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

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):

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:

omarchy4.0.4-1
omarchy-settings4.0.4-1
kernel7.2.5-3-omarchy
coreutils9.11-2
bash5.3.15-1
google-chrome153.0.8010.47-1
chromium152.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