Configuration

Overview

Stasis uses a .rune configuration file. It first looks for $XDG_CONFIG_HOME/stasis/stasis.rune (typically ~/.config/stasis/stasis.rune). For system-wide configuration, copy the example to /etc/stasis/stasis.rune.

On first run, Stasis generates a default configuration at ~/.config/stasis/stasis.rune.

The master reference config is located at /usr/share/doc/stasis/stasis.rune.

Globals (Variables)

The top level is intended for reusable values. Define timers and strings once, then reference them inside default: and profiles.

@author "Your Name"
@description "Stasis configuration"

# Top-level is intended for reusable values.
lock_after        300
screen_off_after  300
suspend_after     1800
debounce_seconds  4
notify_seconds    5

Default Block

Your config must contain a default: block. Most settings live under this block. Action blocks defined directly under default: form the desktop plan. On laptops, the ac: and battery: sub-blocks are used instead (see AC & Battery).

default:
  enable_loginctl false
  enable_dbus_inhibit true
  pre_suspend_command None
  monitor_media true
  ignore_remote_media true

  # Optional: ignore these media sources (case-insensitive)
  #media_blacklist ["spotify"]

  debounce_seconds debounce_seconds

  # Lid actions (laptop only — live here so they apply to both ac: and battery:)
  lid_close_action "hyprlock"
  #lid_open_action ""

  notify_on_unpause true

  # Global gate: per-step notifications only fire if this is true
  notify_before_action true

  inhibit_apps [
    r"steam_app_.*"
  ]

  startup:
    timeout 0
    command "notify-send 'Stasis idle manager started!'"
  end

  lock_screen:
    timeout lock_after
    command "hyprlock"
    resume_command "notify-send 'Welcome back $env.USER!'"
    notification "Locking soon"
    notify_seconds_before notify_seconds
  end

  dpms:
    timeout screen_off_after
    command "hyprctl dispatch dpms off"
    resume_command "hyprctl dispatch dpms on"
  end

  suspend:
    timeout suspend_after
    command "systemctl suspend"
  end
end

Startup Action

The startup: block runs once when Stasis starts (always first):

startup:
  timeout 0
  command "notify-send 'Stasis idle manager started!'"
end

loginctl Integration

Set enable_loginctl true under default: to register Stasis with login1 for global lock/unlock state tracking. This is a top-level global setting — there is no per-block use_loginctl option.

# Enable global login1 tracking (lock/unlock state)
enable_loginctl true

Session D-Bus Inhibit Integration

Keep session-bus inhibit handling enabled so desktop/session requests (including portal inhibits) are honored correctly:

# Enable session-bus inhibit tracking (recommended)
enable_dbus_inhibit true
Session context: Start your compositor in a real session context (for example: niri-session, dbus-run-session, or your compositor's recommended launcher) so session D-Bus features are available.

Pre-Suspend Command

Optionally run a command immediately before suspending — useful to ensure the screen is locked before the system sleeps:

# Run before suspending (e.g. ensure screen is locked first)
# pre_suspend_command is BLOCKING — use your locker's fork flag if it has one,
# or wrap it with the daemonize utility.
pre_suspend_command "swaylock -f"       # swaylock has a built-in fork flag
#pre_suspend_command "daemonize hyprlock"  # hyprlock doesn't, use daemonize

# Disable:
pre_suspend_command None
Important: pre_suspend_command is blocking — Stasis waits for it to exit before suspending, so if you pass a locker directly it will wait until you unlock before ever suspending. Use your locker's fork flag if it has one (e.g. swaylock -f), or wrap it with the daemonize utility. If your plan already has a lock_screen: step, you generally don't need pre_suspend_command at all.
# pre_suspend_command is BLOCKING — Stasis waits for it to finish before suspending.
# Use your locker's fork flag if it has one, or wrap with daemonize:

pre_suspend_command "swaylock -f"         # swaylock: built-in fork flag
#pre_suspend_command "daemonize hyprlock" # hyprlock: no fork flag, use daemonize

suspend:
  timeout 1800
  command "systemctl suspend"
end

# If you already lock as a plan step, prefer this instead:
#pre_suspend_command None

Media Monitoring

Media Playback

Control whether Stasis monitors media playback to prevent idle actions:

monitor_media true
ignore_remote_media true
  • monitor_media — active playback prevents idle timeouts
  • ignore_remote_media — ignores remote sources (Spotify remote, KDEConnect, etc.)

Media Blacklist

Ignore specific media sources (case-insensitive):

media_blacklist ["spotify", "rhythmbox"]

Inhibitors

Application Inhibitors

Specify apps/processes that prevent idle actions while running (strings or regex literals):

inhibit_apps [
  "vlc"
  "Spotify"
  "mpv"
  r".*\.exe"
  r"steam_app_.*"
  r"firefox.*"
]
Regex Pattern Guidelines:
  • Use raw string syntax: r"pattern"
  • Escape special characters: \. for literal dots
  • Use .* for wildcards
  • Use ^ and $ for anchors

Notifications

On Unpause

Notify when resuming from an IPC pause (e.g., stasis pause 1h):

notify_on_unpause true

Pre-Action

To show notifications before actions run:

  • Enable the gate: notify_before_action true
  • Optionally set a default: notify_seconds_before
  • Set notification "message" on the action block
  • Optionally override timing per-block with notify_seconds_before
notify_before_action true
notify_seconds_before 5

Laptop Settings

Lid Actions

Configure what happens when the laptop lid closes or opens. These live directly under default: so they automatically apply to both ac: and battery: plans without duplication. Lid close/open also pause/resume the plan timers regardless. A profile can override or clear them.

# Lives under default: so it applies to both ac: and battery: plans.
# A profile can override or clear these.
lid_close_action "hyprlock"
lid_open_action  "brightnessctl set 60%"

# Clear an action entirely:
lid_open_action ""

Debounce

Prevent rapid lid open/close events from triggering multiple actions:

debounce_seconds 4

Idle Actions

Each action block supports:

  • timeout — seconds of idle time before triggering (required); 0 runs immediately on plan activation
  • command — command to run (required; use None to disable)
  • resume_command / resume-command — optional, run when activity resumes
  • notification — optional message shown before the action runs
  • notify_seconds_before — optional per-block timing override
Action names:
  • startup
  • lock_screen / lock-screen
  • early_dpms — useful for a shorter initial screen-off before the main lock
  • dpms
  • brightness
  • suspend
  • * — custom actions can be named anything

AC & Battery

Laptop plans live as sub-blocks inside default: (or inside a profile). When running on a laptop chassis, Stasis uses ac: or battery: depending on current power state — the desktop plan blocks are ignored. Globals and lid actions defined directly under default: still apply to both plans.

AC Plan

default:
  # ...globals, lid actions, desktop plan...

  ac:
    # timeout 0 runs immediately when AC becomes active
    custom_brightness_instant:
      timeout 0
      command "brightnessctl set 100%"
    end

    brightness:
      timeout 120
      command "brightnessctl set 30%"
    end

    dpms:
      timeout 60
      command "hyprctl dispatch dpms off"
      resume_command "hyprctl dispatch dpms on"
    end

    lock_screen:
      timeout 120
      command "hyprlock"
      notification "Locking on AC in 10s"
      notify_seconds_before 10
    end

    suspend:
      timeout 300
      command "systemctl suspend"
    end
  end
end

Battery Plan

default:
  # ...globals, lid actions, desktop plan...

  battery:
    custom_brightness_instant:
      timeout 0
      command "brightnessctl set 40%"
    end

    brightness:
      timeout 60
      command "brightnessctl set 20%"
    end

    dpms:
      timeout 30
      command "hyprctl dispatch dpms off"
      resume_command "hyprctl dispatch dpms on"
    end

    lock_screen:
      timeout 120
      command "hyprlock"
      resume_command "notify-send 'Welcome back $env.USER!'"
    end

    suspend:
      timeout 120
      command "systemctl suspend"
    end
  end
end
💡 Tips:
  • Put timeout 0 actions first — they run immediately when the plan activates (e.g., restore brightness on plug-in).
  • Profiles can also define their own ac: / battery: sub-blocks to override the laptop plan per-profile.

Full Example

Complete example covering desktop plan, laptop AC/battery plans, and all three profile modes (overlay and fresh):

# This file was auto-generated by Stasis on first run
# Feel free to customize it to your needs
# Master config reference: /usr/share/doc/stasis/stasis.rune

@author "Dustin Pilgrim"
@description "Lightweight feature packed idle manager for Wayland"

# Top-level: reusable values (recommended)
lock_after        300
screen_off_after  300
suspend_after     1800
debounce_seconds  4
notify_seconds    5

default:
  enable_loginctl false
  enable_dbus_inhibit true
  pre_suspend_command None
  monitor_media true
  ignore_remote_media true  # ignore remote players (spotify/kdeconnect/etc.)

  # Optional: ignore these media sources for media inhibit (case-insensitive)
  #media_blacklist ["spotify"]

  debounce_seconds debounce_seconds

  # Lid actions (laptop only — live here so they apply to both ac: and battery:)
  lid_close_action "hyprlock"
  #lid_open_action ""

  # Notify when resuming from IPC pause (e.g. `stasis pause 1h`)
  notify_on_unpause true

  # Enables per-step notifications (only if the block sets `notification`)
  notify_before_action true

  inhibit_apps [
    "mpv"
    "vlc"
    "Spotify"
    r".*\.exe"
    r"steam_app_.*"
    r"firefox.*"
  ]

  # ----------------------------------------------------------------
  # DESKTOP PLAN (used only on desktop chassis)
  # ----------------------------------------------------------------
  startup:
    timeout 0
    command "notify-send -a Stasis 'Stasis started!'"
  end

  early_dpms:
    timeout 300
    command "hyprctl dispatch dpms off"
    resume_command "hyprctl dispatch dpms on"
  end

  lock_screen:
    timeout lock_after
    command "hyprlock"
    resume_command "notify-send 'Welcome back $env.USER!'"
    notification "Locking session in 10s"
    notify_seconds_before 10
  end

  dpms:
    timeout screen_off_after
    command "hyprctl dispatch dpms off"
    resume_command "hyprctl dispatch dpms on"
  end

  suspend:
    timeout suspend_after
    command "systemctl suspend"
  end

  # ----------------------------------------------------------------
  # LAPTOP PLANS (used only on laptop chassis — ac: or battery:)
  # Desktop plan blocks above are ignored for laptops.
  # ----------------------------------------------------------------
  ac:
    # timeout 0 runs immediately when AC becomes active
    custom_brightness_instant:
      timeout 0
      command "brightnessctl set 100%"
    end

    brightness:
      timeout 120
      command "brightnessctl set 30%"
    end

    dpms:
      timeout 60
      command "hyprctl dispatch dpms off"
      resume_command "hyprctl dispatch dpms on"
    end

    lock_screen:
      timeout 120
      command "hyprlock"
      notification "Locking on AC in 10s"
      notify_seconds_before 10
    end

    suspend:
      timeout 300
      command "systemctl suspend"
    end
  end

  battery:
    custom_brightness_instant:
      timeout 0
      command "brightnessctl set 40%"
    end

    brightness:
      timeout 60
      command "brightnessctl set 20%"
    end

    dpms:
      timeout 30
      command "hyprctl dispatch dpms off"
      resume_command "hyprctl dispatch dpms on"
    end

    lock_screen:
      timeout 120
      command "hyprlock"
      resume_command "notify-send 'Welcome back $env.USER!'"
    end

    suspend:
      timeout 120
      command "systemctl suspend"
    end
  end
end

# --------------------------------------------------------------------
# PROFILES
#
# mode "overlay": merges on top of the active base (default/ac/battery)
# mode "fresh":   starts from nothing — define every global and action block you want
# --------------------------------------------------------------------

# gaming: overlay — only replace inhibit_apps
gaming:
  mode "overlay"

  inhibit_apps [
    r".*\.exe"
    r"steam_app_.*"
    r".*\.x86_64"
  ]
end

# work: overlay — longer timeouts, loginctl enabled
work:
  mode "overlay"

  enable_loginctl true
  debounce_seconds 10
  monitor_media true
  ignore_remote_media true

  lock_screen:
    timeout 600
    command "hyprlock"
    resume_command "notify-send 'Welcome back, $env.USER!'"
  end

  dpms:
    timeout 300
    command "hyprctl dispatch dpms off"
    resume_command "hyprctl dispatch dpms on"
  end

  suspend:
    timeout 3600
    command "systemctl suspend"
  end
end

# presentation: fresh — keep display on, suppress all idle actions
presentation:
  mode "fresh"

  pre_suspend_command None
  monitor_media false
  ignore_remote_media true
  debounce_seconds 0
  notify_on_unpause false
  notify_before_action false
  inhibit_apps [ ]

  lid_close_action ""
  lid_open_action ""

  brightness:
    timeout 0
    command "brightnessctl set 100%"
  end
end

# Profile switching (runtime IPC):
#   stasis profile gaming
#   stasis profile work
#   stasis profile presentation
#   stasis profile default   (or: stasis profile none)
Advanced

Profiles

Profiles are any top-level named blocks other than default. They override settings and action blocks from the active base plan, and can define their own ac:/battery: plans.

mode:
  • "overlay" (default) — merges on top of the active base; only what you specify is overridden
  • "fresh" — starts from nothing; you must define every global and action block you want
# Any top-level named block besides `default:` is a profile.
# Profiles override settings/steps from the active base and can
# have their own `ac:` / `battery:` plans.

gaming:
  mode "overlay"  # merges on top of active base

  # Replace the inherited inhibit_apps list:
  inhibit_apps [
    r".*\.exe"
    r"steam_app_.*"
    r".*\.x86_64"
  ]
end

work:
  mode "overlay"

  enable_loginctl true
  debounce_seconds 10
  monitor_media true
  ignore_remote_media true

  lock_screen:
    timeout 600
    command "hyprlock"
    resume_command "notify-send 'Welcome back, $env.USER!'"
  end

  dpms:
    timeout 300
    command "hyprctl dispatch dpms off"
    resume_command "hyprctl dispatch dpms on"
  end

  suspend:
    timeout 3600
    command "systemctl suspend"
  end
end

presentation:
  mode "fresh"  # starts from NOTHING — define every global and step you want

  pre_suspend_command None
  monitor_media false
  ignore_remote_media true
  debounce_seconds 0
  notify_on_unpause false
  notify_before_action false
  inhibit_apps [ ]

  # Clear lid actions so they don't fire during a presentation
  lid_close_action ""
  lid_open_action ""

  # Keep display on; no lock/dpms/suspend unless you add them
  brightness:
    timeout 0
    command "brightnessctl set 100%"
  end
end
Profile selection:

Profiles are switched at runtime via IPC:

# Switch profiles at runtime via IPC:
stasis profile gaming
stasis profile work
stasis profile presentation

# Return to default (no profile):
stasis profile default
# (alias)
stasis profile none