Skip to content
Back to skills

Screen Lock

ASecurity

Password-lock the machine's screen while every process keeps running, and read whether it is locked

  • 287 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 2, 2026
ai-agentspythonbashapisecurity

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned September 20, 2026

npx -y skills add AIOSAI/AIPass --skill screen_lock --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Screen Lock?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Screen Lock
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/aiosai-screen-lock/badge)](https://www.skillsdirectory.com/skills/aiosai-screen-lock)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: screen_lock
description: Password-lock the machine's screen while every process keeps running, and read whether it is locked
version: 1.1.0
tags: [system, control, security]
when_to_use:
  - "lock the screen"
  - "lock the machine"
  - "walking away from the desk, keep the agents running"
  - "did I lock the laptop"
requires:
  pip: []
  bins: [loginctl]
  config: []
has_handler: true
---

# Screen Lock

## What This Does

Password-locks the graphical session and leaves everything running — agents keep
working behind the password wall. Nothing sleeps, nothing is killed, no root is
needed (no sudoers grant, no polkit rule).

Two paths, tried in order:

1. `loginctl lock-session <id>` against the **explicitly resolved** graphical
   session. Callers usually run as a `systemd --user` service with no
   `XDG_SESSION_ID`, so a bare `lock-session` has no ambient session to resolve
   and may refuse — naming the session is what makes the verb work from there.
2. `gdbus` → `org.gnome.ScreenSaver.Lock` as the fallback.

If both fail it says so. A screen that never locked is never reported as locked.

## Doctrine — lock is the exception

> A destructive action never fires from a locked screen. **Lock itself must work
> from anywhere — that is its whole point.**

So this verb is never gated: it does not ask whether the screen is already
locked, does not require a desktop environment, and does not refuse when no
graphical session can be resolved (it still tries the bare call). Callers that
gate *other* verbs on screen state must leave this one ungated.

## The read beside the lock

`lock_state()` answers whether the screen **is** locked — the question "did I
lock the laptop?" asked from away (FPLAN-0585). It reads through the same pair
the lock writes through, in the same order, and changes nothing:

1. `loginctl show-session <id> -p LockedHint` on the resolved graphical session
   — the logind mechanism `lock-session` sets, so read and write agree.
2. `gdbus` → `org.gnome.ScreenSaver.GetActive` when logind cannot answer.
   (`org.freedesktop.ScreenSaver.GetActive` is not implemented on GNOME and is
   never asked.)

It is not gated either — the lock never is, so its read is not — and it never
raises. **Unknown is never unlocked:** a read that cannot tell answers
`ok: False, locked: None` with a reason code, never `locked: False`.

| `reason`      | Meaning                                                          |
|---------------|------------------------------------------------------------------|
| `no_session`  | logind lists no active graphical session of ours, and D-Bus did not answer |
| `no_reader`   | neither `loginctl` nor `gdbus` is installed                      |
| `read_failed` | a reader ran and failed, or answered something other than yes/no |

## Available Actions

| Action  | Description                                        |
|---------|----------------------------------------------------|
| `lock`  | Lock the screen now; agents keep running           |
| `state` | Read whether the screen is locked; locks nothing   |

## Usage

```bash
drone @skills run screen_lock lock
drone @skills run screen_lock state
```

In-process (what the Telegram control bot and the host API verb lane use):

```python
from aipass.skills.lib.screen_lock import handler as screen_lock

result = screen_lock.lock_screen()
# {"locked": True, "method": "loginctl", "session": "3", "error": None}

state = screen_lock.lock_state()
# {"ok": True, "locked": False, "method": "loginctl", "session": "3",
#  "reason": None, "detail": "The screen is unlocked, per logind session 3."}
```

## When to Use

Use this skill when:
- You are leaving the machine and want the screen locked with work continuing
- A remote surface (phone, host API, chat bot) needs to secure the desk

Do NOT use this skill when:
- You want the machine to sleep — that is `/suspend`, a different verb with a
  wake, grace-window and reachability story. Lock never suspends.
- You want to end sessions — nothing is stopped or killed here.

## Output Format

`run()` returns the standard skill envelope:

```python
{"success": True, "output": "Screen locked via loginctl (session=3)", "error": None}
```

`lock_screen()` returns structured detail for programmatic callers:

```python
{"locked": bool, "method": "loginctl" | "dbus" | None, "session": str | None, "error": str | None}
```

`lock_state()` returns the read, six keys every time:

```python
{
    "ok": bool,                                   # True when a reader answered
    "locked": bool | None,                        # None whenever ok is False
    "method": "loginctl" | "dbus" | None,
    "session": str | None,                        # the resolved logind session
    "reason": "no_session" | "no_reader" | "read_failed" | None,
    "detail": str,                                # the skill's own sentence
}
```

`run("state")` puts `detail` in `output` when the read answered, and fails the
envelope with `detail (reason=<code>)` when it cannot tell.

## Notes

- Stdlib + the Prax logger only — importing this skill pulls in no Telegram
  stack, no bot, no network client. A test asserts that in a clean interpreter.
- `resolve_graphical_session()` is public: callers can ask which session is ours
  without locking anything.
- `loginctl` is the declared requirement; `gdbus` is the fallback and is not
  required for the skill to be usable.
- Only sessions of type `wayland`/`x11`, state `active`, owned by our own uid are
  eligible — another user's desktop is never locked.
- Extracted from the Telegram control bot in DPLAN-0300; that bot is the first
  consumer, not the owner.

Files in this skill

  • SKILL.md3.4 KB
  • handler.py6.5 KB
  • tests/conftest.py815 B
  • tests/test_screen_lock.py14.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.