Exercised every Web API + SSH read command live against cascadesDS. - All reads OK; `ls <folder>` (FileStation list) is 407-denied for the admin account on this box (confirmed on-box as SYSTEM_ADMIN) -> now catches the 400/407 and prints an SSH file-browse hint. `ls` share-roots still works. - SSH backend (info/df/run + privileged synowebapi) verified. - Documented MSYS path-mangling of bare `ls /path` arg on Windows. - SKILL.md: per-command results; flagged write/setter path as not-yet-live. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 KiB
name, description
| name | description |
|---|---|
| synology | Control a Synology NAS end-to-end via two surfaces: the DSM Web API (auth + full API discovery + any API method — system, storage, shares, users/groups, packages, services, FileStation, connections) and an SSH backend for the syno* CLI the Web API doesn't expose (filesystem ACLs, low-level share/user/group internals). Reads run freely; mutating calls (set/create/delete/start/stop/reboot/shutdown/...) are gated behind --confirm. Default device is the Cascades NAS (cascadesDS, 192.168.0.120, admin); point at another client's NAS with --vault. Triggers: synology, diskstation, DSM, cascadesDS, NAS, control the synology, synology drive, hyper backup, active backup, synology package/service/share/user, reboot the nas, synology acl. |
synology — Synology DSM control (Web API + SSH)
Full control of a Synology NAS. Two complementary backends:
- DSM Web API (
scripts/syno_client.py) — the structured surface. Auth → discover the device's own API map → call any API method. This is how you "control all functions":apisshows everything the device exposes,call <API> <method>reaches any of it. - SSH
syno*CLI (scripts/syno-ssh.sh) — the gaps the Web API doesn't cover: filesystem ACLs (synoacltool), share/user/group CLI internals, package CLI. We already use this for the Cascades share-permission inventory (docs/migration/synology-permission-inventory.md).
Default target = Cascades cascadesDS (192.168.0.120, DSM on :5000, admin) — credential
vaulted at clients/cascades-tucson/synology-cascadesds.sops.yaml (host/port/
credentials.username/credentials.password). ext4 filesystem (NOT Btrfs → no Active Backup
for Business). Shares: homes, Public, SalesDept, Server, Management. On a private LAN — every
command needs the Cascades site VPN up (a connect failure almost always = VPN down). Point at
any other NAS with --vault clients/<x>/synology-...sops.yaml (same field layout).
Web API commands
PY="$CLAUDETOOLS_ROOT/.claude/scripts/py.sh"; S=".claude/skills/synology/scripts/syno_client.py"
# verify + discover
bash "$PY" "$S" test # login, print model/serial/firmware/RAM/uptime
bash "$PY" "$S" apis [filter] # the device's full API map (e.g. `apis backup`, `apis drive`)
# reads
bash "$PY" "$S" sysinfo # SYNO.Core.System — model/serial/RAM/temp/uptime
bash "$PY" "$S" util # live CPU/mem/disk/net
bash "$PY" "$S" storage # volumes/disks/RAID/usage (SYNO.Storage.CGI.Storage)
bash "$PY" "$S" shares | users | groups | packages | services | connections
bash "$PY" "$S" ls [folder_path] # FileStation: no path = shares; else list a folder
# generic power tool — ANY API method the device exposes
bash "$PY" "$S" call <API> <method> [k=v ...] [k:=json] [--version N] [--post]
bash "$PY" "$S" call SYNO.Core.System.Status get
bash "$PY" "$S" call SYNO.Backup.Task list # Hyper Backup tasks (if installed)
# writes — ALL gated --confirm
bash "$PY" "$S" pkg-start <id> --confirm | pkg-stop <id> --confirm
bash "$PY" "$S" reboot --confirm | shutdown --confirm
bash "$PY" "$S" call <API> <set-method> k=v --confirm # any mutating method needs --confirm
call params: k=v for strings, k:=json for typed/array values (e.g. additional:='["size","owner"]').
A method whose name starts with a mutating verb (set/create/delete/start/stop/reboot/…) is auto-detected
and refuses to run without --confirm.
SSH commands (the syno* CLI surface)
X=".claude/skills/synology/scripts/syno-ssh.sh"
bash "$X" info | df | shares | users | groups | packages | services # reads
bash "$X" acl <share> # synoacltool -get /volume1/<share> (e.g. acl Server)
bash "$X" reboot --confirm # synoshutdown -r (use when the Web-API reboot 103s)
bash "$X" shutdown --confirm # synoshutdown -s
bash "$X" run "<command>" --confirm # arbitrary command (gated); privileged auto-sudo -S
Requires SSH enabled on the NAS (DSM → Terminal & SNMP). Privileged recipes feed the vaulted
admin password to sudo -S. Auth: sshpass if present, else OpenSSH SSH_ASKPASS (on Windows the
fallback needs MSYS/Git-bash ssh on PATH — system OpenSSH can't exec a shell askpass).
Auth model (DSM Web API)
SYNO.API.Auth login (session=DSM, enable_syno_token=yes) → _sid (session cookie) + a
synotoken (CSRF). Both are attached to every subsequent call (the synotoken as X-SYNO-TOKEN
header + SynoToken param) so writes pass CSRF. 2FA: pass --otp <code> or SYNO_OTP. The client
always logs out at the end. Env overrides for ad-hoc use: SYNO_HOST/SYNO_PORT/SYNO_USER/SYNO_PASS,
SYNO_HTTPS=1 (→ :5001, cert verify off).
Full API reference
references/dsm-api.md is the consolidated, sourced map (auth, system/power, storage/shares,
users/groups/security, packages, Log Center, Drive, backup, network/services + the SSH syno*
surface) with per-row confidence and the 5 "golden rules". Read it before any write — the key
ones: discover versions via apis (don't hardcode), CSRF is mandatory for writes (handled),
setters are read-modify-write (get → mutate → push back; never construct from scratch), and
when a setter's param keys are uncertain run it on-box via synowebapi --exec (validates params).
Why both surfaces (don't reach for SSH first)
The Web API is the default and covers system/storage/shares/users/packages/services/FileStation/
backup/connections. Use SSH only for what the API genuinely lacks — chiefly filesystem ACLs
(synoacltool): the Web API tells you a share is in Windows-ACL mode but NOT the per-file ACE list.
That ACE data is exactly what the Cascades cutover needs (docs/migration/ synology-permission-analysis-2026-04-22.md).
Open Cascades work this skill serves (verified call recipes)
- Confirm model/RAM/DSM version (wiki TODO) —
testanswers it in one call. - Log Center syslog collector (
docs/network/network-logging-plan.md) — the NAS-as-syslog-SERVER isSYNO.LogCenter.*(needs the Log Center package):apis logcenterto confirm it's installed, thencall SYNO.LogCenter.RecvRule list(receive rules),call SYNO.LogCenter.Log list(received logs). NOTESYNO.Core.SyslogClient.*is the forward-own-logs-out side, not the collector. Schedule the snapshotter as a root task viaSYNO.Core.TaskScheduler.Root create. - Synology Drive Team Folder migration (
PROJECT_STATEpending) —apis synologydrive, thencall SYNO.SynologyDrive.TeamFolders list/... Tasks list/... Connection listto inspect before the CS-SERVER cutover. (SYNO.C2FS.*is C2 cloud — not this.) - Hyper Backup offsite (phase4 §6.4) —
call SYNO.Backup.Task list/... Repository listonce configured. (NOTE: Active Backup is installed + running on this ext4 box — the Btrfs requirement is only for ABB dedup/self-healing features, not the package.call SYNO.ActiveBackup.Task list.)
Error logging (mandatory)
On a GENUINE functional failure (DSM login fail, API error, SSH connect/auth rc=255) both scripts
log via .claude/scripts/log-skill-error.sh before surfacing. Handled conditions (missing cred,
VPN-down connect error surfaced to the user, a method refused for lack of --confirm) are NOT logged.
Status / verification
- [VERIFIED 2026-06-25 — HOWARD-HOME, Cascades VPN up] Full read-surface exercise against the
device. Device is DS718+, DSM 7.2.1-69057 Update 11, 6 GB RAM, serial 1920PEN537202; ext4,
2x WD10EZEX 1 TB (volume1 913G, 43% used). 10 shares (incl. hidden
pacs,Activities,chat,Sandra Fish); 30 packages running incl. Active Backup for Business 3.1.0, Synology Drive Server 3.5.0, Chat, VPN Server, Hybrid Share. 41 users, 4 groups (administrators, http, MainOffice, users). - Command-by-command results (Web API):
testapissysinfoutilstoragesharesusersgroupspackagesservicesconnectionsls(share roots)call— all OK. Auth (sid + synotoken CSRF), API discovery, version-by-maxVersion routing, and the genericcallpower-tool all confirmed live. - SSH backend (
syno-ssh.sh):infodfrunconfirmed OK (incl. privilegedsudo -Sand on-boxsynowebapi --execat/usr/syno/bin/synowebapi). This is the path for real file browsing. ls <folder>(FileStationlist) is DENIED for the admin account on this box — returns 407 on-box (SYSTEM_ADMIN) / 400 via Web API even for valid subfolders; the built-in admin lacks FileStation file privileges.ls(no path / share roots vialist_share) works fine. For actual file/folder browsing use the SSH backend:syno-ssh.sh run "ls -la /volume1/<share>/<subpath>" --confirm. (ls <folder>now catches this and prints the SSH hint.) Windows note: a barels /Publicarg is also rewritten by MSYS path-conversion — preferMSYS_NO_PATHCONV=1or thecall ... folder_path=/xform, but the 407 denial blocks it regardless.- WRITE/SETTER PATH NOT YET EXERCISED LIVE. Session-expiry re-login (106/119), the mutating-verb gate, and read-modify-write setters are coded to spec but no mutating call has been made against the device yet. Gate is verified only on the read path. First real change will confirm CSRF on a write end-to-end.
- Built originally from a 5-agent scan of the DSM 7 help tree + authoritative API sources (kwent
_full.json, N4S4/synology-api, Synology Web API guides) — seereferences/dsm-api.md. - Live fixes from this run (2026-06-25):
servicesusedSYNO.Core.Service.listwhich 103s on DSM 7.2.1 — corrected toget(returns{"service":[...]}).apis | headraised aBrokenPipeErrortraceback — now caught and exits cleanly.ls <folder>now degrades to an SSH hint on the FileStation 400/407 denial. - DSM 7.2.x reboot/shutdown 103: the Web-API
reboot/shutdowncan return error 103 on some builds. Fallback is the SSHreboot|shutdown --confirmrecipe (synoshutdown -r|-s). - Param-schema caveat: method names + versions in the reference are device-authoritative, but many
setter param keys are community/inferred. For a production write where keys are uncertain, capture
the matching
getand round-trip it, or run on-box viasynowebapi --exec.