12 KiB
name, description
| name | description |
|---|---|
| synology | Control a Synology NAS via the DSM Web API + SSH backend (system, storage, shares, users, packages, FileStation, ACLs); writes gated --confirm; default device Cascades cascadesDS. Triggers: synology, diskstation, DSM, NAS, hyper/active backup, 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 VERIFIED 2026-06-25. Live round-trip on cascadesDS:
SYNO.Core.Share create(hidden throwaway share) -> confirmed inshares->SYNO.Core.Share delete-> confirmed gone. Both mutating calls passed CSRF (synotoken) end-to-end and returned success; device left pristine. The mutating-verb gate correctly blocks without--confirmand allows with it. (Session-expiry re-login 106/119 still not forced live, but the auth/CSRF/setter plumbing is now proven.) Share create signature confirmed:name=<n> 'shareinfo:={"name":..,"vol_path":"/volume1",..}'. - 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.--confirm/--vaultafter the subcommand were rejected by argparse (every documented gated-write example, e.g.call X set k=v --confirm, would have failed) — moved to a shared parent parser so both flags now work before AND after the subcommand. - SSH backend fully verified + fixed (2026-06-25): the
priv(sudo) recipes —shares,users,groups,acl— were ALL broken:sudo'ssecure_pathdrops/usr/syno/{bin,sbin}, sosynoshare/synouser/synogroup/synoacltoolreturned "command not found" (the non-sudoplainrecipes worked because the admin login PATH has those dirs). Fixed by injectingSYNO_PATHand runningprivviash -c(so shell operators survive). Also:synouser/synogroupuse--enum local(not--list);aclquotes the share path (handles "Sandra Fish");servicesrepointed to the Web API (synoservicedoesn't exist on DSM 7.2, andsynosystemctlhas no list-all). Verified live:infodfsharesusers(41)groups(4)packagesacl Server(real Windows ACEs)acl Public(Linux-mode) all OK.aclon a Windows-ACL share is the SSH backend's unique value (the per-file ACE list the Web API can't give). - Code-review hardening (2026-06-25, /code-review high):
SynoErrornow carries the DSMcode- a
handledflag;call()no longer logs eagerly — the top-level handler logs only genuine unhandled failures, so the handled FileStation denial (and VPN-down connect errors) no longer polluteerrorlog.md. FileStation-denial detection is numeric (code in (400,407)), not a substring match, and the SSH hint now also fires on the genericcallpath (not justls).servicesfalls backget->liston 103 for older DSM builds. BrokenPipe flush moved inside the try so small piped output can't leak a shutdown traceback.
- a
- 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.