Skip to content

Releases: HenryGetz/LustLess

v0.1.11 — LustLess (screen accountability actually captures and scores)

Choose a tag to compare

@HenryGetz HenryGetz released this 14 Oct 20:25

Screen accountability actually captures and scores

v0.1.10 shipped with screen capture not working at all — on either backend, on any machine where the user logs in after the daemon starts (i.e. every normal boot). If you installed 0.1.10, upgrade.

v0.1.10 shipped the Wayland backend but screen capture did not work at all on
a real machine, on either backend. Three bugs, all found by running the daemon
on a real X11 desktop rather than a VM, plus one that made a working capture
score nothing:

  • Sessions discovered after startup were silently dropped. logind's User
    property is the struct (uo); the watcher read it as a bare u32, so the read
    always failed and watch_session returned early. Sessions present at daemon
    start come from ListSessions (which already carries the uid) and were fine —
    but every session created after that arrives via the SessionNew signal and
    needs the property. On a normal boot the daemon starts before anyone logs in,
    so every session was dropped and capture skipped every tick as "session
    inactive" forever. Fixed by reading the struct, with a regression test that
    reads the property off real logind and pins the old shape as failing.
  • An ssh session could shadow the desktop. Session selection ranked by
    liveness only, so on any machine the operator was also ssh'd into, the tty
    session won and the capture path then refused it as a text session. Selection
    now prefers a local graphical session (x11/wayland, not remote) first.
  • The gate and the backend could disagree. gate() allowed a tick if any
    session for the uid was active+unlocked, while the backend captured the
    session selection picked. Both now judge the same session.
  • A capture with no window list was never scored. When the X11 backend could
    not read _NET_CLIENT_LIST it wrote apps: [] and only logged a warning
    claiming a full-frame fallback that did not exist. The classifier scores the
    apps in the manifest, so every capture was analyzed as zero windows — the
    daemon looked perfectly healthy while doing nothing. It now synthesizes one
    full-frame entry per monitor, as the Wayland backend always has.
  • Capture failure is no longer silent: "no session tracked for a signed-in uid"
    is a warning naming the defect, not an indistinguishable "session inactive".

Verified on a real X11 session (Display=:0, Type=x11, alongside five ssh
sessions for the same uid): session env applied (uid 1000, type X11, display Some(":0")) → backend selected (X11) → a real 1280×800 capture with 3069
distinct colours, written as a complete spool transaction (PNG + manifest +
ready), consumed by the classifier, and scored:
{"analyzed_count":1,"top_score":0.06666}. The client_screenshot metric row
in the daemon's ledger corroborates the write.


How this was found and verified

Found by running the daemon on a real X11 desktop (this workstation:
Type=x11, Display=:0, active) which also has five ssh sessions for the same
uid — a configuration that exposed two of the three bugs at once.

Verified end to end on that machine, unprivileged, with a test config:

capture: session env applied (uid 1000, type X11, display Some(":0"))
capture: backend selected (X11)
im0.png  1280x800, 3069 distinct colours
manifest.json + ready            (complete transaction)
{"analyzed_count":1,"top_score":0.06666}   (classifier result)

plus the client_screenshot metric row in the daemon's ledger corroborating the
write. Workspace: 355 tests pass, clippy -D warnings clean.

Packages

lustless_0.1.11_amd64.deb, lustless-0.1.11-1.x86_64.rpm,
lustless-0.1.11-1-x86_64.pkg.tar.zst, lustless-0.1.11-r0.apk, plus
install.sh and lustless.rsa.pub.


Correction: Wayland capture does not work in 0.1.11

This release fixed screen capture for X11 sessions and says "captures and
scores", which is true there. On a Wayland session it does not: the daemon
makes the xdg-desktop-portal ScreenCast grant as root, and a stock D-Bus
session bus rejects uid 0 (portal request failed: session bus … I/O error: failed to read from socket), so nothing is captured. Reproduced on Debian 13.7
(GNOME/Wayland) with no host changes, and on Ubuntu 24.04 only after a
hand-written allow-root bus policy plus setcap cap_sys_ptrace+ep on the portal
binaries — which is why it looked fine during this release's verification.

v0.1.12 fixes this properly: the grant is made by a privilege-dropped helper
running as the session user, which hands the PipeWire fd to the daemon. No host
policy changes, no capabilities added to system binaries. Upgrade if you are on a
Wayland desktop (Zorin Core, Ubuntu, Fedora, Debian GNOME).

v0.1.10 — LustLess (Wayland capture on GNOME: version-tolerant PipeWire bindings)

Choose a tag to compare

@HenryGetz HenryGetz released this 14 Oct 19:12

Wayland capture ships on GNOME (Zorin Core, Ubuntu, Fedora)

Screen capture on a Wayland session now uses the compositor's ScreenCast portal instead of falling back to X11/XWayland root capture.

Screen capture on a Wayland session (GNOME: Zorin Core, Ubuntu, Fedora) now uses
the compositor's ScreenCast portal instead of falling back to X11/XWayland root
capture. The backend existed since v0.1.7 but could not ship, and the reason was
structural rather than a flag: the pipewire crate cannot build against the
Zorin 17 / Ubuntu 22.04 baseline. Its libspa bindings need symbols and struct
fields that do not exist in libspa 0.3.48 (spa_meta_first,
spa_meta_region_is_valid, spa_video_info_raw.flags), and
struct spa_video_info_raw changed layout between 0.3.48 and current PipeWire
(flags inserted after format, modifier widened), so no single linked build
could be correct on both 0.3.48 and 1.0.x — and one .deb has to cover both.

  • Our own version-tolerant PipeWire bindings replace the crate
    (crates/lustless-capture/src/pipewire_sys.rs): libpipewire-0.3.so.0 is
    dlopened and the 25 entry points that exist in 0.3.48 and 1.0.x are
    resolved at runtime. The two calls we need that are header macros
    (pw_core_get_registry, pw_registry_add_listener) are reached through the
    real spa_interface vtable — its layout and the pw_core_methods member
    order were verified identical across 0.3.48, 1.0.5 and master. Event tables
    carry trailing padding and the 0.3.x version so a newer library's copy stays
    inside our allocation. No spa struct ever crosses the boundary: only
    byte-level SPA pods do, which is what makes one build correct on both
    baselines. Consequences: the release build needs neither libpipewire-0.3-dev
    nor libclang-dev, no shipped binary has a libpipewire/libspa
    DT_NEEDED, and the packages declare PipeWire as a weak dependency
    (Recommends/optdepends) — absent PipeWire degrades to the X11 path
    instead of failing to install.
  • Our own SPA pod codec (pipewire_pod.rs) replaces the crate's
    PodSerializer/VideoInfoRaw: it builds the EnumFormat we negotiate and
    parses the format the compositor picks, with every constant read from the
    real headers and independently probed on 22.04 and 24.04 (all 24 print
    identically).
  • Backend fixes found while porting: the stream no longer asks for
    PW_STREAM_FLAG_DRIVER (wrong for a capture stream, and a way to get no
    frames); frame extraction honours chunk.offset/chunk.size/chunk.stride
    instead of assuming a packed stride, rejects DMA-BUF buffers with a log
    instead of reading them, and queues buffers back (the old code leaked
    every one); the PipeWire thread builds the whole graph itself instead of
    taking a command channel.
  • Portal permission persistence. The ScreenCast restore token is stored per
    user (<state_dir>/<uid>/portal-restore-token, 0600) and sent back with
    PersistMode, so the compositor re-uses the grant instead of re-prompting on
    every login. The portal session is opened on the session bus explicitly
    (Screencast::with_connection), which is what a root system service needs.
  • The daemon now chooses its backend from the logind session, not from its
    own environment. A system service has no session environment, so
    XDG_SESSION_TYPE was always absent: an X11 desktop would have picked the
    Wayland backend, and the X11 backend could not see $DISPLAY at all.
    sessions.rs reads the session's Type/Display/Remote/Leader and
    derives the environment (DISPLAY, XAUTHORITY, XDG_RUNTIME_DIR,
    DBUS_SESSION_BUS_ADDRESS); text and remote sessions are never captured.
  • X11 capture can connect now. A live run showed the daemon resolving the
    session correctly and then failing with $DISPLAY variable not set, because
    logind's per-session Display property is empty on real machines (verified on
    Ubuntu 24.04 and Zorin 17.3). DISPLAY/XAUTHORITY are recovered from the
    session leader's /proc/<pid>/environ, then from a bounded walk of its
    descendants, then from the ordered XAUTHORITY file probe — and only then
    does an X11 session fall back to :0.
    Not fixed by this: in the same live run the daemon never got past its logind
    gate (Skip capturing Screenstate (session inactive) on every tick) even
    though loginctl reported Active=yes for that session — so no screenstate
    has ever been observed to be captured on a real desktop
    , on either backend.
    See gap 0 in docs/impl/PARITY.md.

Verification: 350 workspace tests pass and clippy -D warnings is clean; the
FFI layer is covered by an integration test that compiles a stub
libpipewire-0.3.so.0 from the real headers and drives the wrappers end to end
(registry vtable, pod negotiation, padded-stride extraction, buffer queueing),
plus a test that resolves the real system library when one is installed. The
build was verified in the ubuntu:22.04 container without any PipeWire
headers, and the resulting release binaries were checked for the absence of a
libpipewire link dependency. Live VM evidence collected so far (daemon
session resolution, classifier artifact loading) is in docs/impl/TEST-MATRIX.md;
the remaining live step — a real compositor granting ScreenCast — is gap 0 in
docs/impl/PARITY.md.


What is proven, and what is not

Proven: 350 workspace tests pass, clippy -D warnings clean; the SPA pod
constants print identically on ubuntu:22.04 (libspa 0.3.48) and ubuntu:24.04
(1.0.x); all 25 symbols resolve against a real libpipewire-0.3.so.0 1.0.5; an
integration test compiles a stub libpipewire-0.3.so.0 from the real 0.3.48
headers and drives the wrappers end to end; the release builds in the
ubuntu:22.04 container with no libpipewire-0.3-dev and no
libclang-dev; no shipped binary has a libpipewire/libspa DT_NEEDED; and
a live install of 0.1.10 reached a running daemon, offline sign-in, a fresh
logind registry and classifier v0.1.10 ready with all six CE artifacts.

Not proven — read this before relying on screen accountability:

  1. No capture has ever been observed end-to-end, on either backend. In the
    one live run that got that far, every tick logged
    Skip capturing Screenstate (session inactive) while loginctl reported the
    session Active=yes, so the daemon never reached the portal at all. That is
    gap 1 in docs/impl/PARITY.md, and it outranks everything v0.1.10 added.
  2. The portal ScreenCast grant and real frame extraction are unverified against a
    compositor (gap 0); the FFI proof is against a stub built from the same
    headers, which is self-consistency rather than independent ground truth — the
    real library check covers symbol resolution only.
  3. X11 capture on a real desktop is unverified for the same reason as (1).

Packages

lustless_0.1.10_amd64.deb, lustless-0.1.10-1.x86_64.rpm,
lustless-0.1.10-1-x86_64.pkg.tar.zst, lustless-0.1.10-r0.apk, plus
install.sh and lustless.rsa.pub. PipeWire is a weak dependency now
(Recommends/optdepends): without it the client still installs and falls back
to X11 capture.

v0.1.9 — LustLess (hardening: local tamper, fail-open DNS, poisoned queues)

Choose a tag to compare

@HenryGetz HenryGetz released this 14 Oct 14:55

Linux accountability client (Covenant Eyes-compatible, clean-room reimplementation).

Install

curl -fsSL https://github.com/HenryGetz/LustLess/releases/latest/download/install.sh | sudo bash

Detects your distro, downloads the matching package, installs it with the
native tool, starts the agent — and the sign-in window opens. Log in there.
Other ways:

Distro File Command
Debian / Ubuntu / Zorin / Mint lustless_0.1.9_amd64.deb sudo apt install ./lustless_0.1.9_amd64.deb
Fedora lustless-0.1.9-1.x86_64.rpm sudo dnf install ./lustless-0.1.9-1.x86_64.rpm
Bazzite / immutable Fedora lustless-0.1.9-1.x86_64.rpm sudo rpm-ostree install ./lustless-0.1.9-1.x86_64.rpm && reboot
Arch / EndeavourOS / SteamOS lustless-0.1.9-1-x86_64.pkg.tar.zst sudo pacman -U ./lustless-0.1.9-1-x86_64.pkg.tar.zst
Alpine lustless-0.1.9-r0.apk sudo apk add --allow-untrusted ./lustless-0.1.9-r0.apk

Uninstall: sudo apt purge lustless (or the dnf/rpm-ostree/pacman/apk
equivalent) — restores the DNS, browser-policy and polkit state exactly as
found.

What's new — hardening

Two focused reviews (local attack surface; error paths) were run against the
shipped code and everything they found is fixed here. In an accountability
client the person at the keyboard is the adversary, so the tamper findings are
as important as crashes.

Local tamper, previously possible for any unprivileged user:

  • Dashboard tokens were validated and then discarded, so any local user could
    fetch a token over the 0666 IPC socket and drive the signed-in session: sign
    it out, set the weakest filter level, or allow-list *. Mutations now require
    the token to own the session.
  • 0x4c USER_LOGOUT had no ownership check — any uid could force a sign-out
    (and with fail_closed_when_signed_out = false, stop the filter).
  • 0x61/0x43/0x4d/0x54/0x67 leaked the signed-in username, filter
    state and outbox path to any uid, minted a dashboard token for them, and let
    them trigger diagnostics, outbox flushes and a feed fetch.
  • The break-glass audit line now carries the promised
    org.lustless.break-glass uid=… reason=… tag; denials are distinguishable.
  • The agent's session-token file no longer falls back to world-writable /tmp
    (symlink capture) and refuses a symlinked directory.

Fail-open and queue defects:

  • Filtering could silently be off while the daemon looked healthy: a DNS
    forwarder that failed to build or bind only logged. It is now marked Outage
    and reported, dns.listen/ports are validated at startup, and an unparsable
    config refuses to start instead of quietly using defaults.
  • Block pages never loaded: the synthesized CNAME was owned by the block
    host instead of the queried name, so a stub resolver returned NODATA. Now
    RFC 1034-shaped.
  • A fail-closed classifier exit counted as clean (it prints bye and
    returns 1), bypassing the crash budget, metric and escalation and respawning
    every 5 s forever with screen accountability off.
  • Rejected (4xx) batches poisoned the upload queue forever (drop_invalid
    matched only locally-invalid rows, so nothing was deleted and the batch was
    re-claimed oldest-first). Deterministic rejections now delete exactly the
    claimed rows.
  • A second daemon could run, sharing the outbox/spool/crash ledger and
    reporting a false crash: lustlessd now holds an exclusive flock.
  • Spool orphans are swept by the daemon (previously only at classifier
    startup), shutdown aborts overrunning tasks, and the staged update payload is
    removed after install.

Workspace suite: 313 passed, 0 failed. Everything from 0.1.8 (crash
pipeline), 0.1.7 (parity audit, real-model proof), 0.1.6 (Zorin Lite branding),
0.1.5 (DNS that stays alive) and 0.1.4 (agent main loop) is unchanged.

v0.1.8 — LustLess (crash-reporting pipeline, CE parity minus the databases)

Choose a tag to compare

@HenryGetz HenryGetz released this 14 Oct 10:37

Linux accountability client (Covenant Eyes-compatible, clean-room reimplementation).

Install

curl -fsSL https://github.com/HenryGetz/LustLess/releases/latest/download/install.sh | sudo bash

Detects your distro, downloads the matching package, installs it with the
native tool, starts the agent — and the sign-in window opens. Log in there.
Other ways:

Distro File Command
Debian / Ubuntu / Zorin / Mint lustless_0.1.8_amd64.deb sudo apt install ./lustless_0.1.8_amd64.deb
Fedora lustless-0.1.8-1.x86_64.rpm sudo dnf install ./lustless-0.1.8-1.x86_64.rpm
Bazzite / immutable Fedora lustless-0.1.8-1.x86_64.rpm sudo rpm-ostree install ./lustless-0.1.8-1.x86_64.rpm && reboot
Arch / EndeavourOS / SteamOS lustless-0.1.8-1-x86_64.pkg.tar.zst sudo pacman -U ./lustless-0.1.8-1-x86_64.pkg.tar.zst
Alpine lustless-0.1.8-r0.apk sudo apk add --allow-untrusted ./lustless-0.1.8-r0.apk

Uninstall: sudo apt purge lustless (or the dnf/rpm-ostree/pacman/apk
equivalent) — restores the DNS, browser-policy and polkit state exactly as
found.

What's new

The crash-reporting pipeline exists now (it was the biggest parity gap):

  • Detection: a clean-shutdown marker; a missing marker at startup means the
    previous run died — systemd's Restart=always is CE's restart helper, so the
    report is made by the next start (CE's timing). A first-ever start reports
    nothing.
  • Type: a panic hook plus fatal-signal handlers (SIGSEGV/SIGABRT/SIGBUS/
    SIGILL/SIGFPE) record the reason, so the metric's crash_type is
    PANIC/SIGNAL where CE hardcodes GENERIC.
  • Attachments: CE's log groups under CE's caps (5 MB per file, 19 MB
    total). state-summary.json replaces CE's debugDB.zip — the activity
    databases are never attached
    , which is deliberately stricter than CE.
  • Counters and metric: CE's ClientCrashesAt/ClientCrashesSince pair,
    the client_crashes telemetry row, and CE's
    client_crash{crash_type=…,count=…} metrics event, cleared after reporting.
  • Test hooks: --crashatonce=true (CE's command-line switch) and
    [daemon] crash_test_hook = "force" (CE's ForceCrashClient setting).

Verified end to end: forced SIGABRT → last-reason=SIGNAL → next start logs
previous run crashed (SIGNAL, 1 in window), writes the attachments, emits the
metric row with crash_type=SIGNAL, inserts client_crashes=SIGNAL:1, clears
the counter; a clean run reports nothing.

Also in this release: the config loader's known-key list now accepts
crash_test_hook (it silently ignored unknown keys before). Everything from
0.1.7 (parity audit, real-model proof, spool fallback), 0.1.6 (branded sign-in
window, Zorin Lite verification), 0.1.5 (DNS that stays alive) and 0.1.4 (agent
main loop) is unchanged.

v0.1.7 — LustLess (parity audit, real-model proof, spool fallback)

Choose a tag to compare

@HenryGetz HenryGetz released this 14 Oct 09:23

Linux accountability client (Covenant Eyes-compatible, clean-room reimplementation).

Install

curl -fsSL https://github.com/HenryGetz/LustLess/releases/latest/download/install.sh | sudo bash

Detects your distro, downloads the matching package, installs it with the
native tool, starts the agent — and the sign-in window opens. Log in there.
Other ways:

Distro File Command
Debian / Ubuntu / Zorin / Mint lustless_0.1.7_amd64.deb sudo apt install ./lustless_0.1.7_amd64.deb
Fedora lustless-0.1.7-1.x86_64.rpm sudo dnf install ./lustless-0.1.7-1.x86_64.rpm
Bazzite / immutable Fedora lustless-0.1.7-1.x86_64.rpm sudo rpm-ostree install ./lustless-0.1.7-1.x86_64.rpm && reboot
Arch / EndeavourOS / SteamOS lustless-0.1.7-1-x86_64.pkg.tar.zst sudo pacman -U ./lustless-0.1.7-1-x86_64.pkg.tar.zst
Alpine lustless-0.1.7-r0.apk sudo apk add --allow-untrusted ./lustless-0.1.7-r0.apk

Uninstall: sudo apt purge lustless (or the dnf/rpm-ostree/pacman/apk
equivalent) — restores the DNS, browser-policy and polkit state exactly as
found.

What's new

Parity work, driven by a subsystem-by-subsystem audit now recorded in
docs/impl/PARITY.md (Windows surface → Linux status → evidence → gaps).

  • The spool consumer survives a missing inotify instance. Previously the
    classifier exited (Too many open files) on a machine at the per-user
    inotify limit; it now falls back to polling with the same semantics — only
    newly-ready folders, and renames still reported as Renamed (matched by
    folder inode). All 28 capture tests pass, including the ones the host limit
    was breaking.
  • The real CE classifier bundle is proven end to end: decrypt → load
    (threshold 0.467 from the calibration artifact) → score a 640×480 capture →
    {"app":"firefox","score":0.0,"flagged":false,"scored_windows":1}.
  • Wayland capture is characterised, not hand-waved: the portal backend is
    implemented but cannot ship on this baseline — the pipewire crate's spa
    bindings need libspa newer than Ubuntu 22.04 / Zorin 17 provides, and a
    binary linked against a newer libspa would not load on the target. Named as
    gap 0 with the work required (dlopen-style bindings or a crate pin).

Everything from 0.1.6 (branded sign-in window, Zorin Lite verification), 0.1.5
(DNS that stays alive) and 0.1.4 (agent main loop) is unchanged.

v0.1.6 — LustLess (branded sign-in window, verified on Zorin OS Lite)

Choose a tag to compare

@HenryGetz HenryGetz released this 14 Oct 04:04

Linux accountability client (Covenant Eyes-compatible, clean-room reimplementation).

Install

curl -fsSL https://github.com/HenryGetz/LustLess/releases/latest/download/install.sh | sudo bash

Detects your distro, downloads the matching package, installs it with the
native tool, starts the agent — and the sign-in window opens. Log in there.
Other ways:

Distro File Command
Debian / Ubuntu / Zorin / Mint lustless_0.1.6_amd64.deb sudo apt install ./lustless_0.1.6_amd64.deb
Fedora lustless-0.1.6-1.x86_64.rpm sudo dnf install ./lustless-0.1.6-1.x86_64.rpm
Bazzite / immutable Fedora lustless-0.1.6-1.x86_64.rpm sudo rpm-ostree install ./lustless-0.1.6-1.x86_64.rpm && reboot
Arch / EndeavourOS / SteamOS lustless-0.1.6-1-x86_64.pkg.tar.zst sudo pacman -U ./lustless-0.1.6-1-x86_64.pkg.tar.zst
Alpine lustless-0.1.6-r0.apk sudo apk add --allow-untrusted ./lustless-0.1.6-r0.apk

Uninstall: sudo apt purge lustless (or the dnf/rpm-ostree/pacman/apk
equivalent) — restores the DNS, browser-policy and polkit state exactly as
found.

What's new

Branding polish, verified on a Zorin OS 17.3 Lite live VM (the primary
target desktop):

  • The sign-in window now shows the CE shield (the real artwork from the
    Windows build, native size) with the product name and a one-line subtitle —
    the login screen looks like the client it reimplements.
  • The shield is installed as /usr/share/lustless/lustless-mark.png (same
    artwork the dashboard embeds) in every format.
  • StartupWMClass=lustless-agent in both .desktop entries, so Xfce's
    taskbar/window list and the Whisker/Zorin menu use the CE icon for the
    running window instead of a generic one.
  • polkit prompts (org.lustless.break-glass, org.lustless.disable-protection)
    now carry the CE shield as their icon.

Carried over from 0.1.5: DNS that stays alive (installer probes the filter
before redirecting, daemon restarts forever) and instant blocked answers;
from 0.1.4: the agent's main loop actually runs and the sign-in window opens
by itself on install and at login.

v0.1.5 — LustLess (DNS that stays alive, and answers instantly)

Choose a tag to compare

@HenryGetz HenryGetz released this 14 Oct 03:12

Linux accountability client (Covenant Eyes-compatible, clean-room reimplementation).

Install

curl -fsSL https://github.com/HenryGetz/LustLess/releases/latest/download/install.sh | sudo bash

Detects your distro, downloads the matching package, installs it with the
native tool, starts the agent — and the sign-in window opens. Log in there.
Other ways:

Distro File Command
Debian / Ubuntu / Zorin / Mint lustless_0.1.5_amd64.deb sudo apt install ./lustless_0.1.5_amd64.deb
Fedora lustless-0.1.5-1.x86_64.rpm sudo dnf install ./lustless-0.1.5-1.x86_64.rpm
Bazzite / immutable Fedora lustless-0.1.5-1.x86_64.rpm sudo rpm-ostree install ./lustless-0.1.5-1.x86_64.rpm && reboot
Arch / EndeavourOS / SteamOS lustless-0.1.5-1-x86_64.pkg.tar.zst sudo pacman -U ./lustless-0.1.5-1-x86_64.pkg.tar.zst
Alpine lustless-0.1.5-r0.apk sudo apk add --allow-untrusted ./lustless-0.1.5-r0.apk

Uninstall: sudo apt purge lustless (or the dnf/rpm-ostree/pacman/apk
equivalent) — restores the DNS, browser-policy and polkit state exactly as
found.

What's new

Fail-closed is about filtering decisions, not about taking the machine off
the network.
Three fixes:

  • DNS is never broken by an install: the installer probes the filter on
    127.0.0.2:53 and, when nothing answers, leaves your /etc/resolv.conf
    untouched and tells you why (previously a machine where the daemon could not
    start lost all name resolution — no package manager, no updates).
  • The daemon is never left dead: Restart=always, no restart limit (was
    3 restarts per 10 s, after which systemd gave up and the box had no
    resolver), OpenRC respawn_max=0. The service re-applies the DNS redirect
    on every start, so a machine installed while the filter was broken becomes
    filtered as soon as it runs.
  • Blocked answers are instant: signed-out lookups took ~2 s because
    per-query process attribution (a SOCK_DIAG netlink dump with a 2 s timeout)
    ran before the fail-closed shortcut. Reordered, plus a 30 s circuit breaker
    on netlink failures. Measured: 2055 ms → 4-20 ms per query, verdict
    unchanged (blocked.lustless.local → 127.0.0.2).

Carried over from 0.1.4: the agent's main loop actually runs (gio hold guard),
the sign-in window opens by itself on install and at login, the CE shield at
every icon size, and the application-menu launcher.

v0.1.4 — LustLess (agent main loop fixed: install → sign-in window)

Choose a tag to compare

@HenryGetz HenryGetz released this 13 Oct 05:22

Linux accountability client (Covenant Eyes-compatible, clean-room reimplementation).

Install

curl -fsSL https://github.com/HenryGetz/LustLess/releases/latest/download/install.sh | sudo bash

Detects your distro, downloads the matching package, installs it with the
native tool, starts the agent — and the sign-in window opens. Log in there.
Other ways:

Distro File Command
Debian / Ubuntu / Zorin / Mint lustless_0.1.4_amd64.deb sudo apt install ./lustless_0.1.4_amd64.deb
Fedora lustless-0.1.4-1.x86_64.rpm sudo dnf install ./lustless-0.1.4-1.x86_64.rpm
Bazzite / immutable Fedora lustless-0.1.4-1.x86_64.rpm sudo rpm-ostree install ./lustless-0.1.4-1.x86_64.rpm && reboot
Arch / EndeavourOS / SteamOS lustless-0.1.4-1-x86_64.pkg.tar.zst sudo pacman -U ./lustless-0.1.4-1-x86_64.pkg.tar.zst
Alpine lustless-0.1.4-r0.apk sudo apk add --allow-untrusted ./lustless-0.1.4-r0.apk

Uninstall: sudo apt purge lustless (or the dnf/rpm-ostree/pacman/apk
equivalent) — restores the DNS, browser-policy and polkit state exactly as
found.

What's new

v0.1.3 was broken and is superseded: its agent's GTK main loop returned the
instant it started, so the tray menu, the notifications and the promised
sign-in window all did nothing.
Fixed here:

  • gio's hold() returns an RAII guard that releases the application on
    drop; called as a bare statement it released a line later, and a windowless
    GtkApplication with a zero use count returns from run() immediately. The
    guard now lives for the process lifetime — the main loop, tray menu,
    notifications and dialogs all work.
  • The program name is kept in the argv passed to run_with_args (an empty
    slice leaves GApplication at argc 0, which returns immediately too).
  • lustless-maintainer wrote user_resolvers = ["a", "b] — invalid TOML,
    which made the daemon discard the entire configuration file. Quoting fixed.
  • The sign-in dialog's default button pointed at a temporary widget (GTK
    critical); it now sets a default response.

Carried over from 0.1.3: the install ends on the sign-in window, the CE shield
at every icon size (menu, window icon, dashboard favicon, tray), and
lustless-agent --show / the application-menu entry to reopen it.

v0.1.3 — LustLess (install → sign-in window, original icons)

Choose a tag to compare

@HenryGetz HenryGetz released this 13 Oct 04:37

Linux accountability client (Covenant Eyes-compatible, clean-room reimplementation).

Install

curl -fsSL https://github.com/HenryGetz/LustLess/releases/latest/download/install.sh | sudo bash

Detects your distro, downloads the matching package, installs it with the
native tool, starts the tray agent. Other ways:

Distro File Command
Debian / Ubuntu / Zorin / Mint lustless_0.1.3_amd64.deb sudo apt install ./lustless_0.1.3_amd64.deb
Fedora lustless-0.1.3-1.x86_64.rpm sudo dnf install ./lustless-0.1.3-1.x86_64.rpm
Bazzite / immutable Fedora lustless-0.1.3-1.x86_64.rpm sudo rpm-ostree install ./lustless-0.1.3-1.x86_64.rpm && reboot
Arch / EndeavourOS / SteamOS lustless-0.1.3-1-x86_64.pkg.tar.zst sudo pacman -U ./lustless-0.1.3-1-x86_64.pkg.tar.zst
Alpine lustless-0.1.3-r0.apk sudo apk add --allow-untrusted ./lustless-0.1.3-r0.apk

Uninstall: sudo apt purge lustless (or the dnf/rpm-ostree/pacman/apk
equivalent) — restores the DNS, browser-policy and polkit state exactly as
found.

What's new

  • The install ends on the sign-in window: a signed-out agent opens the
    login window by itself a couple of seconds after it starts, and the
    installer starts it for you (and at every graphical login until you sign
    in). Fresh machine → one command → log in in the GUI.
  • lustless-agent --show, the application-menu entry and re-activation of a
    running agent all present the same window.
  • Icons are the original client's: the CE shield at every size the desktop
    asks for (hicolor 16/24/32/48/256), as the sign-in window's icon, as the
    dashboard favicon and the tray icon.
  • Carried over from 0.1.2: application-menu launcher, one-liner installer with
    Recommends, bind-mounted /etc/resolv.conf fix, honest version numbers.

Superseded by v0.1.4 — do not use this release. Its agent's GTK main loop returned immediately on start, so the tray menu, notifications and the sign-in window this release advertised did nothing. v0.1.4 fixes that (gio hold guard, argv, config quoting).

v0.1.2 — LustLess (accountability client, Covenant-Eyes-compatible Linux edition)

Choose a tag to compare

@HenryGetz HenryGetz released this 13 Oct 01:07

Linux accountability client (Covenant Eyes-compatible, clean-room reimplementation).

Install

curl -fsSL https://github.com/HenryGetz/LustLess/releases/latest/download/install.sh | sudo bash

Detects your distro, downloads the matching package, installs it with the
native tool, starts the tray agent. Other ways:

Distro File Command
Debian / Ubuntu / Zorin / Mint lustless_0.1.2_amd64.deb sudo apt install ./lustless_0.1.2_amd64.deb
Fedora lustless-0.1.2-1.x86_64.rpm sudo dnf install ./lustless-0.1.2-1.x86_64.rpm
Bazzite / immutable Fedora lustless-0.1.2-1.x86_64.rpm sudo rpm-ostree install ./lustless-0.1.2-1.x86_64.rpm && reboot
Arch / EndeavourOS / SteamOS lustless-0.1.2-1-x86_64.pkg.tar.zst sudo pacman -U ./lustless-0.1.2-1-x86_64.pkg.tar.zst
Alpine lustless-0.1.2-r0.apk sudo apk add --allow-untrusted ./lustless-0.1.2-r0.apk

Uninstall: sudo apt purge lustless (or the dnf/rpm-ostree/pacman/apk
equivalent) — restores the DNS, browser-policy and polkit state exactly as
found.

What's new

  • Sign-in works without a tray: every package ships a LustLess entry in
    the application menu (lustless-agent --show) — sign-in dialog when signed
    out, dashboard when signed in. Headless: lustlessctl signin <username>.
  • One-liner installer (install.sh is attached below), installing with
    Recommends (pulls the Xfce SNI host) and starting the per-user tray agent
    immediately instead of waiting for a re-login.
  • Fixed: the maintainer helper no longer aborts configure on a bind-mounted
    /etc/resolv.conf (containers, WSL).
  • Version numbers are real again: package/CLI version 0.1.2, matching this
    release tag (previously the same 0.1.0 was replaced in place, so upgrades
    printed "Unpacking 0.1.0 over 0.1.0").