WayExpand is a text expander built for Wayland. Type ;sig and get your
signature. Type ;ty and get your support reply — with the cursor waiting
where you need to keep typing. Dates, addresses, templates, team snippets:
local, offline, no account, no telemetry.
-
See it work before you set anything up. The Matcher preview panel expands your saved snippets as you type with the daemon's own matching engine — no daemon, permission, or desktop integration needed — and counts the keystrokes you saved. It does not exercise desktop capture or insertion;
wayexpand doctorcovers that. -
Quick-insert picker. Bind
wayexpand-gui --pickerto a keyboard shortcut. Type a few letters, press Enter, and the running WayExpand service types the snippet into the app you were using. Typing needs focused-window tracking (currently KDE Plasma); elsewhere, or without the service, Enter copies it to the clipboard. No trigger to remember. -
Whole config files, filled in.
wayexpand new playbookasks for the host group and package and writes a ready Ansible playbook. Dockerfiles, Compose, Kubernetes, systemd units, nginx, and CI workflows are built in, and your own templates work the same way. -
One click to turn on. The first-run screen detects your desktop and enables the safest input path it finds. It never grants raw keyboard access on its own.
-
Safe by design. Matching pauses in password fields when the desktop reports them, app-specific snippets fail closed, command snippets run without a shell under timeouts and output limits, and errors never echo your snippet text.
-
Brings your library along. Import Espanso YAML, edit in the GUI, the terminal UI, or plain TOML, and roll out team snippets with fleet policy.
| Area | Current status |
|---|---|
| Configuration, CLI, and JSON contracts | Stable and regression-tested |
| Linux architectures | x86_64 and aarch64 build paths |
| Desktop integration | Wayland backends available; compositor support varies |
| Production certification | No compositor has accepted physical-session certification; see the certification matrix |
| Distribution packages | Ubuntu 26.04 amd64 daily PPA package; no published GitHub release and no published Fedora Copr repository; certification pending |
The currently published package channel is an experimental daily PPA build for Ubuntu 26.04 amd64. No GitHub release is currently published, and no Fedora Copr repository is published. Package recipes and CI install tests exist for Ubuntu 24.04/26.04 and Fedora 45, but that does not mean installable release packages are available for those distributions. The Ubuntu daily package is not compositor-certified.
sudo add-apt-repository ppa:cyberducttape/ppa
sudo apt update
sudo apt install wayexpand
# Arch packaging preview: see docs/PACKAGING.md#arch-linux-packaging-previewThe PPA currently publishes current daily builds for Ubuntu 26.04 (Resolute) amd64. Ubuntu 24.04's PPA package is stale. Read the packaging status before choosing an installation method; do not assume CI artifacts are published packages.
Launch WayExpand from the desktop application menu or run wayexpand-gui.
Its first-run guide checks the installation and route, then asks you to verify
an exact insertion in a real application before it records setup as working.
wayexpand setup --check reports missing integration dependencies without
enabling raw keyboard capture. If automatic expansion cannot be verified yet,
the guide offers a copy-only Quick Picker for manual paste.
Ubuntu 26.04 (Resolute) users can also install the experimental daily package from the WayExpand PPA:
sudo add-apt-repository ppa:cyberducttape/ppa
sudo apt update
sudo apt install wayexpandThe PPA is currently limited to Ubuntu 26.04 amd64 and is not compositor-certified. Check the PPA page and packaging status before installing.
The release archive and source installer are per-user developer paths. They stage the IBus component but may require administrator authorization to register the system-wide IBus component. On GNOME, setup can also ask you to log out and back in before resuming. Prefer a distribution package for an ordinary desktop installation when one is available.
To install a release archive without building from source:
sha256sum --check wayexpand-*-linux-x86_64.tar.gz.sha256
tar -xzf wayexpand-*-linux-x86_64.tar.gz
./wayexpand-*-linux-x86_64/scripts/install-release.shTo build and install from source:
Rust 1.92 or newer:
git clone https://github.com/cyberducttape/wayexpand
cd wayexpand
./scripts/install-user.sh
wayexpand-guiThe installers never run as root, enable services, grant raw-input access, or accept portal consent for you. Packaging covers distro packages and the Ubuntu PPA's status.
- Turn on WayExpand — one click in the GUI, or
wayexpand setupin a terminal. Setup configures the supported input route without requiring manual IBus commands. - Verify this desktop session — setup guides you through a harmless real application insertion and ends as Working, Configured awaiting test, or Action required. The GUI's Open diagnostics shows route capabilities and certification separately.
- Add a snippet — Create test snippet, write your own, or import Espanso.
- Try it — type the trigger in Matcher preview, then in a normal text field in an app you use.
Set up the picker by adding a desktop shortcut (for example Super +
.) that runs wayexpand-gui --picker. Scripts can do the same with
wayexpand insert ';sig'.
Useful commands:
wayexpand doctor # what this desktop supports, and what is missing
wayexpand status # is the daemon running, and on which path
wayexpand upgrade-status # is the running daemon from this CLI build?
wayexpand test ';sig' # check matching without the desktopFor a sysadmin first task, see the HTTP 503 incident triage walkthrough. It generates a validated, review-first checklist for an explicitly selected local or remote shell session; diagnostic commands are never run by WayExpand.
Any snippet with an output_file is a file template. wayexpand new lists
them, asks for each {{field:…}} (in the terminal, or a form with --gui),
and writes the file without overwriting one that exists:
$ wayexpand new compose --set service=web
Wrote compose.yaml
$ cat compose.yaml
services:
web:
image: nginx:stable
restart: unless-stopped
ports:
- "8080:80"
...Built in: dockerfile (plus -python, -node, -rust), compose,
playbook, ansible-role, inventory, systemd-service,
github-actions, nginx-site, and k8s-deployment. Start your own from
wayexpand new --show playbook. Jinja and GitHub {{ … }} expressions are
written as {{{{ … }}}}. See Templates.
- Committed text only. WayExpand does not see IME preedit/composition; finish CJK, dead-key, and Compose sequences before expecting a trigger to match.
- App-specific snippets need focused-window tracking. It is implemented for KDE Plasma (KWin) and GNOME through the WayExpand Shell extension; GNOME tracking has headless integration coverage but is not yet production- certified in a real desktop session. Other desktops fail closed when a trustworthy tracker is unavailable.
- No compositor has accepted physical-session production certification.
Headless CI and protocol tests do not establish physical-keyboard or
real-session behavior. Run
wayexpand doctorfor local readiness, and consult the support and certification matrices before relying on a backend. - evdev is a last resort. It observes every keyboard event, including
password fields, so setup never grants keyboard-device access and
keystroke routes keep typed expansions off until you allow them with
compatibility_mode. Recommended mode configures the highest-ranked route that is available and allowed by policy: certified routes first, then input-method-v2 (when libei key pass-through is available), then IBus; it never automatically selects evdev or another globally observing path. See Insertion guarantees.
The support matrix and certification matrix list evidence and known limits; deployment teams should also use the production acceptance checklist.
WayExpand runs as the unprivileged desktop user, works offline, has no telemetry, and owner-checks its configuration files. Command snippets run without a shell, with bounded queues, output limits, timeouts, and process group cleanup; organization policy can audit or enforce restrictions. These controls make everyday operation predictable — WayExpand does not claim to be a security sandbox. See SECURITY.md and the threat model.
wayexpand import espanso ~/.config/espanso > imported.tomlTo add imported expansions directly to the active library, use --add:
wayexpand import autokey ~/.config/autokey/data/HotKeys --add --report-json--add merges with the existing library, keeps existing trigger conflicts,
tests every added trigger and alias through the matcher and template renderer,
then saves atomically with a backup. Forms are checked for trigger recognition
but need user values; command-backed expansions are matched but never
executed; application filters are not tested without a focused window. A
failed validation or smoke test leaves the active library unchanged. Use
--config PATH with --add to target a different library.
--strict rejects entries whose Espanso semantics cannot be preserved, and
--report-json writes a machine-readable migration report to stderr. Directory
imports include nested match files. Supported dynamic variables and basic
forms are converted; shell/script and clipboard execution or access are not
enabled implicitly. The GUI's import merges by default and previews conflicts
before anything changes. See Migrating from Espanso.
Static AutoKey phrases can be imported from their .txt and JSON sidecar
tree. Scripts, hotkeys, filters, and dynamic macros are reported as
unsupported and never run during import. See
Migrating from AutoKey.
Keep separate personal, work, or customer libraries and switch between them:
wayexpand profile create personal
wayexpand profile create work
wayexpand profile switch personal
wayexpand profile listEach profile stores a validated configuration snapshot beside the active
configuration. Switching checkpoints edits to the outgoing profile, validates
the selected configuration with shared snippets.d layers, replaces the
active config atomically, and asks the daemon to reload. Assign exact desktop
application IDs with wayexpand profile assign-app <app-id> <profile>;
matching pauses while a newly focused app's library loads. See the
profile guide and Operations.
- Getting started · Troubleshooting
- Templates · GUI
- Backends · Support matrix
- Fleet deployment · For sysadmins
- Safety-aware workflows
- Packaging · Roadmap
- Everything else
Non-English documentation: Deutsch.
See CONTRIBUTING.md for the development setup, PR checklist, and project structure. CI runs formatting, tests, Clippy, shellcheck, installer checks, systemd validation, packaging checks, and dependency advisories.
MIT. WayExpand is built and maintained by CyberDuctTape.


