A modern, fluid Material 3 frontend for greetd, crafted with Quickshell and Qt6 to seamlessly match Caelestia's shell.
Airlock incorporates assets and components from Caelestia Shell:
- M3Shapes QML module — Material 3 shape definitions
- LoadingIndicator component — Morphing loader animation
- Clock design — Lockscreen clock styling
- FetchCard widget — System information display
- Google Sans Flex font — Typography asset
Licensed under GPL v3. See LICENSE for details.
Runtime:
- greetd
- Quickshell >= 0.3.0
- Qt 6.6+ (Core, DBus, Qml, Quick, Quick3D)
- M3Shapes QML module — Provided by caelestia-shell (or
caelestia-shell-git/dim-caelestia-shell-git) - A wayland compositor — Example configurations are supplied for use with Hyprland and Cage
Build:
- CMake >= 3.19
- Ninja
Optional:
- caelestia-cli (or
caelestia-cli-git/dim-caelestia-cli-git) — Required to use--sync(provides the Caelestia 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-gitIf using a manual install or a fork of the Caelestia shell and or cli, you can create and install a fake package to satisfy the dependencies:
mkdir -p /tmp/dummy-caelestia && cd /tmp/dummy-caelestia
nano PKGBUILDPaste in these contents:
pkgname=dummy-caelestia
pkgver=1.0.0
pkgrel=1
pkgdesc="Dummy metapackage to satisfy caelestia dependencies"
arch=('any')
provides=('caelestia-shell' 'caelestia-cli')
conflicts=('caelestia-shell' 'caelestia-cli')
package() {
true
}
Then install the dummy package:
makepkg -si# Clone and enter directory
git clone https://github.com/AstraSuite/Airlock.git astra-airlock
cd astra-airlock
# Configure and compile C++ QML plugin
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
# Install QML module, configuration, and launcher
sudo cmake --install buildThis installs:
- The
Astra.AirlockQML plugin under the system Qt6 QML module directory (typically/usr/lib/qt6/qml/Astra/Airlock/) - The launcher binary to
/usr/bin/astra-airlock - The shell configuration to
/etc/xdg/quickshell/astra-airlock/
In 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 Quickshell starts; all other
arguments are passed through to Quickshell. 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:
# Build plugin
cmake -B build -G Ninja
cmake --build build
# Launch Quickshell with local build plugin path
QML2_IMPORT_PATH=./build/qml quickshell -p .When run outside greetd, authentication is safely simulated in test mode.