Skip to content

Smdn.Net.MuninNode version 1.3.0

Compare
Choose a tag to compare
@smdn smdn released this 11 Oct 11:32
· 20 commits to main since this release
1e4f59b

Released package

Release notes

The full release notes are available at gist.

Change log

Change log in this release:

API changes

API changes in this release:
diff --git a/doc/api-list/Smdn.Net.MuninNode/Smdn.Net.MuninNode-net6.0.apilist.cs b/doc/api-list/Smdn.Net.MuninNode/Smdn.Net.MuninNode-net6.0.apilist.cs
index 4bf85bf..130febd 100644
--- a/doc/api-list/Smdn.Net.MuninNode/Smdn.Net.MuninNode-net6.0.apilist.cs
+++ b/doc/api-list/Smdn.Net.MuninNode/Smdn.Net.MuninNode-net6.0.apilist.cs
@@ -1,210 +1,215 @@
-// Smdn.Net.MuninNode.dll (Smdn.Net.MuninNode-1.2.0)
+// Smdn.Net.MuninNode.dll (Smdn.Net.MuninNode-1.3.0)
 //   Name: Smdn.Net.MuninNode
-//   AssemblyVersion: 1.2.0.0
-//   InformationalVersion: 1.2.0+b0e11cd6b408018ad93f29b49e58cab7e9ef6b1b
+//   AssemblyVersion: 1.3.0.0
+//   InformationalVersion: 1.3.0+191d215fe57392cb544e2ffea221644a1007cfc0
 //   TargetFramework: .NETCoreApp,Version=v6.0
 //   Configuration: Release
 //   Referenced assemblies:
 //     Microsoft.Extensions.DependencyInjection.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
 //     Microsoft.Extensions.Logging.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
 //     Smdn.Fundamental.Exception, Version=3.0.0.0, Culture=neutral
 //     System.Collections, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
 //     System.ComponentModel, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
 //     System.IO.Pipelines, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
 //     System.Linq, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
 //     System.Memory, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
 //     System.Net.Primitives, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
 //     System.Net.Sockets, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
 //     System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
 //     System.Security.Cryptography.Algorithms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
 //     System.Text.RegularExpressions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
 #nullable enable annotations
 
 using System;
 using System.Collections.Generic;
 using System.Net;
 using System.Net.Sockets;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.Extensions.Logging;
 using Smdn.Net.MuninNode;
 using Smdn.Net.MuninPlugin;
 
 namespace Smdn.Net.MuninNode {
   public class LocalNode : NodeBase {
     public LocalNode(IPluginProvider pluginProvider, string hostName, int port, ILogger? logger = null) {}
     public LocalNode(IPluginProvider pluginProvider, string hostName, int port, IServiceProvider? serviceProvider = null) {}
     public LocalNode(IReadOnlyCollection<IPlugin> plugins, int port, IServiceProvider? serviceProvider = null) {}
     public LocalNode(IReadOnlyCollection<IPlugin> plugins, string hostName, int port, IServiceProvider? serviceProvider = null) {}
 
     public IPEndPoint LocalEndPoint { get; }
 
     protected override Socket CreateServerSocket() {}
     protected override bool IsClientAcceptable(IPEndPoint remoteEndPoint) {}
   }
 
   public abstract class NodeBase :
     IAsyncDisposable,
     IDisposable
   {
     private protected class PluginProvider : IPluginProvider {
       public PluginProvider(IReadOnlyCollection<IPlugin> plugins) {}
 
       public IReadOnlyCollection<IPlugin> Plugins { get; }
       public INodeSessionCallback? SessionCallback { get; }
     }
 
     protected NodeBase(IPluginProvider pluginProvider, string hostName, ILogger? logger) {}
     protected NodeBase(IReadOnlyCollection<IPlugin> plugins, string hostName, ILogger? logger) {}
 
     public virtual Encoding Encoding { get; }
     public string HostName { get; }
     protected ILogger? Logger { get; }
     public virtual Version NodeVersion { get; }
     [Obsolete("This member will be deprecated in future version.")]
     public IReadOnlyCollection<IPlugin> Plugins { get; }
 
     public async ValueTask AcceptAsync(bool throwIfCancellationRequested, CancellationToken cancellationToken) {}
     public async ValueTask AcceptSingleSessionAsync(CancellationToken cancellationToken = default) {}
     protected abstract Socket CreateServerSocket();
     protected virtual void Dispose(bool disposing) {}
     public void Dispose() {}
     public async ValueTask DisposeAsync() {}
     protected virtual async ValueTask DisposeAsyncCore() {}
     protected abstract bool IsClientAcceptable(IPEndPoint remoteEndPoint);
     public void Start() {}
   }
 }
 
 namespace Smdn.Net.MuninPlugin {
   public interface INodeSessionCallback {
     ValueTask ReportSessionClosedAsync(string sessionId, CancellationToken cancellationToken);
     ValueTask ReportSessionStartedAsync(string sessionId, CancellationToken cancellationToken);
   }
 
   public interface IPlugin {
     IPluginDataSource DataSource { get; }
     PluginGraphAttributes GraphAttributes { get; }
     string Name { get; }
     INodeSessionCallback? SessionCallback { get; }
   }
 
   public interface IPluginDataSource {
     IReadOnlyCollection<IPluginField> Fields { get; }
   }
 
   public interface IPluginField {
     PluginFieldAttributes Attributes { get; }
     string Name { get; }
 
     ValueTask<string> GetFormattedValueStringAsync(CancellationToken cancellationToken);
   }
 
   public interface IPluginProvider {
     IReadOnlyCollection<IPlugin> Plugins { get; }
     INodeSessionCallback? SessionCallback { get; }
   }
 
   public enum PluginFieldGraphStyle : int {
     Area = 1,
     AreaStack = 3,
     Default = 0,
     Line = 100,
     LineStack = 200,
     LineStackWidth1 = 201,
     LineStackWidth2 = 202,
     LineStackWidth3 = 203,
     LineWidth1 = 101,
     LineWidth2 = 102,
     LineWidth3 = 103,
     Stack = 2,
   }
 
   public class Plugin :
     INodeSessionCallback,
     IPlugin,
     IPluginDataSource
   {
     public Plugin(string name, PluginGraphAttributes graphAttributes, IReadOnlyCollection<IPluginField> fields) {}
 
     public IReadOnlyCollection<IPluginField> Fields { get; }
     public PluginGraphAttributes GraphAttributes { get; }
     public string Name { get; }
     IPluginDataSource IPlugin.DataSource { get; }
     INodeSessionCallback? IPlugin.SessionCallback { get; }
     IReadOnlyCollection<IPluginField> IPluginDataSource.Fields { get; }
 
     protected virtual ValueTask ReportSessionClosedAsync(string sessionId, CancellationToken cancellationToken) {}
     protected virtual ValueTask ReportSessionStartedAsync(string sessionId, CancellationToken cancellationToken) {}
     ValueTask INodeSessionCallback.ReportSessionClosedAsync(string sessionId, CancellationToken cancellationToken) {}
     ValueTask INodeSessionCallback.ReportSessionStartedAsync(string sessionId, CancellationToken cancellationToken) {}
   }
 
   public static class PluginFactory {
     public static IPluginField CreateField(string label, Func<double?> fetchValue) {}
     public static IPluginField CreateField(string label, PluginFieldGraphStyle graphStyle, Func<double?> fetchValue) {}
     public static IPluginField CreateField(string label, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, Func<double?> fetchValue) {}
     public static IPluginField CreateField(string name, string label, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, Func<double?> fetchValue) {}
+    public static IPluginField CreateField(string name, string label, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, string? negativeFieldName, Func<double?> fetchValue) {}
     public static IPlugin CreatePlugin(string name, PluginGraphAttributes graphAttributes, IReadOnlyCollection<IPluginField> fields) {}
     public static IPlugin CreatePlugin(string name, PluginGraphAttributes graphAttributes, IReadOnlyCollection<PluginFieldBase> fields) {}
     public static IPlugin CreatePlugin(string name, PluginGraphAttributes graphAttributes, PluginFieldBase field) {}
     public static IPlugin CreatePlugin(string name, string fieldLabel, Func<double?> fetchFieldValue, PluginGraphAttributes graphAttributes) {}
     public static IPlugin CreatePlugin(string name, string fieldLabel, PluginFieldGraphStyle fieldGraphStyle, Func<double?> fetchFieldValue, PluginGraphAttributes graphAttributes) {}
   }
 
   public abstract class PluginFieldBase : IPluginField {
     protected PluginFieldBase(string label, string? name, PluginFieldGraphStyle graphStyle = PluginFieldGraphStyle.Default, PluginFieldNormalValueRange normalRangeForWarning = default, PluginFieldNormalValueRange normalRangeForCritical = default) {}
+    protected PluginFieldBase(string label, string? name, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, string? negativeFieldName) {}
 
     public PluginFieldGraphStyle GraphStyle { get; }
     public string Label { get; }
     public string Name { get; }
+    public string? NegativeFieldName { get; }
     public PluginFieldNormalValueRange NormalRangeForCritical { get; }
     public PluginFieldNormalValueRange NormalRangeForWarning { get; }
     PluginFieldAttributes IPluginField.Attributes { get; }
 
     protected abstract ValueTask<double?> FetchValueAsync(CancellationToken cancellationToken);
     async ValueTask<string> IPluginField.GetFormattedValueStringAsync(CancellationToken cancellationToken) {}
   }
 
   public sealed class PluginGraphAttributes {
     [Obsolete("This member will be deprecated in future version.")]
     public PluginGraphAttributes(string title, string category, string verticalLabel, bool scale, string arguments, TimeSpan updateRate, int? width = null, int? height = null) {}
     public PluginGraphAttributes(string title, string category, string verticalLabel, bool scale, string arguments, TimeSpan? updateRate = null, int? width = null, int? height = null) {}
     public PluginGraphAttributes(string title, string category, string verticalLabel, bool scale, string arguments, TimeSpan? updateRate, int? width, int? height, IEnumerable<string>? order) {}
 
     public string Arguments { get; }
     public string Category { get; }
     public int? Height { get; }
     public string? Order { get; }
     public bool Scale { get; }
     public string Title { get; }
     public TimeSpan? UpdateRate { get; }
     public string VerticalLabel { get; }
     public int? Width { get; }
   }
 
   public readonly struct PluginFieldAttributes {
     public PluginFieldAttributes(string label, PluginFieldGraphStyle graphStyle = PluginFieldGraphStyle.Default) {}
     public PluginFieldAttributes(string label, PluginFieldGraphStyle graphStyle = PluginFieldGraphStyle.Default, PluginFieldNormalValueRange normalRangeForWarning = default, PluginFieldNormalValueRange normalRangeForCritical = default) {}
+    public PluginFieldAttributes(string label, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, string? negativeFieldName) {}
 
     public PluginFieldGraphStyle GraphStyle { get; }
     public string Label { get; }
+    public string? NegativeFieldName { get; }
     public PluginFieldNormalValueRange NormalRangeForCritical { get; }
     public PluginFieldNormalValueRange NormalRangeForWarning { get; }
   }
 
   public readonly struct PluginFieldNormalValueRange {
     public static readonly PluginFieldNormalValueRange None; // = "Smdn.Net.MuninPlugin.PluginFieldNormalValueRange"
 
     public static PluginFieldNormalValueRange CreateMax(double max) {}
     public static PluginFieldNormalValueRange CreateMin(double min) {}
     public static PluginFieldNormalValueRange CreateRange(double min, double max) {}
 
     public bool HasValue { get; }
     public double? Max { get; }
     public double? Min { get; }
   }
 }
-// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.2.0.
-// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
+// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.4.1.0.
+// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.3.1.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.Net.MuninNode/Smdn.Net.MuninNode-net8.0.apilist.cs b/doc/api-list/Smdn.Net.MuninNode/Smdn.Net.MuninNode-net8.0.apilist.cs
new file mode 100644
index 0000000..73b3402
--- /dev/null
+++ b/doc/api-list/Smdn.Net.MuninNode/Smdn.Net.MuninNode-net8.0.apilist.cs
@@ -0,0 +1,215 @@
+// Smdn.Net.MuninNode.dll (Smdn.Net.MuninNode-1.3.0)
+//   Name: Smdn.Net.MuninNode
+//   AssemblyVersion: 1.3.0.0
+//   InformationalVersion: 1.3.0+191d215fe57392cb544e2ffea221644a1007cfc0
+//   TargetFramework: .NETCoreApp,Version=v8.0
+//   Configuration: Release
+//   Referenced assemblies:
+//     Microsoft.Extensions.DependencyInjection.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
+//     Microsoft.Extensions.Logging.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
+//     Smdn.Fundamental.Exception, Version=3.0.0.0, Culture=neutral
+//     System.Collections, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+//     System.ComponentModel, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+//     System.IO.Pipelines, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
+//     System.Linq, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+//     System.Memory, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
+//     System.Net.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+//     System.Net.Sockets, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+//     System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+//     System.Security.Cryptography, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+//     System.Text.RegularExpressions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+#nullable enable annotations
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Smdn.Net.MuninNode;
+using Smdn.Net.MuninPlugin;
+
+namespace Smdn.Net.MuninNode {
+  public class LocalNode : NodeBase {
+    public LocalNode(IPluginProvider pluginProvider, string hostName, int port, ILogger? logger = null) {}
+    public LocalNode(IPluginProvider pluginProvider, string hostName, int port, IServiceProvider? serviceProvider = null) {}
+    public LocalNode(IReadOnlyCollection<IPlugin> plugins, int port, IServiceProvider? serviceProvider = null) {}
+    public LocalNode(IReadOnlyCollection<IPlugin> plugins, string hostName, int port, IServiceProvider? serviceProvider = null) {}
+
+    public IPEndPoint LocalEndPoint { get; }
+
+    protected override Socket CreateServerSocket() {}
+    protected override bool IsClientAcceptable(IPEndPoint remoteEndPoint) {}
+  }
+
+  public abstract class NodeBase :
+    IAsyncDisposable,
+    IDisposable
+  {
+    private protected class PluginProvider : IPluginProvider {
+      public PluginProvider(IReadOnlyCollection<IPlugin> plugins) {}
+
+      public IReadOnlyCollection<IPlugin> Plugins { get; }
+      public INodeSessionCallback? SessionCallback { get; }
+    }
+
+    protected NodeBase(IPluginProvider pluginProvider, string hostName, ILogger? logger) {}
+    protected NodeBase(IReadOnlyCollection<IPlugin> plugins, string hostName, ILogger? logger) {}
+
+    public virtual Encoding Encoding { get; }
+    public string HostName { get; }
+    protected ILogger? Logger { get; }
+    public virtual Version NodeVersion { get; }
+    [Obsolete("This member will be deprecated in future version.")]
+    public IReadOnlyCollection<IPlugin> Plugins { get; }
+
+    public async ValueTask AcceptAsync(bool throwIfCancellationRequested, CancellationToken cancellationToken) {}
+    public async ValueTask AcceptSingleSessionAsync(CancellationToken cancellationToken = default) {}
+    protected abstract Socket CreateServerSocket();
+    protected virtual void Dispose(bool disposing) {}
+    public void Dispose() {}
+    public async ValueTask DisposeAsync() {}
+    protected virtual async ValueTask DisposeAsyncCore() {}
+    protected abstract bool IsClientAcceptable(IPEndPoint remoteEndPoint);
+    public void Start() {}
+  }
+}
+
+namespace Smdn.Net.MuninPlugin {
+  public interface INodeSessionCallback {
+    ValueTask ReportSessionClosedAsync(string sessionId, CancellationToken cancellationToken);
+    ValueTask ReportSessionStartedAsync(string sessionId, CancellationToken cancellationToken);
+  }
+
+  public interface IPlugin {
+    IPluginDataSource DataSource { get; }
+    PluginGraphAttributes GraphAttributes { get; }
+    string Name { get; }
+    INodeSessionCallback? SessionCallback { get; }
+  }
+
+  public interface IPluginDataSource {
+    IReadOnlyCollection<IPluginField> Fields { get; }
+  }
+
+  public interface IPluginField {
+    PluginFieldAttributes Attributes { get; }
+    string Name { get; }
+
+    ValueTask<string> GetFormattedValueStringAsync(CancellationToken cancellationToken);
+  }
+
+  public interface IPluginProvider {
+    IReadOnlyCollection<IPlugin> Plugins { get; }
+    INodeSessionCallback? SessionCallback { get; }
+  }
+
+  public enum PluginFieldGraphStyle : int {
+    Area = 1,
+    AreaStack = 3,
+    Default = 0,
+    Line = 100,
+    LineStack = 200,
+    LineStackWidth1 = 201,
+    LineStackWidth2 = 202,
+    LineStackWidth3 = 203,
+    LineWidth1 = 101,
+    LineWidth2 = 102,
+    LineWidth3 = 103,
+    Stack = 2,
+  }
+
+  public class Plugin :
+    INodeSessionCallback,
+    IPlugin,
+    IPluginDataSource
+  {
+    public Plugin(string name, PluginGraphAttributes graphAttributes, IReadOnlyCollection<IPluginField> fields) {}
+
+    public IReadOnlyCollection<IPluginField> Fields { get; }
+    public PluginGraphAttributes GraphAttributes { get; }
+    public string Name { get; }
+    IPluginDataSource IPlugin.DataSource { get; }
+    INodeSessionCallback? IPlugin.SessionCallback { get; }
+    IReadOnlyCollection<IPluginField> IPluginDataSource.Fields { get; }
+
+    protected virtual ValueTask ReportSessionClosedAsync(string sessionId, CancellationToken cancellationToken) {}
+    protected virtual ValueTask ReportSessionStartedAsync(string sessionId, CancellationToken cancellationToken) {}
+    ValueTask INodeSessionCallback.ReportSessionClosedAsync(string sessionId, CancellationToken cancellationToken) {}
+    ValueTask INodeSessionCallback.ReportSessionStartedAsync(string sessionId, CancellationToken cancellationToken) {}
+  }
+
+  public static class PluginFactory {
+    public static IPluginField CreateField(string label, Func<double?> fetchValue) {}
+    public static IPluginField CreateField(string label, PluginFieldGraphStyle graphStyle, Func<double?> fetchValue) {}
+    public static IPluginField CreateField(string label, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, Func<double?> fetchValue) {}
+    public static IPluginField CreateField(string name, string label, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, Func<double?> fetchValue) {}
+    public static IPluginField CreateField(string name, string label, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, string? negativeFieldName, Func<double?> fetchValue) {}
+    public static IPlugin CreatePlugin(string name, PluginGraphAttributes graphAttributes, IReadOnlyCollection<IPluginField> fields) {}
+    public static IPlugin CreatePlugin(string name, PluginGraphAttributes graphAttributes, IReadOnlyCollection<PluginFieldBase> fields) {}
+    public static IPlugin CreatePlugin(string name, PluginGraphAttributes graphAttributes, PluginFieldBase field) {}
+    public static IPlugin CreatePlugin(string name, string fieldLabel, Func<double?> fetchFieldValue, PluginGraphAttributes graphAttributes) {}
+    public static IPlugin CreatePlugin(string name, string fieldLabel, PluginFieldGraphStyle fieldGraphStyle, Func<double?> fetchFieldValue, PluginGraphAttributes graphAttributes) {}
+  }
+
+  public abstract class PluginFieldBase : IPluginField {
+    protected PluginFieldBase(string label, string? name, PluginFieldGraphStyle graphStyle = PluginFieldGraphStyle.Default, PluginFieldNormalValueRange normalRangeForWarning = default, PluginFieldNormalValueRange normalRangeForCritical = default) {}
+    protected PluginFieldBase(string label, string? name, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, string? negativeFieldName) {}
+
+    public PluginFieldGraphStyle GraphStyle { get; }
+    public string Label { get; }
+    public string Name { get; }
+    public string? NegativeFieldName { get; }
+    public PluginFieldNormalValueRange NormalRangeForCritical { get; }
+    public PluginFieldNormalValueRange NormalRangeForWarning { get; }
+    PluginFieldAttributes IPluginField.Attributes { get; }
+
+    protected abstract ValueTask<double?> FetchValueAsync(CancellationToken cancellationToken);
+    async ValueTask<string> IPluginField.GetFormattedValueStringAsync(CancellationToken cancellationToken) {}
+  }
+
+  public sealed class PluginGraphAttributes {
+    [Obsolete("This member will be deprecated in future version.")]
+    public PluginGraphAttributes(string title, string category, string verticalLabel, bool scale, string arguments, TimeSpan updateRate, int? width = null, int? height = null) {}
+    public PluginGraphAttributes(string title, string category, string verticalLabel, bool scale, string arguments, TimeSpan? updateRate = null, int? width = null, int? height = null) {}
+    public PluginGraphAttributes(string title, string category, string verticalLabel, bool scale, string arguments, TimeSpan? updateRate, int? width, int? height, IEnumerable<string>? order) {}
+
+    public string Arguments { get; }
+    public string Category { get; }
+    public int? Height { get; }
+    public string? Order { get; }
+    public bool Scale { get; }
+    public string Title { get; }
+    public TimeSpan? UpdateRate { get; }
+    public string VerticalLabel { get; }
+    public int? Width { get; }
+  }
+
+  public readonly struct PluginFieldAttributes {
+    public PluginFieldAttributes(string label, PluginFieldGraphStyle graphStyle = PluginFieldGraphStyle.Default) {}
+    public PluginFieldAttributes(string label, PluginFieldGraphStyle graphStyle = PluginFieldGraphStyle.Default, PluginFieldNormalValueRange normalRangeForWarning = default, PluginFieldNormalValueRange normalRangeForCritical = default) {}
+    public PluginFieldAttributes(string label, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, string? negativeFieldName) {}
+
+    public PluginFieldGraphStyle GraphStyle { get; }
+    public string Label { get; }
+    public string? NegativeFieldName { get; }
+    public PluginFieldNormalValueRange NormalRangeForCritical { get; }
+    public PluginFieldNormalValueRange NormalRangeForWarning { get; }
+  }
+
+  public readonly struct PluginFieldNormalValueRange {
+    public static readonly PluginFieldNormalValueRange None; // = "Smdn.Net.MuninPlugin.PluginFieldNormalValueRange"
+
+    public static PluginFieldNormalValueRange CreateMax(double max) {}
+    public static PluginFieldNormalValueRange CreateMin(double min) {}
+    public static PluginFieldNormalValueRange CreateRange(double min, double max) {}
+
+    public bool HasValue { get; }
+    public double? Max { get; }
+    public double? Min { get; }
+  }
+}
+// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.4.1.0.
+// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.3.1.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.Net.MuninNode/Smdn.Net.MuninNode-netstandard2.1.apilist.cs b/doc/api-list/Smdn.Net.MuninNode/Smdn.Net.MuninNode-netstandard2.1.apilist.cs
index 817c4b6..48ee340 100644
--- a/doc/api-list/Smdn.Net.MuninNode/Smdn.Net.MuninNode-netstandard2.1.apilist.cs
+++ b/doc/api-list/Smdn.Net.MuninNode/Smdn.Net.MuninNode-netstandard2.1.apilist.cs
@@ -1,203 +1,208 @@
-// Smdn.Net.MuninNode.dll (Smdn.Net.MuninNode-1.2.0)
+// Smdn.Net.MuninNode.dll (Smdn.Net.MuninNode-1.3.0)
 //   Name: Smdn.Net.MuninNode
-//   AssemblyVersion: 1.2.0.0
-//   InformationalVersion: 1.2.0+b0e11cd6b408018ad93f29b49e58cab7e9ef6b1b
+//   AssemblyVersion: 1.3.0.0
+//   InformationalVersion: 1.3.0+191d215fe57392cb544e2ffea221644a1007cfc0
 //   TargetFramework: .NETStandard,Version=v2.1
 //   Configuration: Release
 //   Referenced assemblies:
 //     Microsoft.Extensions.DependencyInjection.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
 //     Microsoft.Extensions.Logging.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
 //     Smdn.Fundamental.Encoding.Buffer, Version=3.0.0.0, Culture=neutral
 //     Smdn.Fundamental.Exception, Version=3.0.0.0, Culture=neutral
 //     System.IO.Pipelines, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
 //     netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
 #nullable enable annotations
 
 using System;
 using System.Collections.Generic;
 using System.Net;
 using System.Net.Sockets;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.Extensions.Logging;
 using Smdn.Net.MuninNode;
 using Smdn.Net.MuninPlugin;
 
 namespace Smdn.Net.MuninNode {
   public class LocalNode : NodeBase {
     public LocalNode(IPluginProvider pluginProvider, string hostName, int port, ILogger? logger = null) {}
     public LocalNode(IPluginProvider pluginProvider, string hostName, int port, IServiceProvider? serviceProvider = null) {}
     public LocalNode(IReadOnlyCollection<IPlugin> plugins, int port, IServiceProvider? serviceProvider = null) {}
     public LocalNode(IReadOnlyCollection<IPlugin> plugins, string hostName, int port, IServiceProvider? serviceProvider = null) {}
 
     public IPEndPoint LocalEndPoint { get; }
 
     protected override Socket CreateServerSocket() {}
     protected override bool IsClientAcceptable(IPEndPoint remoteEndPoint) {}
   }
 
   public abstract class NodeBase :
     IAsyncDisposable,
     IDisposable
   {
     private protected class PluginProvider : IPluginProvider {
       public PluginProvider(IReadOnlyCollection<IPlugin> plugins) {}
 
       public IReadOnlyCollection<IPlugin> Plugins { get; }
       public INodeSessionCallback? SessionCallback { get; }
     }
 
     protected NodeBase(IPluginProvider pluginProvider, string hostName, ILogger? logger) {}
     protected NodeBase(IReadOnlyCollection<IPlugin> plugins, string hostName, ILogger? logger) {}
 
     public virtual Encoding Encoding { get; }
     public string HostName { get; }
     protected ILogger? Logger { get; }
     public virtual Version NodeVersion { get; }
     [Obsolete("This member will be deprecated in future version.")]
     public IReadOnlyCollection<IPlugin> Plugins { get; }
 
     public async ValueTask AcceptAsync(bool throwIfCancellationRequested, CancellationToken cancellationToken) {}
     public async ValueTask AcceptSingleSessionAsync(CancellationToken cancellationToken = default) {}
     protected abstract Socket CreateServerSocket();
     protected virtual void Dispose(bool disposing) {}
     public void Dispose() {}
     public async ValueTask DisposeAsync() {}
     protected virtual ValueTask DisposeAsyncCore() {}
     protected abstract bool IsClientAcceptable(IPEndPoint remoteEndPoint);
     public void Start() {}
   }
 }
 
 namespace Smdn.Net.MuninPlugin {
   public interface INodeSessionCallback {
     ValueTask ReportSessionClosedAsync(string sessionId, CancellationToken cancellationToken);
     ValueTask ReportSessionStartedAsync(string sessionId, CancellationToken cancellationToken);
   }
 
   public interface IPlugin {
     IPluginDataSource DataSource { get; }
     PluginGraphAttributes GraphAttributes { get; }
     string Name { get; }
     INodeSessionCallback? SessionCallback { get; }
   }
 
   public interface IPluginDataSource {
     IReadOnlyCollection<IPluginField> Fields { get; }
   }
 
   public interface IPluginField {
     PluginFieldAttributes Attributes { get; }
     string Name { get; }
 
     ValueTask<string> GetFormattedValueStringAsync(CancellationToken cancellationToken);
   }
 
   public interface IPluginProvider {
     IReadOnlyCollection<IPlugin> Plugins { get; }
     INodeSessionCallback? SessionCallback { get; }
   }
 
   public enum PluginFieldGraphStyle : int {
     Area = 1,
     AreaStack = 3,
     Default = 0,
     Line = 100,
     LineStack = 200,
     LineStackWidth1 = 201,
     LineStackWidth2 = 202,
     LineStackWidth3 = 203,
     LineWidth1 = 101,
     LineWidth2 = 102,
     LineWidth3 = 103,
     Stack = 2,
   }
 
   public class Plugin :
     INodeSessionCallback,
     IPlugin,
     IPluginDataSource
   {
     public Plugin(string name, PluginGraphAttributes graphAttributes, IReadOnlyCollection<IPluginField> fields) {}
 
     public IReadOnlyCollection<IPluginField> Fields { get; }
     public PluginGraphAttributes GraphAttributes { get; }
     public string Name { get; }
     IPluginDataSource IPlugin.DataSource { get; }
     INodeSessionCallback? IPlugin.SessionCallback { get; }
     IReadOnlyCollection<IPluginField> IPluginDataSource.Fields { get; }
 
     protected virtual ValueTask ReportSessionClosedAsync(string sessionId, CancellationToken cancellationToken) {}
     protected virtual ValueTask ReportSessionStartedAsync(string sessionId, CancellationToken cancellationToken) {}
     ValueTask INodeSessionCallback.ReportSessionClosedAsync(string sessionId, CancellationToken cancellationToken) {}
     ValueTask INodeSessionCallback.ReportSessionStartedAsync(string sessionId, CancellationToken cancellationToken) {}
   }
 
   public static class PluginFactory {
     public static IPluginField CreateField(string label, Func<double?> fetchValue) {}
     public static IPluginField CreateField(string label, PluginFieldGraphStyle graphStyle, Func<double?> fetchValue) {}
     public static IPluginField CreateField(string label, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, Func<double?> fetchValue) {}
     public static IPluginField CreateField(string name, string label, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, Func<double?> fetchValue) {}
+    public static IPluginField CreateField(string name, string label, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, string? negativeFieldName, Func<double?> fetchValue) {}
     public static IPlugin CreatePlugin(string name, PluginGraphAttributes graphAttributes, IReadOnlyCollection<IPluginField> fields) {}
     public static IPlugin CreatePlugin(string name, PluginGraphAttributes graphAttributes, IReadOnlyCollection<PluginFieldBase> fields) {}
     public static IPlugin CreatePlugin(string name, PluginGraphAttributes graphAttributes, PluginFieldBase field) {}
     public static IPlugin CreatePlugin(string name, string fieldLabel, Func<double?> fetchFieldValue, PluginGraphAttributes graphAttributes) {}
     public static IPlugin CreatePlugin(string name, string fieldLabel, PluginFieldGraphStyle fieldGraphStyle, Func<double?> fetchFieldValue, PluginGraphAttributes graphAttributes) {}
   }
 
   public abstract class PluginFieldBase : IPluginField {
     protected PluginFieldBase(string label, string? name, PluginFieldGraphStyle graphStyle = PluginFieldGraphStyle.Default, PluginFieldNormalValueRange normalRangeForWarning = default, PluginFieldNormalValueRange normalRangeForCritical = default) {}
+    protected PluginFieldBase(string label, string? name, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, string? negativeFieldName) {}
 
     public PluginFieldGraphStyle GraphStyle { get; }
     public string Label { get; }
     public string Name { get; }
+    public string? NegativeFieldName { get; }
     public PluginFieldNormalValueRange NormalRangeForCritical { get; }
     public PluginFieldNormalValueRange NormalRangeForWarning { get; }
     PluginFieldAttributes IPluginField.Attributes { get; }
 
     protected abstract ValueTask<double?> FetchValueAsync(CancellationToken cancellationToken);
     async ValueTask<string> IPluginField.GetFormattedValueStringAsync(CancellationToken cancellationToken) {}
   }
 
   public sealed class PluginGraphAttributes {
     [Obsolete("This member will be deprecated in future version.")]
     public PluginGraphAttributes(string title, string category, string verticalLabel, bool scale, string arguments, TimeSpan updateRate, int? width = null, int? height = null) {}
     public PluginGraphAttributes(string title, string category, string verticalLabel, bool scale, string arguments, TimeSpan? updateRate = null, int? width = null, int? height = null) {}
     public PluginGraphAttributes(string title, string category, string verticalLabel, bool scale, string arguments, TimeSpan? updateRate, int? width, int? height, IEnumerable<string>? order) {}
 
     public string Arguments { get; }
     public string Category { get; }
     public int? Height { get; }
     public string? Order { get; }
     public bool Scale { get; }
     public string Title { get; }
     public TimeSpan? UpdateRate { get; }
     public string VerticalLabel { get; }
     public int? Width { get; }
   }
 
   public readonly struct PluginFieldAttributes {
     public PluginFieldAttributes(string label, PluginFieldGraphStyle graphStyle = PluginFieldGraphStyle.Default) {}
     public PluginFieldAttributes(string label, PluginFieldGraphStyle graphStyle = PluginFieldGraphStyle.Default, PluginFieldNormalValueRange normalRangeForWarning = default, PluginFieldNormalValueRange normalRangeForCritical = default) {}
+    public PluginFieldAttributes(string label, PluginFieldGraphStyle graphStyle, PluginFieldNormalValueRange normalRangeForWarning, PluginFieldNormalValueRange normalRangeForCritical, string? negativeFieldName) {}
 
     public PluginFieldGraphStyle GraphStyle { get; }
     public string Label { get; }
+    public string? NegativeFieldName { get; }
     public PluginFieldNormalValueRange NormalRangeForCritical { get; }
     public PluginFieldNormalValueRange NormalRangeForWarning { get; }
   }
 
   public readonly struct PluginFieldNormalValueRange {
     public static readonly PluginFieldNormalValueRange None; // = "Smdn.Net.MuninPlugin.PluginFieldNormalValueRange"
 
     public static PluginFieldNormalValueRange CreateMax(double max) {}
     public static PluginFieldNormalValueRange CreateMin(double min) {}
     public static PluginFieldNormalValueRange CreateRange(double min, double max) {}
 
     public bool HasValue { get; }
     public double? Max { get; }
     public double? Min { get; }
   }
 }
-// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.2.0.
-// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
+// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.4.1.0.
+// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.3.1.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)

Full changes

Full changes in this release:
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode.csproj b/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode.csproj
index 9295e42..6384660 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode.csproj
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode.csproj
@@ -4,24 +4,21 @@ SPDX-License-Identifier: MIT
 -->
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFrameworks>netstandard2.1;net6.0</TargetFrameworks>
-    <VersionPrefix>1.2.0</VersionPrefix>
+    <TargetFrameworks>netstandard2.1;net6.0;net8.0</TargetFrameworks>
+    <VersionPrefix>1.3.0</VersionPrefix>
     <VersionSuffix></VersionSuffix>
-    <PackageValidationBaselineVersion>1.0.0</PackageValidationBaselineVersion>
+    <PackageValidationBaselineVersion>1.2.0</PackageValidationBaselineVersion>
     <RootNamespace/> <!-- empty the root namespace so that the namespace is determined only by the directory name, for code style rule IDE0030 -->
     <Nullable>enable</Nullable>
     <DefineConstants
       Condition="$([MSBuild]::VersionGreaterThanOrEquals('$(NETCoreSdkVersion)', '7.0.0'))"
     >$(DefineConstants);LANG_VERSION_11_OR_GREATER</DefineConstants> <!-- required to use the UTF-8 string literals in C# 11 -->
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(Configuration)' == 'Release' " Label="Required properties to generate API list">
-    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
+    <NoWarn>CS1591;$(NoWarn)</NoWarn> <!-- CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' -->
   </PropertyGroup>
 
   <PropertyGroup Label="assembly attributes">
     <Description>
-<![CDATA[A .NET implementation of [Munin-Node](http://guide.munin-monitoring.org/en/latest/node/index.html) and [Munin-Plugin](http://guide.munin-monitoring.org/en/latest/plugin/index.html).
+<![CDATA[A .NET implementation of [Munin-Node](https://guide.munin-monitoring.org/en/latest/node/index.html) and [Munin-Plugin](https://guide.munin-monitoring.org/en/latest/plugin/index.html).
 
 This library provides Munin-Node implementation for .NET, which enables to you to create custom Munin-Node using the .NET languages and libraries.
 
@@ -33,6 +30,7 @@ This library also provides abstraction APIs for implementing Munin-Plugin. By us
 
   <PropertyGroup Label="package properties">
     <PackageTags>Munin,Munin-Node,Munin-Plugin</PackageTags>
+    <GenerateNupkgReadmeFileDependsOnTargets>$(GenerateNupkgReadmeFileDependsOnTargets);GenerateReadmeFileContent</GenerateNupkgReadmeFileDependsOnTargets>
   </PropertyGroup>
 
   <ItemGroup>
@@ -42,4 +40,15 @@ This library also provides abstraction APIs for implementing Munin-Plugin. By us
     <PackageReference Include="Smdn.Fundamental.Encoding.Buffer" Version="[3.0.0,4.0.0)" Condition="$(TargetFramework.StartsWith('net4')) or $(TargetFramework.StartsWith('netstandard'))" />
     <PackageReference Include="Smdn.Fundamental.Exception" Version="[3.0.0,4.0.0)" />
   </ItemGroup>
+
+  <Target Name="GenerateReadmeFileContent">
+    <PropertyGroup>
+      <PackageReadmeFileContent><![CDATA[# $(PackageId) $(PackageVersion)
+$(Description)
+
+## Contributing
+This project welcomes contributions, feedbacks and suggestions. You can contribute to this project by submitting [Issues]($(RepositoryUrl)/issues/new/choose) or [Pull Requests]($(RepositoryUrl)/pulls/) on the [GitHub repository]($(RepositoryUrl)).
+]]></PackageReadmeFileContent>
+    </PropertyGroup>
+  </Target>
 </Project>
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/LocalNode.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/LocalNode.cs
index b42f9dc..9810630 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/LocalNode.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/LocalNode.cs
@@ -19,7 +21,7 @@ public class LocalNode : NodeBase {
 
   public IPEndPoint LocalEndPoint { get; }
 
-  /// <inheritdoc cref="LocalNode(IReadOnlyCollection{IPlugin}, int, string, IServiceProvider)"/>
+  /// <inheritdoc cref="LocalNode(IReadOnlyCollection{IPlugin}, string, int, IServiceProvider)"/>
   public LocalNode(
     IReadOnlyCollection<IPlugin> plugins,
     int port,
@@ -177,7 +179,9 @@ public class LocalNode : NodeBase {
       return server;
     }
     catch {
+#pragma warning disable CA1508
       server?.Dispose();
+#pragma warning restore CA1508
       throw;
     }
   }
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/NodeBase.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/NodeBase.cs
index d3a7ebd..df4b7a1 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/NodeBase.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/NodeBase.cs
@@ -27,16 +28,16 @@ namespace Smdn.Net.MuninNode;
 /// <summary>
 /// Provides an extensible base class with basic Munin-Node functionality.
 /// </summary>
-/// <seealso href="http://guide.munin-monitoring.org/en/latest/node/index.html">The Munin node</seealso>
+/// <seealso href="https://guide.munin-monitoring.org/en/latest/node/index.html">The Munin node</seealso>
 public abstract class NodeBase : IDisposable, IAsyncDisposable {
-  private static readonly Version defaultNodeVersion = new(1, 0, 0, 0);
+  private static readonly Version DefaultNodeVersion = new(1, 0, 0, 0);
 
   [Obsolete("This member will be deprecated in future version.")]
   public IReadOnlyCollection<IPlugin> Plugins => pluginProvider.Plugins;
 
   public string HostName { get; }
 
-  public virtual Version NodeVersion => defaultNodeVersion;
+  public virtual Version NodeVersion => DefaultNodeVersion;
   public virtual Encoding Encoding => Encoding.Default;
 
   private readonly IPluginProvider pluginProvider;
@@ -355,12 +356,12 @@ public abstract class NodeBase : IDisposable, IAsyncDisposable {
     CancellationToken cancellationToken
   )
   {
-    const int minimumBufferSize = 256;
+    const int MinimumBufferSize = 256;
 
     for (; ; ) {
       cancellationToken.ThrowIfCancellationRequested();
 
-      var memory = writer.GetMemory(minimumBufferSize);
+      var memory = writer.GetMemory(MinimumBufferSize);
 
       try {
         if (!socket.Connected)
@@ -544,7 +545,7 @@ public abstract class NodeBase : IDisposable, IAsyncDisposable {
     return false;
   }
 
-  private static readonly byte commandQuitShort = (byte)'.';
+  private static readonly byte CommandQuitShort = (byte)'.';
 
 #if LANG_VERSION_11_OR_GREATER
   private ValueTask RespondToCommandAsync(
@@ -567,7 +568,7 @@ public abstract class NodeBase : IDisposable, IAsyncDisposable {
     }
     else if (
       ExpectCommand(commandLine, "quit"u8, out _) ||
-      (commandLine.Length == 1 && commandLine.FirstSpan[0] == commandQuitShort)
+      (commandLine.Length == 1 && commandLine.FirstSpan[0] == CommandQuitShort)
     ) {
       client.Close();
 #if SYSTEM_THREADING_TASKS_VALUETASK_COMPLETEDTASK
@@ -649,9 +650,14 @@ public abstract class NodeBase : IDisposable, IAsyncDisposable {
 #endif
 
 #pragma warning disable IDE0230
-  private static readonly ReadOnlyMemory<byte> endOfLine = new[] { (byte)'\n' };
+  private static readonly ReadOnlyMemory<byte> EndOfLine = new[] { (byte)'\n' };
 #pragma warning restore IDE0230
 
+  private static readonly string[] ResponseLinesUnknownService = [
+    "# Unknown service",
+    ".",
+  ];
+
   private static ValueTask SendResponseAsync(
     Socket client,
     Encoding encoding,
@@ -687,7 +693,7 @@ public abstract class NodeBase : IDisposable, IAsyncDisposable {
       ).ConfigureAwait(false);
 
       await client.SendAsync(
-        buffer: endOfLine,
+        buffer: EndOfLine,
         socketFlags: SocketFlags.None,
         cancellationToken: cancellationToken
       ).ConfigureAwait(false);
@@ -731,8 +737,8 @@ public abstract class NodeBase : IDisposable, IAsyncDisposable {
     CancellationToken cancellationToken
   )
   {
-    // TODO: multigraph (http://guide.munin-monitoring.org/en/latest/plugin/protocol-multigraph.html)
-    // TODO: dirtyconfig (http://guide.munin-monitoring.org/en/latest/plugin/protocol-dirtyconfig.html)
+    // TODO: multigraph (https://guide.munin-monitoring.org/en/latest/plugin/protocol-multigraph.html)
+    // TODO: dirtyconfig (https://guide.munin-monitoring.org/en/latest/plugin/protocol-dirtyconfig.html)
     // XXX: ignores capability arguments
     return SendResponseAsync(
       client: client,
@@ -773,10 +779,7 @@ public abstract class NodeBase : IDisposable, IAsyncDisposable {
       await SendResponseAsync(
         client,
         Encoding,
-        new[] {
-          "# Unknown service",
-          ".",
-        },
+        ResponseLinesUnknownService,
         cancellationToken
       ).ConfigureAwait(false);
 
@@ -834,10 +837,7 @@ public abstract class NodeBase : IDisposable, IAsyncDisposable {
       return SendResponseAsync(
         client,
         Encoding,
-        new[] {
-          "# Unknown service",
-          ".",
-        },
+        ResponseLinesUnknownService,
         cancellationToken
       );
     }
@@ -861,8 +861,15 @@ public abstract class NodeBase : IDisposable, IAsyncDisposable {
     if (!string.IsNullOrEmpty(graphAttrs.Order))
       responseLines.Add($"graph_order {graphAttrs.Order}");
 
-    foreach (var field in plugin.DataSource.Fields) {
+    // The fields referenced by {fieldname}.negative must be defined ahread of others,
+    // and thus lists the negative field settings first.
+    // Otherwise, the following error occurs when generating the graph.
+    // "[RRD ERROR] Unable to graph /var/cache/munin/www/XXX.png : undefined v name XXXXXXXXXXXXXX"
+    var orderedFields = plugin.DataSource.Fields.OrderBy(f => IsNegativeField(f, plugin.DataSource.Fields) ? 0 : 1);
+
+    foreach (var field in orderedFields) {
       var fieldAttrs = field.Attributes;
+      bool? graph = null;
 
       responseLines.Add($"{field.Name}.label {fieldAttrs.Label}");
 
@@ -877,6 +884,22 @@ public abstract class NodeBase : IDisposable, IAsyncDisposable {
       if (fieldAttrs.NormalRangeForCritical.HasValue)
         AddFieldValueRange("critical", fieldAttrs.NormalRangeForCritical);
 
+      if (!string.IsNullOrEmpty(fieldAttrs.NegativeFieldName)) {
+        var negativeField = plugin.DataSource.Fields.FirstOrDefault(
+          f => string.Equals(fieldAttrs.NegativeFieldName, f.Name, StringComparison.Ordinal)
+        );
+
+        if (negativeField is not null)
+          responseLines.Add($"{field.Name}.negative {negativeField.Name}");
+      }
+
+      // this field is defined as the negative field of other field, so should not be graphed
+      if (IsNegativeField(field, plugin.DataSource.Fields))
+        graph = false;
+
+      if (graph is bool drawGraph)
+        responseLines.Add($"{field.Name}.graph {(drawGraph ? "yes" : "no")}");
+
       void AddFieldValueRange(string attr, PluginFieldNormalValueRange range)
       {
         if (range.Min.HasValue && range.Max.HasValue)
@@ -896,5 +919,10 @@ public abstract class NodeBase : IDisposable, IAsyncDisposable {
       responseLines: responseLines,
       cancellationToken: cancellationToken
     );
+
+    static bool IsNegativeField(IPluginField field, IReadOnlyCollection<IPluginField> fields)
+      => fields.Any(
+        f => string.Equals(field.Name, f.Attributes.NegativeFieldName, StringComparison.Ordinal)
+      );
   }
 }
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPlugin.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPlugin.cs
index 6130160..cf725ac 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPlugin.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPlugin.cs
@@ -6,7 +6,7 @@ namespace Smdn.Net.MuninPlugin;
 /// <summary>
 /// Provides an interface that abstracts the plugin.
 /// </summary>
-/// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html">Plugin reference</seealso>
+/// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html">Plugin reference</seealso>
 public interface IPlugin {
   /// <summary>Gets a plugin name.</summary>
   /// <remarks>This value is used as the plugin name returned by the 'list' argument, or the plugin name specified by the 'fetch' argument.</remarks>
@@ -20,7 +20,7 @@ public interface IPlugin {
   /// <seealso cref="IPluginDataSource"/>
   IPluginDataSource DataSource { get; }
 
-  /// <summary>Gets a <see cref="INodeSessionCallback"/>, which defines the callbacks when a request session from the <c>munin-update</c> starts or ends, such as fetching data or getting configurations.
+  /// <summary>Gets a <see cref="INodeSessionCallback"/>, which defines the callbacks when a request session from the <c>munin-update</c> starts or ends, such as fetching data or getting configurations.</summary>
   /// <remarks>Callbacks of this interface can be used to initiate bulk collection of field values.</remarks>
   /// <seealso cref="INodeSessionCallback"/>
   /// <seealso cref="MuninNode.NodeBase"/>
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPluginDataSource.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPluginDataSource.cs
index 715881b..9dc380d 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPluginDataSource.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPluginDataSource.cs
@@ -9,7 +9,7 @@ namespace Smdn.Net.MuninPlugin;
 /// Provides an interface that abstracts the data source for the plugin.
 /// </summary>
 public interface IPluginDataSource {
-  /// <summary>Gets a collection of plugin fields (<see cref="IPluginField"/>) provided by this data source.
+  /// <summary>Gets a collection of plugin fields (<see cref="IPluginField"/>) provided by this data source.</summary>
   /// <seealso cref="IPluginField"/>
   IReadOnlyCollection<IPluginField> Fields { get; }
 }
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPluginField.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPluginField.cs
index 29f1033..469ce16 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPluginField.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPluginField.cs
@@ -10,10 +10,10 @@ namespace Smdn.Net.MuninPlugin;
 /// Provides an interface that abstracts the fields of the plugin.
 /// This interface represents the 'field name attributes'.
 /// </summary>
-/// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#field-name-attributes">Plugin reference - Field name attributes</seealso>
+/// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#field-name-attributes">Plugin reference - Field name attributes</seealso>
 public interface IPluginField {
   /// <summary>Gets a value for the <c>fieldname</c>. This value represents the <c>fieldname</c> itself for the attribute <c>{fieldname}.xxx</c>.</summary>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#field-name-attributes">Plugin reference - Field name attributes</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#field-name-attributes">Plugin reference - Field name attributes</seealso>
   string Name { get; }
 
   /// <summary>Gets a collection of attributes that describes the <c>fieldname</c>.</summary>
@@ -24,7 +24,7 @@ public interface IPluginField {
   /// </summary>
   /// <param name="cancellationToken">The <see cref="CancellationToken" /> to monitor for cancellation requests.</param>
   /// <returns>
-  /// A <see cref="ValueTask{string?}"/> representing the current value of the field.
+  /// A <see cref="ValueTask{String}"/> representing the current value of the field.
   /// Returns a field's numeric value in its string representation.
   /// By returning <c>"U"</c> instead of numeric value, the field can also be reported as having a value of 'UNKNOWN'.
   /// </returns>
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPluginProvider.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPluginProvider.cs
index 875400c..92cd30b 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPluginProvider.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/IPluginProvider.cs
@@ -18,7 +18,7 @@ public interface IPluginProvider {
   /// <seealso cref="MuninNode.NodeBase"/>
   IReadOnlyCollection<IPlugin> Plugins { get; }
 
-  /// <summary>Gets a <see cref="INodeSessionCallback"/>, which defines the callbacks when a request session from the <c>munin-update</c> starts or ends, such as fetching data or getting configurations.
+  /// <summary>Gets a <see cref="INodeSessionCallback"/>, which defines the callbacks when a request session from the <c>munin-update</c> starts or ends, such as fetching data or getting configurations.</summary>
   /// <seealso cref="INodeSessionCallback"/>
   /// <seealso cref="MuninNode.NodeBase"/>
   INodeSessionCallback? SessionCallback { get; }
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFactory.CreateField.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFactory.CreateField.cs
index 50ec013..3656e58 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFactory.CreateField.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFactory.CreateField.cs
@@ -19,6 +19,7 @@ partial class PluginFactory {
       graphStyle: PluginFieldGraphStyle.Default,
       normalRangeForWarning: PluginFieldNormalValueRange.None,
       normalRangeForCritical: PluginFieldNormalValueRange.None,
+      negativeFieldName: null,
       fetchValue: fetchValue
     );
 
@@ -33,6 +34,7 @@ partial class PluginFactory {
       graphStyle: graphStyle,
       normalRangeForWarning: PluginFieldNormalValueRange.None,
       normalRangeForCritical: PluginFieldNormalValueRange.None,
+      negativeFieldName: null,
       fetchValue: fetchValue
     );
 
@@ -49,6 +51,7 @@ partial class PluginFactory {
       graphStyle: graphStyle,
       normalRangeForWarning: normalRangeForWarning,
       normalRangeForCritical: normalRangeForCritical,
+      negativeFieldName: null,
       fetchValue: fetchValue
     );
 
@@ -66,6 +69,26 @@ partial class PluginFactory {
       graphStyle: graphStyle,
       normalRangeForWarning: normalRangeForWarning,
       normalRangeForCritical: normalRangeForCritical,
+      negativeFieldName: null,
+      fetchValue: fetchValue
+    );
+
+  public static IPluginField CreateField(
+    string name,
+    string label,
+    PluginFieldGraphStyle graphStyle,
+    PluginFieldNormalValueRange normalRangeForWarning,
+    PluginFieldNormalValueRange normalRangeForCritical,
+    string? negativeFieldName,
+    Func<double?> fetchValue
+  )
+    => new ValueFromFuncPluginField(
+      label: label,
+      name: name,
+      graphStyle: graphStyle,
+      normalRangeForWarning: normalRangeForWarning,
+      normalRangeForCritical: normalRangeForCritical,
+      negativeFieldName: negativeFieldName,
       fetchValue: fetchValue
     );
 
@@ -78,6 +101,7 @@ partial class PluginFactory {
       PluginFieldGraphStyle graphStyle,
       PluginFieldNormalValueRange normalRangeForWarning,
       PluginFieldNormalValueRange normalRangeForCritical,
+      string? negativeFieldName,
       Func<double?> fetchValue
     )
       : base(
@@ -85,7 +109,8 @@ partial class PluginFactory {
         name: name,
         graphStyle: graphStyle,
         normalRangeForWarning: normalRangeForWarning,
-        normalRangeForCritical: normalRangeForCritical
+        normalRangeForCritical: normalRangeForCritical,
+        negativeFieldName: negativeFieldName
       )
     {
       this.fetchValue = fetchValue ?? throw new ArgumentNullException(nameof(fetchValue));
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFactory.CreatePlugin.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFactory.CreatePlugin.cs
index 741ae54..e91d533 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFactory.CreatePlugin.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFactory.CreatePlugin.cs
@@ -41,6 +41,7 @@ partial class PluginFactory {
         graphStyle: fieldGraphStyle,
         normalRangeForWarning: PluginFieldNormalValueRange.None,
         normalRangeForCritical: PluginFieldNormalValueRange.None,
+        negativeFieldName: null,
         fetchValue: fetchFieldValue
       )
     );
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldAttributes.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldAttributes.cs
index 22a37cf..096a1a2 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldAttributes.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldAttributes.cs
@@ -9,29 +9,38 @@ namespace Smdn.Net.MuninPlugin;
 /// Defines field attributes that should be returned when the plugin is called with the 'config' argument.
 /// This type represents the collection of 'field name attributes'.
 /// </summary>
-/// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#field-name-attributes">Plugin reference - Field name attributes</seealso>
+/// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#field-name-attributes">Plugin reference - Field name attributes</seealso>
 public readonly struct PluginFieldAttributes {
   /// <summary>Gets a value for the <c>{fieldname}.label</c>.</summary>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-label">Plugin reference - Field name attributes - {fieldname}.label</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-label">Plugin reference - Field name attributes - {fieldname}.label</seealso>
   public string Label { get; }
 
   /// <summary>Gets a value for the <c>{fieldname}.draw</c>.</summary>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-draw">Plugin reference - Field name attributes - {fieldname}.draw</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-draw">Plugin reference - Field name attributes - {fieldname}.draw</seealso>
   /// <seealso cref="PluginFieldGraphStyle"/>
   public PluginFieldGraphStyle GraphStyle { get; }
 
   /// <summary>Gets a value for the <c>{fieldname}.warning</c>.</summary>
   /// <remarks>This property defines the upper limit, lower limit, or range of normal value, that is not treated as warning.</remarks>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-warning">Plugin reference - Field name attributes - {fieldname}.warning</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-warning">Plugin reference - Field name attributes - {fieldname}.warning</seealso>
   /// <seealso cref="PluginFieldNormalValueRange"/>
   public PluginFieldNormalValueRange NormalRangeForWarning { get; }
 
   /// <summary>Gets a value for the <c>{fieldname}.critical</c>.</summary>
   /// <remarks>This property defines the upper limit, lower limit, or range of normal value, that is not treated as critical.</remarks>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-critical">Plugin reference - Field name attributes - {fieldname}.critical</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-critical">Plugin reference - Field name attributes - {fieldname}.critical</seealso>
   /// <seealso cref="PluginFieldNormalValueRange"/>
   public PluginFieldNormalValueRange NormalRangeForCritical { get; }
 
+  /// <summary>Gets a value for the <c>{fieldname}.negative</c>.</summary>
+  /// <remarks>
+  /// This property specifies that the specified field is drawn as the negative side of this field.
+  /// If a valid field name is specified for this property, it also implicitly sets the attribute <c>{fieldname}.graph no</c>.
+  /// </remarks>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-negative">Plugin reference - Field name attributes - {fieldname}.critical</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/develop/plugins/plugin-bcp.html#plugin-bcp-direction">Best Current Practices for good plugin graphs - Direction</seealso>
+  public string? NegativeFieldName { get; }
+
   public PluginFieldAttributes(
     string label,
     PluginFieldGraphStyle graphStyle = PluginFieldGraphStyle.Default
@@ -40,7 +49,8 @@ public readonly struct PluginFieldAttributes {
       label: label,
       graphStyle: graphStyle,
       normalRangeForWarning: default,
-      normalRangeForCritical: default
+      normalRangeForCritical: default,
+      negativeFieldName: null
     )
   {
   }
@@ -50,6 +60,23 @@ public readonly struct PluginFieldAttributes {
     PluginFieldGraphStyle graphStyle = PluginFieldGraphStyle.Default,
     PluginFieldNormalValueRange normalRangeForWarning = default,
     PluginFieldNormalValueRange normalRangeForCritical = default
+  )
+    : this(
+      label: label,
+      graphStyle: graphStyle,
+      normalRangeForWarning: normalRangeForWarning,
+      normalRangeForCritical: normalRangeForCritical,
+      negativeFieldName: null
+    )
+  {
+  }
+
+  public PluginFieldAttributes(
+    string label,
+    PluginFieldGraphStyle graphStyle,
+    PluginFieldNormalValueRange normalRangeForWarning,
+    PluginFieldNormalValueRange normalRangeForCritical,
+    string? negativeFieldName
   )
   {
     if (label is null)
@@ -61,5 +88,6 @@ public readonly struct PluginFieldAttributes {
     GraphStyle = graphStyle;
     NormalRangeForWarning = normalRangeForWarning;
     NormalRangeForCritical = normalRangeForCritical;
+    NegativeFieldName = negativeFieldName;
   }
 }
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldBase.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldBase.cs
index 808d264..84ab32b 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldBase.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldBase.cs
@@ -14,13 +14,15 @@ public abstract class PluginFieldBase : IPluginField {
   public PluginFieldGraphStyle GraphStyle { get; }
   public PluginFieldNormalValueRange NormalRangeForWarning { get; }
   public PluginFieldNormalValueRange NormalRangeForCritical { get; }
+  public string? NegativeFieldName { get; }
 
 #pragma warning disable CA1033
   PluginFieldAttributes IPluginField.Attributes => new(
     label: Label,
     graphStyle: GraphStyle,
     normalRangeForWarning: NormalRangeForWarning,
-    normalRangeForCritical: NormalRangeForCritical
+    normalRangeForCritical: NormalRangeForCritical,
+    negativeFieldName: NegativeFieldName
   );
 #pragma warning restore CA1033
 
@@ -30,27 +32,47 @@ public abstract class PluginFieldBase : IPluginField {
     PluginFieldGraphStyle graphStyle = default,
     PluginFieldNormalValueRange normalRangeForWarning = default,
     PluginFieldNormalValueRange normalRangeForCritical = default
+  )
+    : this(
+      label: label,
+      name: name,
+      graphStyle: graphStyle,
+      normalRangeForWarning: normalRangeForWarning,
+      normalRangeForCritical: normalRangeForCritical,
+      negativeFieldName: null
+    )
+  {
+  }
+
+  protected PluginFieldBase(
+    string label,
+    string? name,
+    PluginFieldGraphStyle graphStyle,
+    PluginFieldNormalValueRange normalRangeForWarning,
+    PluginFieldNormalValueRange normalRangeForCritical,
+    string? negativeFieldName
   )
   {
     if (label is null)
       throw new ArgumentNullException(nameof(label));
     if (label.Length == 0)
       throw ExceptionUtils.CreateArgumentMustBeNonEmptyString(nameof(label));
-    if (!regexValidFieldLabel.IsMatch(label))
-      throw new ArgumentException($"'{label}' is invalid for field name. The value of {nameof(label)} must match the following regular expression: '{regexValidFieldLabel}'", nameof(label));
+    if (!RegexValidFieldLabel.IsMatch(label))
+      throw new ArgumentException($"'{label}' is invalid for field name. The value of {nameof(label)} must match the following regular expression: '{RegexValidFieldLabel}'", nameof(label));
 
     name ??= GetDefaultNameFromLabel(label);
 
     if (name.Length == 0)
       throw ExceptionUtils.CreateArgumentMustBeNonEmptyString(nameof(name));
-    if (!regexValidFieldName.IsMatch(name))
-      throw new ArgumentException($"'{name}' is invalid for field name. The value of {nameof(name)} must match the following regular expression: '{regexValidFieldName}'", nameof(name));
+    if (!RegexValidFieldName.IsMatch(name))
+      throw new ArgumentException($"'{name}' is invalid for field name. The value of {nameof(name)} must match the following regular expression: '{RegexValidFieldName}'", nameof(name));
 
     Label = label;
     Name = name;
     GraphStyle = graphStyle;
     NormalRangeForWarning = normalRangeForWarning;
     NormalRangeForCritical = normalRangeForCritical;
+    NegativeFieldName = negativeFieldName;
   }
 
   /// <summary>Gets a value for plugin field.</summary>
@@ -60,39 +82,41 @@ public abstract class PluginFieldBase : IPluginField {
 #pragma warning disable CA1033
   async ValueTask<string> IPluginField.GetFormattedValueStringAsync(CancellationToken cancellationToken)
   {
-    const string unknownValueString = "U";
+    const string UnknownValueString = "U";
 
     var value = await FetchValueAsync(cancellationToken).ConfigureAwait(false);
 
-    return value?.ToString(provider: null) ?? unknownValueString; // TODO: format specifier
+    return value?.ToString(provider: null) ?? UnknownValueString; // TODO: format specifier
   }
 #pragma warning restore CA1033
 
-  // http://guide.munin-monitoring.org/en/latest/reference/plugin.html#field-name-attributes
+  // https://guide.munin-monitoring.org/en/latest/reference/plugin.html#field-name-attributes
   // Field name attributes
   //   Attribute: {fieldname}.label
   //   Value: anything except # and \
-  private static readonly Regex regexValidFieldLabel = new(
+  private static readonly Regex RegexValidFieldLabel = new(
     pattern: $@"^[^{Regex.Escape("#\\")}]+$",
     options: RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant
   );
 
-  // http://guide.munin-monitoring.org/en/latest/reference/plugin.html#notes-on-field-names
+  // https://guide.munin-monitoring.org/en/latest/reference/plugin.html#notes-on-field-names
   // Notes on field names
   //   The characters must be [a-zA-Z0-9_], while the first character must be [a-zA-Z_].
-  private static readonly Regex regexValidFieldName = new(
+#pragma warning disable SYSLIB1045
+  private static readonly Regex RegexValidFieldName = new(
     pattern: $@"^[a-zA-Z_][a-zA-Z0-9_]*$",
     options: RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant
   );
 
-  private static readonly Regex regexInvalidFieldNamePrefix = new(
+  private static readonly Regex RegexInvalidFieldNamePrefix = new(
     pattern: $@"^[0-9_]+",
     options: RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant
   );
-  private static readonly Regex regexInvalidFieldNameChars = new(
+  private static readonly Regex RegexInvalidFieldNameChars = new(
     pattern: $@"[^a-zA-Z0-9_]",
     options: RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant
   );
+#pragma warning restore SYSLIB1045
 
   private static string GetDefaultNameFromLabel(string label)
   {
@@ -101,8 +125,8 @@ public abstract class PluginFieldBase : IPluginField {
     if (label.Length == 0)
       throw ExceptionUtils.CreateArgumentMustBeNonEmptyString(nameof(label));
 
-    return regexInvalidFieldNameChars.Replace(
-      regexInvalidFieldNamePrefix.Replace(label, string.Empty),
+    return RegexInvalidFieldNameChars.Replace(
+      RegexInvalidFieldNamePrefix.Replace(label, string.Empty),
       string.Empty
     );
   }
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldGraphStyle.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldGraphStyle.cs
index f561581..642796b 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldGraphStyle.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldGraphStyle.cs
@@ -6,7 +6,7 @@ namespace Smdn.Net.MuninPlugin;
 /// <summary>
 /// Represents the style of how the field should be drawn on the graph.
 /// </summary>
-/// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-draw">Plugin reference - Field name attributes - {fieldname}.draw</seealso>
+/// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-draw">Plugin reference - Field name attributes - {fieldname}.draw</seealso>
 public enum PluginFieldGraphStyle {
   Default = default,
 
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldNormalValueRange.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldNormalValueRange.cs
index bbc3c51..4b842c8 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldNormalValueRange.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginFieldNormalValueRange.cs
@@ -12,8 +12,8 @@ namespace Smdn.Net.MuninPlugin;
 /// With <c>{fieldname}.warning</c> or <c>{fieldname}.ciritical</c>, you can define the upper and lower limits of values that are considered to be warning or critical.
 /// This type defines the upper limit, lower limit, or range that is considered to be normal value.
 /// </remarks>
-/// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-critical">Plugin reference - Field name attributes - {fieldname}.critical</seealso>
-/// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-warning">Plugin reference - Field name attributes - {fieldname}.warning</seealso>
+/// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-critical">Plugin reference - Field name attributes - {fieldname}.critical</seealso>
+/// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#fieldname-warning">Plugin reference - Field name attributes - {fieldname}.warning</seealso>
 public readonly struct PluginFieldNormalValueRange {
   private static double ValidateValue(double val, string paramName)
   {
diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginGraphAttributes.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginGraphAttributes.cs
index 0331728..3e76d32 100644
--- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginGraphAttributes.cs
+++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/PluginGraphAttributes.cs
@@ -11,44 +11,44 @@ namespace Smdn.Net.MuninPlugin;
 /// Defines graph attributes that should be returned when the plugin is called with the 'config' argument.
 /// This type represents the collection of 'field name attributes'.
 /// </summary>
-/// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#global-attributes">Plugin reference - Global attributes</seealso>
+/// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#global-attributes">Plugin reference - Global attributes</seealso>
 public sealed class PluginGraphAttributes {
   /// <summary>Gets a value for the <c>graph_title</c>.</summary>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-title">Plugin reference - Global attributes - graph_title</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-title">Plugin reference - Global attributes - graph_title</seealso>
   public string Title { get; }
 
   /// <summary>Gets a value for the <c>graph_category</c>.</summary>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-category">Plugin reference - Global attributes - graph_category</seealso>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/graph-category.html">Plugin graph categories</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-category">Plugin reference - Global attributes - graph_category</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/graph-category.html">Plugin graph categories</seealso>
   public string Category { get; }
 
   /// <summary>Gets a value for the <c>graph_args</c>.</summary>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-args">Plugin reference - Global attributes - graph_args</seealso>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/example/graph/graph_args.html">Recommended graph_args</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-args">Plugin reference - Global attributes - graph_args</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/example/graph/graph_args.html">Recommended graph_args</seealso>
   public string Arguments { get; }
 
   /// <summary>Gets a value indicating whether the field value should be scaled. Represents the value for the <c>graph_scale</c>.</summary>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-scale">Plugin reference - Global attributes - graph_scale</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-scale">Plugin reference - Global attributes - graph_scale</seealso>
   public bool Scale { get; }
 
   /// <summary>Gets a value for the <c>graph_vlabel</c>.</summary>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-vlabel">Plugin reference - Global attributes - graph_vlabel</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-vlabel">Plugin reference - Global attributes - graph_vlabel</seealso>
   public string VerticalLabel { get; }
 
   /// <summary>Gets a value for the <c>update_rate</c>.</summary>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#update-rate">Plugin reference - Global attributes - update_rate</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#update-rate">Plugin reference - Global attributes - update_rate</seealso>
   public TimeSpan? UpdateRate { get; }
 
   /// <summary>Gets a value for the <c>graph_width</c>.</summary>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-width">Plugin reference - Global attributes - graph_width</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-width">Plugin reference - Global attributes - graph_width</seealso>
   public int? Width { get; }
 
   /// <summary>Gets a value for the <c>graph_height</c>.</summary>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-height">Plugin reference - Global attributes - graph_height</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-height">Plugin reference - Global attributes - graph_height</seealso>
   public int? Height { get; }
 
   /// <summary>Gets a value for the <c>graph_order</c>.</summary>
-  /// <seealso href="http://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-order">Plugin reference - Global attributes - graph_order</seealso>
+  /// <seealso href="https://guide.munin-monitoring.org/en/latest/reference/plugin.html#graph-order">Plugin reference - Global attributes - graph_order</seealso>
   public string? Order { get; }
 
   public PluginGraphAttributes(

Notes

Full Changelog: releases/Smdn.Net.MuninNode-1.2.0...releases/Smdn.Net.MuninNode-1.3.0