An unofficial desktop client for 4chan: boards and threads in a modern three-pane layout, a thread watcher, filters, and Clip, which fits a video to the board you are posting to.
CloverEye is not affiliated with, endorsed by or connected to 4chan. The site’s rules apply to everything read and posted through it.
Licence: AGPL-3.0. Scope and phases: docs/SCOPE.md.
| Part | State |
|---|---|
Rust fetch layer (crates/fetch) |
Built and tested: one queue, ≥ 1 s between requests, ≥ 10 s per thread, If-Modified-Since/304, back-off on 429/5xx/other errors that waits out Retry-After in full |
ffmpeg check (crates/ffcheck) |
Built and tested: read-only -version/-encoders/-filters |
First-time setup (ui/) |
Runs in a browser with a mock bridge; 8 steps per SCOPE §3.1b |
Design tokens (ui/css/tokens.css) |
Dark default, light, follows the system; density; text size |
Desktop shell (src-tauri/) |
Release-built and packaged as RPMs for Fedora 44 and 45; launched once on Fedora 45 (setup renders in WebKitGTK), not yet exercised further (see below) |
Live API probe (crates/fetch/examples/probe.rs) |
Run once on 4 November 2026: no 429s; results in docs/C0-PROBE.md |
Not done yet (C0 gates in SCOPE §7): the week of normal browsing with zero 429s, WebM playback in WebKitGTK and WKWebView, and the hand-posted codec/MP4/dimension checks.
crates/fetch clovereye-fetch: queue, limits, cache, back-off, API models
crates/ffcheck clovereye-ffcheck: what the local ffmpeg can do for Clip
ui/ the app's UI: plain HTML/CSS/JS, no build step, no CDN
js/setup-model.js setup rules, DOM-free (tested with node --test)
js/bridge.js Tauri invoke inside the app, a mock in a browser
src-tauri/ Tauri 2 shell (excluded from the root Cargo workspace)
tests/ node tests for ui/
The Rust crates and the UI need only Rust and Node. The desktop shell also needs the WebKitGTK and GTK 3 development files. Install them yourself; the project never runs a package manager:
sudo dnf install webkit2gtk4.1-devel gtk3-devel libsoup3-devel librsvg2-devel
Package names checked against Fedora 44 and Fedora 45 with
dnf repoquery --whatprovides 'pkgconfig(webkit2gtk-4.1)' (and the same for
gtk+-3.0, libsoup-3.0, librsvg-2.0). javascriptcoregtk4.1-devel
comes in as a dependency of webkit2gtk4.1-devel. TLS uses rustls, so
openssl-devel is not needed.
Toolchain used: Rust 1.99 (Fedora’s), Node 22, and the Tauri CLI only for
bundling (cargo install tauri-cli --version '^2'), which C0 does not need.
CARGO_BUILD_JOBS=4 cargo test # Rust: fetch limits, models, ffmpeg parsing
node --test tests/*.test.mjs # UI: setup model, bridge, comment parser, static checks
All automated tests use recorded fixtures and a fake clock; none touches the
network. The fixtures and how they were recorded are listed in
crates/fetch/tests/fixtures/SOURCE.txt (two GETs, three seconds apart).
ES modules need a server (not file://):
python3 -m http.server 7397 --bind 127.0.0.1 --directory ui
# open http://127.0.0.1:7397/
In a browser the bridge is a mock: settings live in localStorage, the board
list comes from the recorded ui/data/boards.sample.json, and the ffmpeg
check returns a canned result matching the Fedora 45 machine CloverEye is
built on. Query parameters exercise the other states:
| Parameter | Effect |
|---|---|
?mock-boards=fail / fail-once |
Board list fails (always / first try only): Retry and Skip |
?mock-ffmpeg=missing / partial / no-vp9 |
ffmpeg absent, optional extras missing, or VP9 missing |
?mock-delay=0 |
No artificial delay |
#setup |
Run setup again after it was finished |
To start over, clear the site data for 127.0.0.1:7397.
With the server above running, two scripts drive the UI. Playwright is not a
dependency of this repo; point PLAYWRIGHT at an installed copy’s
index.mjs.
PLAYWRIGHT=/path/to/playwright/index.mjs node tests/e2e/setup-flow.mjs chromium # or firefox
PLAYWRIGHT=/path/to/playwright/index.mjs SHOTS_DIR=shots node tests/e2e/layout.mjs chromium
setup-flow.mjs walks all eight steps with the mouse and again with the
keyboard only (Enter, Esc, arrows, Tab, Space), plus the board-list failure
and missing-ffmpeg states. layout.mjs screenshots every step, the dialogs
and the shell at widths 360, 800, 1280 and 1920 in dark and light, and
reports horizontal scroll, off-screen or clipped elements, overlapping
controls and console errors.
cd src-tauri
CARGO_BUILD_JOBS=4 cargo run
Notes for the first test builds:
ipc: connection under the page’s own CSP (ui/index.html
carries a meta CSP as well as tauri.conf.json; if the IPC is blocked,
drop the meta tag), and that the rules link opens in the system browser.capabilities/default.json allows only the four app commands
(get_settings, set_settings, check_ffmpeg, fetch_boards), the
title-bar window controls, and opening https://www.4chan.org/rules.Test builds are attached to the GitHub releases:
sudo dnf install ./clovereye-0.1.0-1.fc45.x86_64.rpm # Fedora 45 (or .fc44)
chmod +x CloverEye-0.1.0-x86_64.AppImage && ./CloverEye-0.1.0-x86_64.AppImage # other x86-64 Linux
The AppImage is built on Ubuntu 22.04 and needs glibc 2.35 or newer. It carries its own WebKitGTK and GStreamer (base and good plugins, which include the WebM demuxer and VP8/VP9 decoders); WebM playback in it is not tested yet.
To build them yourself (results go to ~/.cache/clovereye/packaging, never
into the repository):
packaging/build-rpm.sh # the Fedora release you are on
packaging/build-in-container.sh 44 # another release, in a podman container
packaging/build-appimage.sh # the AppImage, in an Ubuntu 22.04 container
build-rpm.sh makes the same release build as cargo tauri build (the UI
embedded, no dev server) and packages it with packaging/clovereye.spec. The
RPM recommends /usr/bin/ffmpeg, so Fedora’s ffmpeg-free and RPM Fusion’s
ffmpeg both satisfy it.
cargo run -p clovereye-fetch --example probe -- --out results/probe.md
At most 8 requests (25 allowed), one queue, ≥ 1 s apart, honest
User-Agent: CloverEye/0.1.0: boards, /g/ catalog twice (the second
conditional), threads.json, one live thread twice ≥ 10 s apart, and
archive.json. --thumb adds one thumbnail request to see whether the
image host answers a polite client or returns 429.
If-Modified-Since on every repeat request
(4chan’s published API rules). These are hard limits, not settings.Retry-After is waited in full (only values over 24 h
are cut); without one the delay doubles from 2 s, capped at five minutes.User-Agent: CloverEye/<version>. Nothing about the user is sent.ws_board: 0) stay hidden until the user opts in with an
explicit 18+ confirmation.ui/fonts/Inter-OFL.txtui/fonts/NotoSansMono-OFL.txtBoth are subset to Latin, Greek and Cyrillic and converted to WOFF2.