A modern, fluid Material 3 frontend for greetd, written in Rust on Qt6 (via cxx-qt) to seamlessly match Caelestia's shell. Ships as a single self-contained binary with the whole UI baked in — no Quickshell, no QML files on disk.
Airlock also reads KiiteShell's generated Material You palette as an additional "kiite" colour scheme, alongside Caelestia's own schemes and the per-user "dynamic" sync.
Airlock incorporates assets, components, and (now vendored, see native/blobs/NOTICE.md) native code from Caelestia Shell:
- Blobs metaball renderer (
BlobGroup/BlobRect) — vendored C++ source, compiled directly into this binary; caelestia-shell itself is no longer required - LoadingIndicator component — Morphing loader animation
- Clock design — Lockscreen clock styling
- FetchCard widget — System information display
- Google Sans Flex font — Typography asset
The Material 3 shape language (M3Shapes) comes from the standalone
qt6-m3shapes QML
module instead — a dependency extracted from caelestia-shell, not the whole
shell package.
Licensed under GPL v3. See LICENSE for details.
Runtime:
- greetd
- Qt 6.6+ (Core, DBus, Qml, Quick)
- M3Shapes QML module — Provided by qt6-m3shapes-git (AUR); caelestia-shell itself is not a dependency any more
- A wayland compositor — Example configurations are supplied for use with Hyprland and Cage
Build:
- Rust (stable toolchain) and Cargo
- Qt 6.6+ dev packages (
qmake6onPATH, orQMAKEset)
Optional:
- caelestia-cli (or
caelestia-cli-git/dim-caelestia-cli-git) — Required to use--sync(provides the Caelestia scheme) - KiiteShell — if present, its generated palette (
~/.local/state/kiite/user/generated/colors.json) shows up automatically as the"kiite"scheme - wlr-randr — Only necessary if using the monitor flags
Arch Linux (AUR)
Install one of the packages from the AUR using your preferred AUR helper:
- astra-airlock — stable release
- astra-airlock-git — latest development version
# With paru
paru -S astra-airlock
# Or the git version
paru -S astra-airlock-git# Clone and enter directory
git clone https://github.com/AstraSuite/Airlock.git astra-airlock
cd astra-airlock
# Build the single self-contained binary (QML, fonts, and the Rust
# backend are all compiled into it — nothing else needs installing)
cargo build --release
# Install it
sudo install -Dm755 target/release/astra-airlock /usr/bin/astra-airlockIn your flake.nix:
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
astra-airlock.url = "github:AstraSuite/Airlock";
};
outputs = { self, nixpkgs, astra-airlock, ... }: {
nixosConfigurations.myhostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
astra-airlock.nixosModules.default
{
services.greetd.astraAirlock.enable = true;
services.greetd.astraAirlock.compositor = "cage";
}
];
};
};
}Most distributions create the greeter system user when installing greetd. If yours didn't, create it manually:
# Create the greeter user only if your greetd package did not provide one
getent passwd greeter >/dev/null || \
sudo useradd -r -M -s /usr/bin/nologin -d /var/cache/astra-airlock greeter
# Add the user to groups required by the greeter
sudo usermod -aG video,input greeter
# Create the state/cache directory for persistent settings & session memory
sudo install -d -m 0755 -o greeter -g greeter /var/cache/astra-airlockNote
Greeter UI preferences (avatar shape, 12/24h clock, idle animations, color scheme, flavour, dark/light mode, and last-used session per user) are automatically saved to /var/cache/astra-airlock/greeter.json.
For user profile pictures (pfps) to work properly with greetd, the user needs to set their profile picture using the --set-pfp flag:
# Automatically identifies your user from SUDO_USER
sudo astra-airlock --set-pfp /path/to/avatar.pngNote
Because greetd runs under the restricted system greeter user, it cannot access files inside personal home directories (such as ~/.face). Using --set-pfp copies the selected image into the shared avatar store (/var/cache/astra-airlock/avatars/<username>) with the correct read permissions so the greeter can display it.
You can run astra-airlock inside either Cage (lightweight kiosk compositor) or Hyprland.
Caution
--kiosk replaces existing greetd configuration files; make sure you back them up first if you have a customized setup.
The astra-airlock binary provides a -k / --kiosk command to automatically deploy the greetd configurations:
# Deploy Cage configuration to /etc/greetd/config.toml:
sudo astra-airlock -k cage
# Or deploy Hyprland configuration to /etc/greetd/config.toml and /etc/greetd/hyprland.lua:
sudo astra-airlock -k hyprland-k cage/--kiosk cage: Copiesassets/greetd.toml.exampledirectly into/etc/greetd/config.tomlconfigured to launchcage -s -- astra-airlock. Session output is silenced so no tty console screen flashes before the greeter appears.-k hyprland/--kiosk hyprland: Configures/etc/greetd/config.tomlto launchstart-hyprland -- -c /etc/greetd/hyprland.lua(with output silenced) and copies the kiosk configurationassets/hyprland.lua.exampleto/etc/greetd/hyprland.lua.
Note
Both kiosk configurations redirect the compositor's stdout/stderr to /dev/null. Without this, Hyprland (or Cage) prints its startup logs to the tty, leaving a console screen visible before the greeter renders.
Important
This example targets Arch Linux. On other distros, add pam_gnome_keyring.so to your existing greetd PAM configuration rather than replacing the entire file verbatim.
To ensure GNOME Keyring automatically unlocks when logging in through greetd, verify that /etc/pam.d/greetd includes pam_gnome_keyring.so:
# /etc/pam.d/greetd - PAM configuration for greetd
#%PAM-1.0
auth required pam_securetty.so
auth requisite pam_nologin.so
auth include system-local-login
auth optional pam_gnome_keyring.so
account include system-local-login
password include system-local-login
session include system-local-login
session optional pam_gnome_keyring.so auto_start
An example configuration file is provided in assets/pam.d/greetd.example.
Tip
When installing via the AUR package (astra-airlock or astra-airlock-git), /etc/pam.d/greetd is automatically configured with GNOME Keyring auto-unlock entries during post-install.
In /etc/greetd/config.toml:
[terminal]
vt = 1
[default_session]
command = "cage -s -- astra-airlock >/dev/null 2>&1"
user = "greeter"In /etc/greetd/config.toml:
[terminal]
vt = 1
[default_session]
command = "start-hyprland -- -c /etc/greetd/hyprland.lua >/dev/null 2>&1"
user = "greeter"Create /etc/greetd/hyprland.lua (an example is provided in assets/hyprland.lua.example):
-- Default monitor conf
hl.monitor({ output = "", mode = "preferred", position = "auto", scale = 1 })
-- Default options
hl.config({
animations = { enabled = false },
decoration = {
blur = { enabled = false },
shadow = { enabled = false },
},
input = {
kb_layout = "us", -- Change as needed
numlock_by_default = false,
repeat_delay = 250,
repeat_rate = 35,
touchpad = {
natural_scroll = true,
disable_while_typing = true,
scroll_factor = 0.3,
},
},
misc = {
disable_autoreload = true,
disable_hyprland_logo = true,
force_default_wallpaper = 0,
},
})
-- Cursors (ensure cursor theme is installed in /usr/share/icons)
local cursor_theme = "Bibata-Modern-Classic"
local cursor_size = "14"
hl.env("HYPRCURSOR_THEME", cursor_theme)
hl.env("HYPRCURSOR_SIZE", cursor_size)
hl.env("XCURSOR_THEME", cursor_theme)
hl.env("XCURSOR_SIZE", cursor_size)
-- Start the greeter on init
hl.on("hyprland.start", function()
hl.exec_cmd("astra-airlock; hyprctl dispatch exit")
end)When using Hyprland as the compositor, you can customize any aspect of the greeter environment in /etc/greetd/hyprland.lua:
Important
Because greetd runs under the system greeter user, custom cursor themes must be installed system-wide in /usr/share/icons/ (e.g. /usr/share/icons/Bibata-Modern-Classic) with standard read permissions (chmod -R 755).
- Cursor Theme & Size:
Set your cursor theme and size via the unified
cursor_themeandcursor_sizevariables:local cursor_theme = "Bibata-Modern-Classic" local cursor_size = "14" hl.env("HYPRCURSOR_THEME", cursor_theme) hl.env("HYPRCURSOR_SIZE", cursor_size) hl.env("XCURSOR_THEME", cursor_theme) hl.env("XCURSOR_SIZE", cursor_size)
- Keyboard Layout & Input:
Configure keyboard layout, variants, repeat rates, and touchpad behaviors:
input = { kb_layout = "us,de", kb_options = "grp:alt_shift_toggle,caps:escape", touchpad = { natural_scroll = true, tap_to_click = true, }, }
- Monitors & Scaling:
Define explicit monitor modes and scaling factors:
hl.monitor({ output = "DP-1", mode = "2560x1440@144", position = "0x0", scale = 1 }) hl.monitor({ output = "HDMI-A-1", mode = "1920x1080@60", position = "2560x0", scale = 1 })
By default the greeter is shown on every connected output. The launcher
passes monitor options through to wlr-randr, so you can pick which
output(s) the greeter appears on and how they are configured. Pass them
inside the command string of /etc/greetd/config.toml:
# Show only on DP-2 (all other outputs are disabled)
command = "cage -s -- astra-airlock --only DP-2"
# Explicit layout: DP-2 at 2560x1440@120 positioned at 0,0, others off
command = "cage -s -- astra-airlock --output DP-2 --mode 2560x1440@120 --pos 0,0 --output DP-1 --off --output DP-3 --off"Supported options: --only, --output NAME, --on, --off,
--toggle, --mode WxH[@RATE], --custom-mode WxH[@RATE], --preferred,
--pos X,Y, --left-of, --right-of, --above, --below,
--transform, --scale, --adaptive-sync.
The options are applied to the running compositor via the
wlr-output-management protocol before the greeter's own window opens.
Run astra-airlock --help for the full list.
If you already have your monitor layout configured in Hyprland, you can generate the matching flags instead of writing them by hand:
astra-airlock --convert ~/.config/caelestia/hyprland-gui.luaThis reads monitor definitions from a Hyprland config — either plain
monitor = NAME, WxH@RATE, XxY, SCALE lines or Lua hl.monitor({ ... })
blocks (as generated by HyprMod) — and prints the equivalent
astra-airlock flags:
astra-airlock --output DP-1 --custom-mode 1920x1080@280.00Hz --pos 1920,333 --scale 1 --output DP-2 --custom-mode 1920x1080@143.98Hz --pos 0,333 --scale 1Disabled monitors become --off, preferred/auto modes map to
--preferred, and Hyprland transform numbers (0-7) map to wlr-randr
transform names (e.g. 3 → 270). Copy the printed flags into the
command string of /etc/greetd/config.toml as shown above.
The -s / --sync flag copies your active desktop Caelestia scheme into the greeter:
sudo astra-airlock --sync
# or
sudo astra-airlock -sIt grabs the scheme currently applied by caelestia-cli — the active scheme name, flavour, mode, and generated colors — and writes it to /var/cache/astra-airlock/schemes/dynamic/<user>/ as two files: dark.json and light.json.
Once synced, a Dynamic scheme option appears in the greeter's scheme modal for your specific user. Selecting it renders the greeter with the colors of your desktop scheme instead of a built-in one. When multiple users have synced schemes, the greeter automatically picks the matching dynamic scheme and crossfades between them as you switch users on the login screen. Re-run --sync whenever you change your desktop scheme to refresh it.
Note
Because the command is run via sudo, it identifies your account from SUDO_USER and reads your scheme from your real home directory (~/.config/caelestia and ~/.cache/caelestia). Run it with sudo so it targets your user rather than the restricted greeter user.
To let the sync run non-interactively (e.g. from a hook, see below) without prompting for a password, add a sudoers rule allowing only this command:
# Replace <your-username> with your actual user name
sudo tee /etc/sudoers.d/astra-airlock-sync << EOF
<your-username> ALL=(ALL) NOPASSWD: /usr/bin/astra-airlock -s, /usr/bin/astra-airlock --sync
EOF
sudo chmod 440 /etc/sudoers.d/astra-airlock-syncThis lets you run astra-airlock --sync (or -s) with sudo without a password, while every other invocation of astra-airlock still requires authentication. Verify it with the non-interactive form:
sudo -n astra-airlock --syncUsing sudo -n makes the command fail immediately instead of hanging when no rule is in place — exactly what a background hook wants.
caelestia-cli runs a configurable postHook after applying a theme (theme.postHook) and after setting a wallpaper (wallpaper.postHook). Point both at the sync command in ~/.config/caelestia/cli.json:
{
"theme": {
"postHook": "sudo -n astra-airlock --sync"
},
"wallpaper": {
"postHook": "sudo -n astra-airlock --sync"
}
}Now whenever you change your Caelestia scheme or your wallpaper, the greeter's Dynamic scheme for your user is refreshed automatically — no need to run the command manually.
Tip
These hooks execute in your user's environment, so the sudoers rule above is required for sudo -n to succeed without a password prompt.
# Replace "sddm.service" with your currently enabled display manager (e.g., sddm/gdm/lightdm)
sudo systemctl disable sddm.service
# Enable and start greetd
sudo systemctl enable --now greetd.serviceYou can test and iterate on the greeter locally without logging out:
cargo runWhen run outside greetd (no $GREETD_SOCK), authentication is safely simulated in test mode. Because the QML tree is baked into the binary at build time, re-run cargo run after editing any .qml file to see the change.