From 1c4aa8866075c1b0eb3db84dd00898b73fed8142 Mon Sep 17 00:00:00 2001 From: Chrison Simtian Date: Sun, 31 May 2026 21:14:14 +1200 Subject: [PATCH] feat: verify discover against live DSM; snapshot model/serial (not hostname) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live-verified against the DS1813+ (DSM 7.1.1-42962): login + SYNO.Core.Share/ User/System reads all return real data (9 shares, 7 users). SYNO.Core.System info has no hostname field — swap the snapshot's Hostname for Model + Serial (DS1813+, firmware_ver), which it does expose. Live test passes. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 12 ++++++------ src/SynoSharp/SynologyDiscovery.cs | 22 ++++++++++++---------- src/SynoSharp/SynologySnapshot.cs | 3 ++- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 35177bf..49df3f0 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ this is a hand-written **read-API client** (now) + an **SSH-runner** for mutatio ```bash dotnet build && dotnet test export SYNOLOGY_BASE_URL=https://nas:5001 SYNOLOGY_USER=… SYNOLOGY_PASSWORD=… SYNOLOGY_VERIFY_TLS=false -synosharp discover # JSON snapshot: DSM version, shares, users +synosharp discover # JSON snapshot: model, serial, DSM version, shares, users ``` Packages publish to GitHub Packages (chrison-dev) like the siblings: prerelease @@ -35,8 +35,8 @@ on push to `main`, stable on `v*` tag. ## Status -**Read/discover scaffold — UNVERIFIED.** The Virtual DSM test container needs -KVM/x86 so it can't run on Apple Silicon; the discover path is wired but must be -verified against a DSM target (a Linux-hosted Virtual DSM, or the live NAS -read-only). The `SYNO.Core.*` reads are defensive (degrade to empty on shape -mismatch). **Next:** verify discover, then the SSH-runner for the write path. +**Read/discover — verified against the live NAS (2026-05-31, DS1813+ / DSM +7.1.1-42962).** `discover` returns model, serial, DSM version, share names and +user names (`SYNO.Core.System` / `Share` / `User`). The `SYNO.Core.*` reads are +defensive (degrade to empty on shape mismatch). **Next:** the SSH-runner for the +write path (shares/NFS/users via on-box `syno*` + `synowebapi`). diff --git a/src/SynoSharp/SynologyDiscovery.cs b/src/SynoSharp/SynologyDiscovery.cs index 4862612..85428a1 100644 --- a/src/SynoSharp/SynologyDiscovery.cs +++ b/src/SynoSharp/SynologyDiscovery.cs @@ -28,20 +28,18 @@ public async Task DiscoverAsync(CancellationToken cancellation var users = await SafeListNamesAsync("SYNO.Core.User", 1, "list", "users", cancellationToken).ConfigureAwait(false); string? version = null; - string? hostname = null; + string? model = null; + string? serial = null; try { + // SYNO.Core.System info exposes firmware_ver / model / serial (verified + // against DSM 7.1.1 — there is no hostname field here). var info = await _client.GetAsync("SYNO.Core.System", 1, "info", cancellationToken: cancellationToken).ConfigureAwait(false); if (info.ValueKind == JsonValueKind.Object) { - if (info.TryGetProperty("firmware_ver", out var fv)) - { - version = fv.GetString(); - } - if (info.TryGetProperty("hostname", out var hn)) - { - hostname = hn.GetString(); - } + version = GetString(info, "firmware_ver"); + model = GetString(info, "model"); + serial = GetString(info, "serial"); } } catch @@ -51,13 +49,17 @@ public async Task DiscoverAsync(CancellationToken cancellation return new SynologySnapshot { + Model = model, + Serial = serial, DsmVersion = version, - Hostname = hostname, Shares = shares, Users = users, }; } + private static string? GetString(JsonElement obj, string name) + => obj.TryGetProperty(name, out var v) && v.ValueKind == JsonValueKind.String ? v.GetString() : null; + private async Task> SafeListNamesAsync( string api, int version, string method, string arrayProperty, CancellationToken cancellationToken) { diff --git a/src/SynoSharp/SynologySnapshot.cs b/src/SynoSharp/SynologySnapshot.cs index 41668c5..0ebdb10 100644 --- a/src/SynoSharp/SynologySnapshot.cs +++ b/src/SynoSharp/SynologySnapshot.cs @@ -3,8 +3,9 @@ namespace SynoSharp; /// A read-only snapshot of DSM state (discover output). public sealed record SynologySnapshot { + public string? Model { get; init; } + public string? Serial { get; init; } public string? DsmVersion { get; init; } - public string? Hostname { get; init; } public IReadOnlyList Shares { get; init; } = []; public IReadOnlyList Users { get; init; } = []; }