Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ 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
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`).
22 changes: 12 additions & 10 deletions src/SynoSharp/SynologyDiscovery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,18 @@ public async Task<SynologySnapshot> 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
Expand All @@ -51,13 +49,17 @@ public async Task<SynologySnapshot> 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<IReadOnlyList<string>> SafeListNamesAsync(
string api, int version, string method, string arrayProperty, CancellationToken cancellationToken)
{
Expand Down
3 changes: 2 additions & 1 deletion src/SynoSharp/SynologySnapshot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ namespace SynoSharp;
/// <summary>A read-only snapshot of DSM state (discover output).</summary>
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<string> Shares { get; init; } = [];
public IReadOnlyList<string> Users { get; init; } = [];
}
Loading