{"id":50765119,"url":"https://github.com/greenm01/lockme","last_synced_at":"2026-06-11T13:01:39.876Z","repository":{"id":354786587,"uuid":"1224890413","full_name":"greenm01/lockme","owner":"greenm01","description":"A secure Matrix style GPU rendered screen-lock for Wayland.","archived":false,"fork":false,"pushed_at":"2026-05-10T19:15:48.000Z","size":1468,"stargazers_count":7,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-10T21:20:31.476Z","etag":null,"topics":["gpu","matrix","nim","screen-lock","screenlocker","wayland","wayland-client"],"latest_commit_sha":null,"homepage":"","language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/greenm01.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-29T18:27:06.000Z","updated_at":"2026-05-10T19:15:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/greenm01/lockme","commit_stats":null,"previous_names":["greenm01/lockme"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/greenm01/lockme","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greenm01%2Flockme","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greenm01%2Flockme/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greenm01%2Flockme/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greenm01%2Flockme/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/greenm01","download_url":"https://codeload.github.com/greenm01/lockme/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greenm01%2Flockme/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34199516,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gpu","matrix","nim","screen-lock","screenlocker","wayland","wayland-client"],"created_at":"2026-06-11T13:01:39.251Z","updated_at":"2026-06-11T13:01:39.856Z","avatar_url":"https://github.com/greenm01.png","language":"Nim","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lockme\n\nA hardened, minimalist screen locker for Wayland compositors that\nimplement `ext-session-lock-v1`.\n\n![Matrix rain lock screen](matrix_rain.png?v=20260511)\n\n`lockme` aims at one thing: keep a typed password out of every place\nthe kernel and userspace would otherwise let it leak.\n\nThe password buffer is page-aligned, `mlock`'d, marked\n`MADV_DONTDUMP`, and wiped with `explicit_bzero` on every clear. The\nlocker process drops dumpability, denies new privileges, and\nsuppresses core dumps. PAM runs in a forked child that talks back\nover a length-prefixed pipe, so a misbehaving auth module never\nshares an address space with your secret.\n\nThe UI defaults to GPU-rendered Matrix rain while idle. Typing switches to a\nsolid configurable palette, failed auth shows a solid failure color, and `Alt-B`\ntoggles between Matrix and a blank screen. Colors and the rest of the runtime\nknobs live in a small KDL 2.0 config file (see [Configuration](#configuration)).\n\nIt is small on disk too: a stripped release binary is **558 KiB**,\nwell under the size of `hyprlock` and several times smaller than\n`waylock` while shipping more hardening than either. See\n[compare.md](compare.md) for the full security and size comparison\nagainst `swaylock`, `waylock`, and `hyprlock`.\n\nPress Alt-B to toggle between a blank screen and the matrix rainfall.\n\n## Why\n\nI was missing waylock on the Niri window manager and decided to write a\nnew one from scratch \u0026mdash; and because I dig Nim.\n\n## Install build dependencies\n\n`lockme` needs Nim/Nimble, a C toolchain, `pkg-config`, the Nimble package\n`nimkdl \u003e= 2.1.0`, and development headers for Wayland, Wayland EGL, EGL,\nOpenGL ES 3, xkbcommon, and PAM. The OpenGL ES link dependency is discovered\nthrough the standard `glesv2` pkg-config module, even though the renderer\nincludes GLES3 headers.\n\nVoid Linux:\n\n```sh\nsudo xbps-install -Sy nim base-devel pkg-config wayland-devel libglvnd-devel libxkbcommon-devel pam-devel\nnimble install -y nimkdl\n```\n\nVoid, Arch Linux, Debian, and Ubuntu package `nimble` with `nim`.\n\nArch Linux:\n\n```sh\nsudo pacman -S --needed nim base-devel wayland libglvnd libxkbcommon pam pkgconf\nnimble install -y nimkdl\n```\n\nDebian/Ubuntu:\n\n```sh\nsudo apt update\nsudo apt install nim build-essential libwayland-dev libegl-dev libgles-dev libxkbcommon-dev libpam0g-dev pkg-config\nnimble install -y nimkdl\n```\n\n## Build\n\nBuild dependencies:\n\n- Nim `2.2.0` or newer\n- Nimble package `nimkdl \u003e= 2.1.0`\n- a C compiler\n- `pkg-config`\n- development packages for `wayland-client`, `xkbcommon`, `pam`, `egl`,\n  `glesv2`, and `wayland-egl`\n- OpenGL ES 3 headers, normally provided by the same development package\n  that provides `glesv2`\n\nProtocol refresh dependency:\n\n- `wayland-scanner`\n\n```sh\nnimble build\n```\n\nRelease builds use checked-in Wayland protocol stubs, so `wayland-scanner`\nand `wayland-protocols` are not required unless you are refreshing those\ngenerated files.\n\n## Format\n\nFormatting uses `nph`:\n\n```sh\nnimble setupTools\nnimble fmt\nnimble fmtCheck\n```\n\n`nimble setupTools` installs the pinned formatter as a user-level Nimble tool.\n`nimble fmt` formats maintained Nim sources. `nimble fmtCheck` is the\nnon-mutating check form for review or CI. The generated Matrix font source is\nleft to `nimble regenFont`.\n\n## Deploy\n\n```sh\nnimble deploy\n```\n\nThis builds an optimized release, installs `lockme` to `~/.local/bin/lockme`,\nand installs the PAM service file to `/etc/pam.d/lockme`. The binary install\nruns as your user; the PAM install uses `sudo` because `/etc/pam.d` is\nroot-owned.\n\n## Check compositor compatibility\n\n```sh\nlockme --check-protocols\n```\n\nAt runtime, the compositor must advertise:\n\n- `ext_session_lock_manager_v1`\n- `wp_viewporter`\n- either `wl_shm` or `wp_single_pixel_buffer_manager_v1`\n\n`wl_shm` is used for the default color surfaces when available.\n`wp_single_pixel_buffer_manager_v1` is optional and is used for solid-color\nbuffers when available.\n\n## Wayland protocol sources\n\n`lockme` uses `libwayland-client` directly through a small C shim and\ngenerated protocol stubs. It does not depend on a third-party Wayland wrapper\nlibrary; this keeps the C/Nim boundary explicit and leaves protocol handling\non the standard Wayland C stack.\n\nThe generated protocol files are checked in under `src/lockme/protocols`.\nTheir XML sources are vendored in `src/lockme/protocols/xml`:\n\n- `ext-session-lock-v1` from `wayland-protocols/staging`\n- `single-pixel-buffer-v1` from `wayland-protocols/staging`\n- `viewporter` from `wayland-protocols/stable`\n- `xdg-shell` from `wayland-protocols/stable`\n\nTo refresh the generated C/header files after updating the XML:\n\n```sh\nnimble regenProtocols\n```\n\nRefreshing protocols requires `wayland-scanner`. The task regenerates C/H\nfrom the vendored XML only; update the XML from `wayland-protocols` first\nwhen intentionally moving to a newer protocol revision. Commit the XML and\ngenerated C/H changes together.\n\n## Run\n\n```sh\nlockme\n```\n\nPlain `lockme` shows Matrix rain while idle and ignores the Enter key on an\nempty password buffer.\n\n### Battery / power\n\n**If you use a laptop, set `--idle-timeout`.** A continuously rendering GPU\nscreensaver prevents deep sleep and drains the battery noticeably on an\nunattended locked machine. Setting an idle timeout blanks the screen after the\nspecified number of seconds, letting the system reach low-power states:\n\n```sh\nlockme --idle-timeout 60   # blank after 60 s of inactivity\n```\n\nThe next keypress wakes the screen back to matrix. You can also set this in\n`~/.config/lockme/config.kdl` so you never forget it:\n\n```kdl\nidle-timeout 60\n```\n\n### Other common flags\n\n`--blank` starts on a blank screen instead of matrix rain.\n`--allow-empty-password` allows an empty Enter press to reach PAM.\n`--no-gpu` forces the CPU renderer and removes EGL from the process (also\nuseful on systems with unreliable GPU drivers).\n\nFor development only, `lockme --dev-mode` makes `Esc` unlock and exit cleanly\nwithout talking to PAM. This is intentionally insecure and should not be used\nfor a real screen lock, but it provides a compositor-safe escape hatch while\ntesting lockme itself.\n\nFor screenshots while developing the Matrix renderer, use:\n\n```sh\nlockme --dev-mode --dev-window\n```\n\nThis opens the Matrix rain in a normal Wayland window and does not lock the\nsession or start PAM.\n\nMatrix rain is rendered through a Sokol/EGL/GLES path when available. The glyph\natlas is generated from lockme's built-in high-resolution alpha glyph source,\nwhich is rasterized from the CNTR Koine Greek TrueType font; if GPU setup fails,\nlockme warns and falls back to the existing software renderer. The GPU rain\npipeline adapts MIT-licensed shader logic from Rezmason's Matrix rain renderer.\nWhile locked, `Alt-B` toggles between Matrix rain and a blank screen.\n\nTyping rotates the surface through a configurable input palette, and failed\nauthentication shows a solid failure color. The `--init-color`, `--input-color`\n(repeatable), and `--fail-color` flags override the defaults.\n\n### Default palette\n\n| State            | Color       | RGB        |\n| ---------------- | ----------- | ---------- |\n| At rest (`init`) | Black       | `0x000000` |\n| Typing (Father)  | Indigo      | `0x4B0082` |\n| Typing (Son)     | Royal Blue  | `0x003366` |\n| Typing (Spirit)  | Life Green  | `0x006400` |\n| Auth failure     | Crimson     | `0x8B0000` |\n\nRepeating `--input-color` defines a custom palette. The first occurrence\nreplaces the built-in palette; later occurrences append:\n\n```sh\nlockme --input-color 0x111111 --input-color 0x222222 --input-color 0x333333\n```\n\n## Configuration\n\n`lockme` reads an optional KDL 2.0 configuration file. The discovery order is:\n\n1. `--config \u003cpath\u003e` (must exist if specified),\n2. `$XDG_CONFIG_HOME/lockme/config.kdl` (default `~/.config/lockme/config.kdl`),\n3. each `$XDG_CONFIG_DIRS/lockme/config.kdl` in order (default `/etc/xdg`).\n\n`--no-config` disables the search entirely. CLI flags always win over values\nset in the config file. Parse and validation errors abort startup with a\ndiagnostic on stderr.\n\nA documented template lives at `examples/config.kdl` and is dropped into\n`~/.config/lockme/config.kdl` by `nimble installBin`/`nimble deploy` only if\nthat file does not already exist. Keys absent from the config always take the\nbuilt-in default, so an existing config never breaks when new options are\nadded; diff your file against `examples/config.kdl` after pulling updates to\nsee what is new. Both `0xRRGGBB` and `#RRGGBB` color forms\nare accepted in the config file (the CLI requires the `0x` form).\nLegacy Matrix font keys from older templates are accepted as no-ops; Matrix\nglyphs now always come from the built-in Koine Greek glyph source.\nBy default, Matrix glyph size follows the upstream Matrix renderer's 80-column\ngrid. Set `matrix-cell-scale \"auto\"` in the config for responsive sizing, or\nset it to a number from 1.0 through 8.0 only if you want a fixed glyph scale.\n\nThe built-in Matrix glyph alpha data is generated from the vendored CNTR\n`KoineGreek.ttf` font in `third_party/cntr-font`, copyright 2012-2023 Alan\nBunning / Center for New Testament Restoration and distributed under CC BY-SA\n4.0. Run `nimble regenFont` after changing the vendored font or glyph list; the\ngenerator uses Python with Pillow and fontTools.\n\n## Build size\n\nThe release build uses size-oriented flags (`--opt:size --mm:orc\n-d:useMalloc -flto -Wl,--gc-sections -Wl,-s`) so that the KDL parser\nand its transitive dependencies (`bigints`, `unicodedb`) do not bloat\nthe binary. The current stripped output is 558 KiB (570,808 bytes).\nRun `nimble sizecheck` to print the size of your build.\n\n## Platform requirements\n\n`lockme` is Linux-only. It relies on the following Linux-specific facilities\nto harden the password buffer and the auth child:\n\n- `mlock(2)` and `madvise(MADV_DONTDUMP)` on a page-aligned password buffer,\n  preventing it from being paged to swap or included in core dumps.\n- Best-effort `mlockall(2)` on the locker process and auth child to keep\n  transient password material on the stack and in libc/PAM internals out of\n  swap when `RLIMIT_MEMLOCK` permits it. Matrix mode and the auth child use\n  `MCL_CURRENT`; `--blank` uses `MCL_CURRENT | MCL_FUTURE` in the locker\n  process.\n- `explicit_bzero(3)` (glibc/musl) for password clearing that the compiler\n  is not permitted to elide.\n- `prctl(PR_SET_DUMPABLE, 0)` on both the parent and the auth child to\n  block ptrace and `/proc` snooping by other processes of the same UID.\n- `prctl(PR_SET_NO_NEW_PRIVS, 1)` on the parent after the PAM auth child is\n  forked, so future parent-side `execve` cannot gain privileges without\n  breaking PAM helpers such as `unix_chkpwd`.\n- `setrlimit(RLIMIT_CORE, 0)` to suppress core dumps for the locker.\n- `close_range(2)` (kernel 5.9+) in the auth child to drop inherited file\n  descriptors before invoking PAM; falls back to a manual loop on older\n  kernels.\n\n## Security\n\n`lockme` mirrors waylock's privilege-separation model: the parent process\nholds the Wayland connection and renders the lock surface, while a forked\nchild performs PAM authentication over a length-prefixed pipe. The\npassword buffer:\n\n- has a fixed `1024`-byte capacity rounded up to a page,\n- is allocated via `posix_memalign` and `mlock`'d for its lifetime,\n- is excluded from core dumps via `madvise(MADV_DONTDUMP)`,\n- is zeroed via `explicit_bzero` on every clear (including after each\n  failed authentication and after each `Backspace`),\n- has its protections re-applied after `--fork-on-lock`.\n\nThe auth child also re-applies best-effort `mlockall(MCL_CURRENT)` before\ninitializing PAM, because memory locks are not inherited across `fork(2)`.\n\nWith `--fork-on-lock`, the background process additionally redirects\n`stdin`/`stdout`/`stderr` to `/dev/null` to avoid `SIGPIPE` if the parent\nshell is closed.\n\n`RLIMIT_MEMLOCK` must be at least the password buffer size (one page).\nProcess-wide `mlockall` needs more headroom and can fail under normal\ndesktop mappings or restrictive limits. That best-effort failure is reported\nonly at `--log-level debug`; the locker continues with the password buffer's\nown mandatory `mlock` still active.\n\nSee [audit.md](audit.md) for the running security and performance review log,\nincluding the recent PAM, Matrix renderer, signal handling, and SHM sizing\nchecks.\n\n## PAM stack\n\n`lockme` performs authentication through PAM. The shipped default\n`pam.d/lockme` is a minimal, auditable, distribution-independent chain:\n\n```\nauth        optional      pam_faildelay.so delay=2000000\nauth        required      pam_unix.so     nullok\naccount     required      pam_unix.so\n```\n\nThis verifies a plain Unix password and applies a two-second failure delay\nwithout recording faillock tallies or locking the user out after mistyped\npasswords. That avoids the bad screen-locker failure mode where three\nincorrect attempts can block a later correct password for several minutes.\nMost Linux users authenticate this way and gain nothing from a larger PAM\nstack on their screen locker, so this is the default.\n\nThe default does NOT enable `pam_systemd_home`, GNOME Keyring or\nKWallet auto-unlock, fingerprint readers, smartcards, or any other\nauxiliary auth method. If you need any of those, install the full PAM\nfile instead:\n\n```sh\nnimble installPamFull\n# or, without nimble:\nsudo install -m 0644 pam.d/lockme.full /etc/pam.d/lockme\n```\n\nThe full file contains a single line, `auth include system-auth`, which\ndelegates authentication to the distribution's `system-auth` chain.\nThis is the same approach `waylock` and most other screen lockers ship\nwith. The trade-off is that `lockme`'s effective auth surface becomes\nwhatever `system-auth` says it is. To audit it, read\n`/etc/pam.d/system-auth`; edits there (for example a debugging\n`auth sufficient pam_permit.so` line, or a `pam_succeed_if` clause that\nbypasses checks for a group) silently affect `lockme` as well, and\n`lockme` cannot defend against this.\n\nFor PAM debugging, run `./lockme --log-level debug --blank --dev-mode` from\na terminal. Debug logging records PAM status codes and messages only; it\ndoes not log password contents, password length, or prompts. In `--dev-mode`,\n`Esc` exits without asking PAM, which keeps manual auth tests recoverable.\n\nTo revert to the default minimal chain at any time:\n\n```sh\nnimble installPam\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreenm01%2Flockme","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgreenm01%2Flockme","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreenm01%2Flockme/lists"}