diff --git a/helper/sk.houmeres.synologynas.policy b/helper/sk.houmeres.synologynas.policy
new file mode 100644
index 0000000..fe612c8
--- /dev/null
+++ b/helper/sk.houmeres.synologynas.policy
@@ -0,0 +1,20 @@
+
+
+
+ Synology NAS plasmoid
+ https://git.houmeres.sk/Houmeres/Plasma-Addon-Synology-NAS
+ folder-network
+
+
+ Mount or unmount a network share
+ Authentication is required to mount or unmount a network share
+
+ no
+ no
+ auth_admin_keep
+
+ /usr/lib/synology-nas/helper
+ true
+
+
diff --git a/helper/synology-nas-helper b/helper/synology-nas-helper
new file mode 100755
index 0000000..53a0d64
--- /dev/null
+++ b/helper/synology-nas-helper
@@ -0,0 +1,118 @@
+#!/bin/sh
+#
+# synology-nas-helper — privileged mount/unmount helper for the Synology NAS
+# plasmoid. It is invoked as root via pkexec (see the accompanying
+# sk.houmeres.synologynas.policy). The CALLER IS UNPRIVILEGED, so every argument
+# is treated as hostile and strictly validated before any mount happens.
+#
+# Usage (all arguments positional):
+# synology-nas-helper mount
+# synology-nas-helper unmount
+#
+# Guarantees enforced below:
+# * mountpoint must resolve to a path under the CALLING user's $HOME or
+# /run/media/ (rejects .. traversal via realpath -m).
+# * source must look like //host/share.
+# * credfile must live under /run/user//, be a regular file,
+# owned by the caller, mode 0600.
+# * vers, permission modes and extra options are charset/whitelist checked.
+#
+set -eu
+
+die() { echo "synology-nas-helper: $1" >&2; exit 1; }
+
+# --- identify the calling (unprivileged) user ---------------------------------
+[ -n "${PKEXEC_UID:-}" ] || die "must be run through pkexec (PKEXEC_UID unset)"
+case "$PKEXEC_UID" in
+ ''|*[!0-9]*) die "invalid PKEXEC_UID" ;;
+esac
+
+pwline=$(getent passwd "$PKEXEC_UID") || die "unknown caller uid $PKEXEC_UID"
+CALLER_USER=$(printf '%s\n' "$pwline" | cut -d: -f1)
+CALLER_GID=$(printf '%s\n' "$pwline" | cut -d: -f4)
+CALLER_HOME=$(printf '%s\n' "$pwline" | cut -d: -f6)
+[ -n "$CALLER_HOME" ] || die "caller has no home directory"
+
+# --- shared validators --------------------------------------------------------
+validate_mountpoint() {
+ # Canonicalise (resolves .. lexically, no need for the path to exist yet).
+ canon=$(realpath -m -- "$1") || die "cannot canonicalise mountpoint"
+ case "$canon" in
+ "$CALLER_HOME"/*) : ;;
+ "/run/media/$CALLER_USER"/*) : ;;
+ *) die "mountpoint must be under \$HOME or /run/media/$CALLER_USER" ;;
+ esac
+ printf '%s' "$canon"
+}
+
+# --- subcommands --------------------------------------------------------------
+do_mount() {
+ [ $# -eq 7 ] || die "mount: expected 7 arguments"
+ source=$1; mountpoint=$2; credfile=$3; vers=$4; extra=$5; fmode=$6; dmode=$7
+
+ case "$source" in
+ //?*/?*) : ;;
+ *) die "source must be //host/share" ;;
+ esac
+
+ mp=$(validate_mountpoint "$mountpoint")
+
+ # Credentials file: under the caller's runtime dir, their file, mode 0600.
+ cf=$(realpath -m -- "$credfile") || die "cannot canonicalise credfile"
+ case "$cf" in
+ "/run/user/$PKEXEC_UID"/*) : ;;
+ *) die "credfile must be under /run/user/$PKEXEC_UID" ;;
+ esac
+ [ -f "$cf" ] || die "credfile not found"
+ [ "$(stat -c %u "$cf")" = "$PKEXEC_UID" ] || die "credfile not owned by caller"
+ [ "$(stat -c %a "$cf")" = "600" ] || die "credfile must be mode 0600"
+
+ case "$vers" in
+ default|1.0|2.0|2.1|3.0|3.02|3.1.1) : ;;
+ *) die "unsupported SMB version: $vers" ;;
+ esac
+ case "$fmode" in 0[0-7][0-7][0-7]) : ;; *) die "bad file mode" ;; esac
+ case "$dmode" in 0[0-7][0-7][0-7]) : ;; *) die "bad dir mode" ;; esac
+ case "$extra" in
+ '') : ;;
+ *[!A-Za-z0-9=,_.:-]*) die "extra mount options contain illegal characters" ;;
+ esac
+
+ if findmnt -rn -o TARGET --mountpoint "$mp" >/dev/null 2>&1; then
+ die "already mounted: $mp"
+ fi
+
+ # Create the mount point and hand the freshly-created dirs to the caller.
+ mkdir -p "$mp" || die "cannot create mount point"
+ chown "$PKEXEC_UID:$CALLER_GID" "$mp" 2>/dev/null || true
+ p=$(dirname "$mp")
+ while [ "$p" != "$CALLER_HOME" ] && [ "$p" != "/" ] && [ "$p" != "/run/media" ]; do
+ chown "$PKEXEC_UID:$CALLER_GID" "$p" 2>/dev/null || true
+ p=$(dirname "$p")
+ done
+
+ opts="credentials=$cf,uid=$PKEXEC_UID,gid=$CALLER_GID,forceuid,forcegid,file_mode=$fmode,dir_mode=$dmode,vers=$vers"
+ [ -n "$extra" ] && opts="$opts,$extra"
+
+ exec mount.cifs "$source" "$mp" -o "$opts"
+}
+
+do_unmount() {
+ [ $# -eq 1 ] || die "unmount: expected 1 argument"
+ mp=$(validate_mountpoint "$1")
+ findmnt -rn -t cifs -o TARGET --mountpoint "$mp" >/dev/null 2>&1 \
+ || die "not a cifs mount: $mp"
+ umount "$mp" || die "umount failed"
+ # Tidy up empty leaf + host dirs (never recursive, never touches files).
+ rmdir "$mp" 2>/dev/null || true
+ rmdir "$(dirname "$mp")" 2>/dev/null || true
+}
+
+[ $# -ge 1 ] || die "no subcommand"
+cmd=$1
+shift
+case "$cmd" in
+ mount) do_mount "$@" ;;
+ unmount) do_unmount "$@" ;;
+ *) die "unknown subcommand: $cmd" ;;
+esac