Skip to content

Support headless auto-approve, removing the hard requirement of a D-Bus service #484

Description

@jpike88

I run passless headless, and want it to always auto-approve a passkey request when it's asked for one.

I see there is an experimental agent mode, but it seems really complex and overkill for a secure headless instance.

In the interim, I wrote a script that essentially sits in memory as automatically approved notifications as needed. Then I wrap it in dbus-run-session... it works reliably.

Thoughts?

#!/usr/bin/env python3
import signal
import sys

import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GLib

# 1. Initialize the GLib event loop for D-Bus
DBusGMainLoop(set_as_default=True)

bus = dbus.SessionBus()
name = dbus.service.BusName("org.freedesktop.Notifications", bus)


class NotificationServer(dbus.service.Object):
    def __init__(self):
        super().__init__(bus, '/org/freedesktop/Notifications')
        self.counter = 1

    @dbus.service.signal('org.freedesktop.Notifications', signature='us')
    def ActionInvoked(self, id, action_key):
        pass

    @dbus.service.signal('org.freedesktop.Notifications', signature='uu')
    def NotificationClosed(self, id, reason):
        pass

    @dbus.service.method('org.freedesktop.Notifications', in_signature='susssasa{sv}i', out_signature='u')
    def Notify(self, app_name, replaces_id, app_icon, summary, body, actions, hints, timeout):
        nid = self.counter
        self.counter += 1
        print(f"[DummyNotification] Received: {summary} (ID: {nid}, Actions: {actions})", flush=True)

        # Determine the action to invoke (usually first action key or 'default')
        action_to_invoke = "default"
        if actions and len(actions) >= 2:
            action_to_invoke = actions[0]

        print(f"[DummyNotification] Auto-approving with action '{action_to_invoke}'", flush=True)

        # Fire signal on next event loop tick
        def trigger_approval():
            self.ActionInvoked(nid, action_to_invoke)
            return False

        GLib.timeout_add(50, trigger_approval)
        return nid

    @dbus.service.method('org.freedesktop.Notifications', out_signature='ssss')
    def GetServerInformation(self):
        return ("HeadlessNotify", "Passless", "1.0", "1.2")

    @dbus.service.method('org.freedesktop.Notifications', out_signature='as')
    def GetCapabilities(self):
        return ["actions", "body"]


server = NotificationServer()
print("Dummy Notification server listening...", flush=True)

# 2. Blocking event loop to keep the service running
loop = GLib.MainLoop()

def handle_sigterm(signum, frame):
    loop.quit()

signal.signal(signal.SIGTERM, handle_sigterm)
signal.signal(signal.SIGINT, handle_sigterm)

try:
    loop.run()
except KeyboardInterrupt:
    pass

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions