Let a host carry pinned folders, merged path by path

This commit is contained in:
2026-08-07 15:25:51 +02:00
parent 82966af37b
commit 49645db680
8 changed files with 633 additions and 7 deletions
+28 -1
View File
@@ -102,12 +102,22 @@ public static class HostSecretCodec
/// </remarks>
public const int TagIdsSchemaVersion = 6;
/// <summary>The version that introduced <see cref="HostSecret.PinnedPaths"/>.</summary>
/// <remarks>
/// One past tags rather than sharing with them, for the reason <see cref="TagIdsSchemaVersion"/> gives
/// for sharing with inheritance instead of standing alone: the two fields are independent, a host can
/// pin paths without wearing a single tag, and stating the version such a host would actually need
/// keeps the rule in <see cref="SchemaVersionFor"/> a maximum over what is genuinely present rather
/// than a coincidence of what shipped together.
/// </remarks>
public const int PinnedPathsSchemaVersion = 7;
/// <summary>The highest schema version this build can write.</summary>
/// <remarks>
/// Names the highest constant above, which <see cref="SchemaVersionFor"/> assumes when it takes a
/// maximum. A new field added below this line has to be named here too.
/// </remarks>
public const int CurrentSchemaVersion = TagIdsSchemaVersion;
public const int CurrentSchemaVersion = PinnedPathsSchemaVersion;
/// <summary>Serialises a host to the bytes that get sealed.</summary>
/// <exception cref="ArgumentException">The host is not valid for storage.</exception>
@@ -149,6 +159,10 @@ public static class HostSecretCodec
// [] here would land in every host in every vault and make the first sync after the upgrade
// read as though every one of them had changed.
TagIds = host.TagIds.Count == 0 ? null : [.. host.TagIds],
// Last, and null for the same reason TagIds is: a host that pins nothing must encode exactly
// as it did before this field existed.
PinnedPaths = host.PinnedPaths.Count == 0 ? null : [.. host.PinnedPaths],
};
return JsonSerializer.SerializeToUtf8Bytes(
@@ -219,6 +233,11 @@ public static class HostSecretCodec
version = Math.Max(version, TagIdsSchemaVersion);
}
if (host.PinnedPaths.Count > 0)
{
version = Math.Max(version, PinnedPathsSchemaVersion);
}
return version;
}
@@ -295,6 +314,8 @@ public static class HostSecretCodec
AsksForPassword = parsed.AsksForPassword is true ? true : null,
TagIds = TagSet.Create(parsed.TagIds ?? []),
PinnedPaths = PinnedPathList.Create(parsed.PinnedPaths ?? []),
};
if (!candidate.TryValidate(out _))
@@ -369,6 +390,12 @@ internal sealed class HostPayloadDocument
/// which the sync engine would read as every host having changed.
/// </remarks>
public Guid[]? TagIds { get; set; }
/// <remarks>
/// Last, and null rather than <c>[]</c>, for exactly the reasons <see cref="TagIds"/> gives — it is the
/// newer of the two and follows the same rule.
/// </remarks>
public string[]? PinnedPaths { get; set; }
}
[JsonSourceGenerationOptions(