What happened
Every launch unpacks the bundled libopentui.so into a temporary file and dlopens it, but never deletes the file. One file per launch, ~10 MB each, and the name is unique per launch so nothing is ever reused.
$ ls -la /tmp/.*.so
-rw-r--r-- 1 root root 10240680 Sep 27 08:59 /tmp/.1adb7be55fff07df-00000000.so
-rw-r--r-- 1 root root 10240680 Sep 27 09:00 /tmp/.1adb7bed3b9d5ee7-00000000.so
-rw-r--r-- 1 root root 10240680 Sep 27 09:05 /tmp/.1adb7abd59df37fb-00000000.so
Details that make this easy to confirm:
- size is always exactly
10240680 bytes, and the md5 is identical across launches (f29cb944…) — the same library, over and over
- the file is a valid
ELF64 AArch64 shared object with SONAME: libopentui.so
- the blob is embedded in the shipped binary: a 64-byte chunk from the middle of the extracted file is present in
freebuff
- exactly one file per launch, on every exit path —
--version, --help, Ctrl-C, through the npm launcher, and direct execution all leak one
It leaks regardless of how the process exits, so this is not an artefact of killing it.
On Android /tmp is a tmpfs, so this is RAM, not disk. Measured over one working session: 35 launches → 35 files → 342 MB resident. At a few dozen launches a day that is roughly 300 MB/day and ~9 GB/month that nothing ever reclaims.
I have not found a way to make Bun reuse the file. Bun does have a built-in extraction path — when a compiled binary dlopens a library imported with with { type: 'file' }, Bun unpacks it to /tmp/.bun-<hash>.so — but that one is deduplicated by name, so repeated runs reuse the same file. Stock Bun 1.2.21 and 1.4.2 both do this and do not grow. The per-launch unique name here is the CLI's own extraction, so the unlink is missing on that path.
Steps to reproduce
rm -f /tmp/.*.so
freebuff --version
ls -la /tmp/.*.so # one 10240680-byte file
freebuff --version
ls -la /tmp/.*.so # two
Any invocation works; --version is used here only because it exits on its own. Repeat N times and you get N files.
Safe to delete while running, which makes it easy to confirm nothing depends on the file after startup:
freebuff & sleep 8; rm -f /tmp/.*.so; sleep 5; kill -0 $! && echo "still running"
That keeps the process alive with no errors, which is what makes the fix below safe.
Workaround, if a fix is not immediately available — point TMPDIR somewhere disposable and sweep it:
mkdir -p /path/to/scratch
TMPDIR=/path/to/scratch freebuff
Where does this happen?
CLI (terminal client)
Operating system
Linux
Version
0.0.204 (linux-arm64)
Model
Pixel 8 Pro (Tensor G3), aarch64, Android 16, Termux with proot-distro 5.9.0. Also reproduces on the direct binary at ~/.config/manicode/freebuff, so it is not a launcher issue.
Logs or screenshots
$ for i in 1 2 3; do freebuff --version; done
0.0.204
0.0.204
0.0.204
$ ls /tmp/.*.so | wc -l
3
$ du -ch /tmp/.*.so | tail -1
3 total
30M <- ~10 MB per launch
readelf -d on the extracted file:
SONAME: Library soname: [libopentui.so]
NEEDED: libm.so.6, libpthread.so.0, libc.so.6, libdl.so.2
Build path baked into the library, for reference:
/Users/runner/work/_temp/.../zig-aarch64-macos-0.15.2/lib
Suggested fix
Delete the file as soon as dlopen returns — the library is mapped into the process at that point and the file is no longer read. Verified on this machine: removing it 8s into a running session leaves the process running with no errors.
import { dlopen } from 'bun:ffi'
import { unlinkSync } from 'node:fs'
const handle = dlopen(path, { symbols })
try {
unlinkSync(path) // mapped already; the file is no longer needed
} catch {
// couldn't remove it — not worth failing startup over
}
If the library is reached through Bun's own embedded-file path (/$bunfs/...), no extra temp file is needed at all — unlinking the file Bun already unpacked is the whole fix.
Unrelated to #1374, but same runtime: os.cpus()[0].model throws when /proc/stat and /proc/cpuinfo disagree, which on this machine is an upstream Bun issue — oven-sh/bun#44125
What happened
Every launch unpacks the bundled
libopentui.sointo a temporary file anddlopens it, but never deletes the file. One file per launch, ~10 MB each, and the name is unique per launch so nothing is ever reused.Details that make this easy to confirm:
10240680bytes, and the md5 is identical across launches (f29cb944…) — the same library, over and overELF64 AArch64shared object withSONAME: libopentui.sofreebuff--version,--help, Ctrl-C, through the npm launcher, and direct execution all leak oneIt leaks regardless of how the process exits, so this is not an artefact of killing it.
On Android
/tmpis a tmpfs, so this is RAM, not disk. Measured over one working session: 35 launches → 35 files → 342 MB resident. At a few dozen launches a day that is roughly 300 MB/day and ~9 GB/month that nothing ever reclaims.I have not found a way to make Bun reuse the file. Bun does have a built-in extraction path — when a compiled binary
dlopens a library imported withwith { type: 'file' }, Bun unpacks it to/tmp/.bun-<hash>.so— but that one is deduplicated by name, so repeated runs reuse the same file. Stock Bun 1.2.21 and 1.4.2 both do this and do not grow. The per-launch unique name here is the CLI's own extraction, so the unlink is missing on that path.Steps to reproduce
Any invocation works;
--versionis used here only because it exits on its own. Repeat N times and you get N files.Safe to delete while running, which makes it easy to confirm nothing depends on the file after startup:
That keeps the process alive with no errors, which is what makes the fix below safe.
Workaround, if a fix is not immediately available — point
TMPDIRsomewhere disposable and sweep it:Where does this happen?
CLI (terminal client)
Operating system
Linux
Version
0.0.204 (linux-arm64)
Model
Pixel 8 Pro (Tensor G3), aarch64, Android 16, Termux with proot-distro 5.9.0. Also reproduces on the direct binary at
~/.config/manicode/freebuff, so it is not a launcher issue.Logs or screenshots
readelf -don the extracted file:Build path baked into the library, for reference:
Suggested fix
Delete the file as soon as
dlopenreturns — the library is mapped into the process at that point and the file is no longer read. Verified on this machine: removing it 8s into a running session leaves the process running with no errors.If the library is reached through Bun's own embedded-file path (
/$bunfs/...), no extra temp file is needed at all — unlinking the file Bun already unpacked is the whole fix.Unrelated to #1374, but same runtime:
os.cpus()[0].modelthrows when/proc/statand/proc/cpuinfodisagree, which on this machine is an upstream Bun issue — oven-sh/bun#44125