Confio 0.1.0-preview.2

Confio

独立的 .NET 配置文件读写组件,通过同一个 NuGet 包提供运行库与源生成器。源码和验证入口维护在项目仓库。

以 .NET 10 LTS 为主要目标,同时支持 .NET 6 / 7 / 8 / 9 和 Windows .NET Framework 4.6.2 至 4.8.1;.NET 8 / 9 / 10 支持 Native AOT。当前处于预发布开发阶段,已实现 JSON、YAML/YML、TOML 1.0、INI、模型默认值与业务校验、同步与异步读写、不可变模型更新、直接变更订阅、字段保护、普通 DI 和原生 Configuration / Options / Host 接入,尚未正式发布。保护内置 Windows DPAPI、指定密钥的 AES-GCM、macOS Keychain 和 Linux Secret Service 接入。完整范围与实测边界见设计方案。

当前维护四种格式共用的配置读写、数据语义、保护和文件可靠性,示例服务于这些核心行为的验证;通用集成深化、额外格式及更多平台适配按明确需求推进,具体顺序见实施安排。

安装

预览包 Confio 0.1.0-preview.2 通过 BaGet 分发,NuGet 源地址为 https://baget.pluscode.cn/v3/index.json。安装不需要发布 API key。

在应用仓库的 NuGet.Config 中配置包源;已有配置时合并对应节点。以下映射让 Confio 从 BaGet 获取,其余依赖从 nuget.org 获取:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="BaGet" value="https://baget.pluscode.cn/v3/index.json" />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
  <packageSourceMapping>
    <packageSource key="BaGet">
      <package pattern="Confio" />
    </packageSource>
    <packageSource key="nuget.org">
      <package pattern="*" />
    </packageSource>
  </packageSourceMapping>
</configuration>

在应用项目目录安装:

dotnet add package Confio --version 0.1.0-preview.2

也可以在项目文件中直接声明包引用。以下是可使用后文模型与读写代码的最小控制台项目:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <LangVersion>12.0</LangVersion>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Confio" Version="0.1.0-preview.2" />
  </ItemGroup>
</Project>

其他目标将 TargetFramework 改为下表中的对应值,同样保留 C# 12 设置。生成器和 Framework 编译期辅助类型随包提供。.NET 8 / 9 / 10 应用可直接发布 Native AOT,例如 Windows x64:

dotnet publish -c Release -r win-x64 -p:PublishAot=true

使用

应用声明模型和一个原生 JSON 上下文,Confio 自动生成配置节、默认构造、保护和类型服务声明。需要业务约束的模型实现 IValidatableSettings,规则只写一次,直接使用和 DI 共用。使用 SDK 风格项目、PackageReference 与 C# 12;当前验收工具链为 .NET 10 SDK,编译器版本不要求应用运行在 .NET 10。Confio 的生成器随 NuGet 包交付,应用无需额外安装生成器包。

应用目标 NuGet 自动选择 发布方式
.NET Framework 4.6.2 / 4.7 / 4.7.1 / 4.7.2 / 4.8 / 4.8.1 net462 Windows 托管应用
.NET 6 / 7 net6.0 JIT
.NET 8 / 9 net8.0 JIT、Native AOT
.NET 10 net10.0 JIT、Native AOT

BaGet 的 Dependencies 页面显示包内四个资产的依赖组,上表列出它们实际支持的应用版本。NuGet 会自动选择兼容资产;仅在底层 API 或依赖确有差异时增加目标资产,无需为每个消费版本重复打包。

低版本通过包依赖自动取得所需 JSON 静态元数据 API;Framework 的 AES-GCM 由微软官方兼容库提供,PolySharp 提供编译期语言辅助,使用 record/init 不必手写辅助类型。各目标共用以下模型和 API,不需要额外选择“兼容模式”或“AOT 包”。Framework 消费项目需显式设置 <LangVersion>12.0</LangVersion>;DateOnly 等仅在现代 .NET 存在的 BCL 类型不由 Confio 补造。

.NET 6 / 7 使用 JSON 8 与 Microsoft.Extensions 8,其他资产使用对应的 JSON 10 与 Microsoft.Extensions 10。Confio 自动补充共享读写所需的静态信息;JSON 9 才引入的 JsonStringEnumMemberNameAttribute 等原生新特性不由组件移植到 JSON 8,旧目标仍可使用原生字符串枚举转换。.NET 5 不纳入支持范围。

using Confio;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;

[SettingsSection("Mail")]
public sealed class MailSettings : IValidatableSettings
{
    public string Host { get; set; } = "localhost";
    public int Port { get; set; } = 587;

    [Protected]
    public string Password { get; set; } = "";

    public void Validate()
    {
        if (string.IsNullOrWhiteSpace(Host))
            throw new ValidationException("The mail host is required.");
        if (Port is < 1 or > 65535)
            throw new ValidationException("The mail port must be between 1 and 65535.");
    }
}

[SettingsSection("Retry")]
public sealed record RetrySettings : IValidatableSettings
{
    public int MaxAttempts { get; init; } = 3;

    public void Validate()
    {
        if (MaxAttempts < 1)
            throw new ValidationException("At least one retry attempt is required.");
    }
}

[JsonSerializable(typeof(MailSettings))]
[JsonSerializable(typeof(RetrySettings))]
public partial class AppSettingsContext : JsonSerializerContext
{
}

直接使用,以下代码放在方法体中:

using var config = new ConfigurationFile(
    AppSettingsContext.Default, applicationId: "Contoso.Mail");

var mail = config.Read<MailSettings>();
mail.Host = "smtp.example.com";
mail.Port = 465;
config.Save(mail);

同一实例提供 ReadAsync、SaveAsync、UpdateAsync、ResetAsync、ReloadAsync,均返回 Task 并接受取消令牌。同步与异步消费都使用普通 using,首次读写自动加载,不需要先初始化。读取返回独立副本;修改副本后显式保存,或用 Update / UpdateAsync 基于最新文件修改。

要做的事 使用入口 实际效果
读取当前生效配置 Read<T>() 首次加载并自动保护文件中的明文,之后从最近成功的内存配置返回副本;不会每次读取磁盘
保存一份完整编辑结果 Save(model) 完整替换目标模型节;旧草稿也会覆盖该节的新值
在最新配置上修改 Update<T>(edit) 在文件锁内读取磁盘最新值,执行一次修改并保存;应用不管理文件锁或重试
恢复某个模型的默认值 Reset<T>() 删除该节的持久化覆盖,采用模型默认值;例如默认端口是 587,恢复后就是 587
采用外部文件修改 Reload() 重新读取并自动保护声明位置的明文,成功后更新生效配置;失败保留之前成功的内存配置

保存、更新或重置保留其他配置节的数据与已有密文,并将其他所选声明位置的待保护明文合并到同一次提交;完整替换目标节时,该节内未被模型承载的未知属性不保证保留。JSON 输出保留中文等常用 Unicode 文本的可读性,引号、控制字符和换行仍按 JSON 规则转义;不承诺原注释、排版或 YAML 锚点写法保持不变。

Update 同时接受修改副本的 Action<T> 和返回新模型的 Func<T,T>。record/init 模型使用 with,文件协调和保存由组件完成:

await config.UpdateAsync<RetrySettings>(retry =>
    retry with { MaxAttempts = checked(retry.MaxAttempts + 1) });

业务校验在首次加载、重载和提交前执行,失败抛出 ValidationException,保留文件与最近的有效快照,不发出变化通知。已加载的普通读取不重复校验;Reset 也要求模型默认值有效。校验只读取模型,不做 I/O;消息描述规则,避免包含敏感值。它与原生 Options 的创建后校验有不同的执行时点,详见业务校验契约。

直接使用 config.OnChange<T>(listener),或在容器中调用 ISettings<T>.OnChange(listener),即可订阅共享文件的变化:

using var subscription = config.OnChange<MailSettings>(mail =>
    System.Console.WriteLine($"当前端口:{mail.Port}"));

订阅不加载文件、不立即回调。成功保存、重置和重载后,每个订阅者获得当前模型的独立副本;同文件其他节的提交也会通知。回调在锁外执行,不保证 UI 线程;释放返回值可退订,回调异常通过诊断报告,不回滚保存。Avalonia 示例已演示 UI 调度和退订。

OnChange 不会自动监听外部文件,外部编辑后仍需调用 Reload。格式损坏、业务校验失败、解密失败都会报告,不能统一重置为默认值;修正配置或密钥后可重新加载。备份、恢复与历史管理不属于当前产品范围。Sample 的恢复按钮只还原它自己制造损坏前保留的演示原文,具体见错误处理与功能边界。

通过 path 的扩展名选择格式;默认是 JSON,.yaml 与 .yml 等价。JSON、YAML、TOML、INI 使用相同的模型、上下文和读写 API;应用不增加逐格式的序列化声明:

using var config = new ConfigurationFile(
    AppSettingsContext.Default, applicationId: "Contoso.Mail", path: "appsettings.toml");

var mail = await config.ReadAsync<MailSettings>();
mail.Port = 465;
await config.SaveAsync(mail);
扩展名 可用范围与主要限制
.json 对象、数组、标量、null,允许注释与尾逗号输入
.yaml / .yml YAML 1.2 Core 的字符串键映射、数组、标量、null 与已完成锚点的别名;字符串 00123 需要加引号
.toml 表、数组、标量和原生日期;支持空容器、多行字符串,没有原生 null;原生整数为有符号 64 位,浮点为有限 binary64,保存不能往返的高精度数值会报错
.ini 原生 INI 行与节规则,用 : 表示层级,数组用从 0 开始的连续索引;本项目的固定方言不支持 null、空容器和多行值

INI 根据静态模型判断类型:Port=465 进入整数属性,Code=00123 进入字符串属性并保留前导零。常见数值、布尔、枚举和日期类型由组件处理;不能确定明文形态的自定义转换器在创建文件实例时拒绝,整体保护后的载荷不受这一限制。

格式限制针对实际写入数据检查,不会悄悄省略属性或改为空字符串。例如整体保护的非空子对象先变成一条密文,其内部可以含 null 或空集合;属性本身是 null 时,TOML / INI 仍无法保存。保存保留其他配置节的数据语义与密文,包括 TOML 原生日期与普通字符串的区别;注释与原排版不承诺保留。完整边界见格式约定。

模型可以声明可空属性,由实际保存的值决定是否超出格式范围。格式适配发现无法表达的值时抛出 ConfigurationValueException,提供 Format、Path 和 Reason;文件、旧快照与通知保持不变,应用不需要先做一轮格式检查。开发者可以直接看到具体位置与原因,例如:

TOML cannot persist null at '/Mail/Password'.
INI cannot persist an empty collection at '/Mail/Recipients'.

Path 使用 JSON Pointer,例如 /Mail/Recipients/0 指向第一个元素,路径段中的 ~、/ 分别写为 ~0、~1。界面可根据结构化属性生成自己的提示,无需解析异常正文或显示配置值。

容器入口也通过同一个 path 参数选择格式。四种格式共用静态 JSON 模型元数据;TOML 通过已知文件节点解析和输出,应用无需额外声明 TOML 上下文。所有目标统一接受 TOML 1.0,不接受 TOML 1.1 新增的 \e、\xHH 转义、多行内联表及省略秒的时间。遇到这些输入会报错并保留原文件与上一次成功的配置,JIT 与 Native AOT 使用同一路径。

普通容器只注册一次,应用服务直接注入 ISettings<MailSettings>,使用同样的类型读写方法:

services.AddConfigurationFile(
    AppSettingsContext.Default, applicationId: "Contoso.Mail");

文件和类型服务默认是单例,文件由根容器释放;需要复用外部实例时使用 services.AddConfigurationFile(config),仍由调用方释放。注册和解析服务不加载文件。完整示例见容器接入示例,详细语义见读写与容器契约。

需要原生 IConfiguration、IOptions<T>、IOptionsSnapshot<T> 或 IOptionsMonitor<T> 时,直接用构建入口完成预加载和注册,不再调用前面的普通注册。以下代码位于异步方法中:

using var provider = await services.BuildServiceProviderWithConfigurationFileAsync(
    AppSettingsContext.Default, applicationId: "Contoso.Mail");

var mail = provider.GetRequiredService<IOptionsMonitor<MailSettings>>().CurrentValue;

Host 使用原生构建器;需要只加载选定文件时,从空构建器开始。日志输出和业务服务按应用需要选择:

var builder = Host.CreateEmptyApplicationBuilder(null);
using var host = await builder.BuildWithConfigurationFileAsync(
    AppSettingsContext.Default, applicationId: "Contoso.Mail",
    configure: app =>
    {
        app.Logging.AddConfiguration(app.Configuration.GetSection("Logging")).AddConsole();
        app.Services.AddHostedService<MailWorker>();
    });

await host.RunAsync(cancellationToken);

上述原生类型分别来自 Microsoft.Extensions.DependencyInjection、Microsoft.Extensions.Configuration、Microsoft.Extensions.Options、Microsoft.Extensions.Hosting 和 Microsoft.Extensions.Logging;MailWorker 是应用自己的业务服务,可执行示例见容器接入示例。两个构建入口均有同步版本、上下文集合和已有文件重载;configure 在文件可读后执行。应用无需逐模型绑定或编写 Options 工厂,构建失败清理和自建文件释放由组件完成。

IConfiguration 保留原生配置源优先级。Confio 的默认命名 Options 从文件快照创建,再执行原生 Configure/PostConfigure/Validate;其他 Provider 的值不会自动进入该文件或这些 Options。保存、更新、重置和显式重载会刷新 IOptionsMonitor<T>,IOptions<T> 和已有作用域的快照保留原生缓存。编辑使用文件或 ISettings<T> 的副本;Provider 只读,外部文件修改通过 ConfigurationFile.Reload/ReloadAsync 获取。完整启动与所有权规则见原生读取接入。

省略 path 时,Windows 使用 %LocalAppData%/Confio/<applicationId>/appsettings.json。不存在的文件读取默认值,不落盘。标记 [Protected] 的字符串和子对象在保存时自动加密,首次加载或显式重载发现普通明文时自动加密写回;空字符串和 null 不调用密钥设施。

文件密文统一使用 enc:v1:<Base64> 字符串。文件输入中受保护位置的 enc: 是保留前缀,带该前缀的手写明文会按密文解析;版本、编码或认证失败会报错。通过模型 API 保存时,值始终按明文处理,即使密码本身以 enc:v1: 开头也能完整往返。读取已有密文不会重复加密写盘,未声明保护的普通字段不按前缀解密。

例如,手写一份普通明文配置:

{"Mail":{"Password":"smtp-password"}}

首次 Read 或显式 Reload 校验通过后,组件将 Password 自动替换为 enc:v1:<Base64> 密文,应用仍读到 smtp-password,无需额外调用保存。缺失的默认属性不会补写;需要保护而无法写回时加载失败,已有有效密文的文件可只读加载。四种格式使用相同规则。

默认使用系统用户保护:Windows 直接使用 DPAPI,现代 .NET 在 macOS 通过 Keychain 托管 AES 密钥,在 Linux 通过 Secret Service 托管 AES 密钥。Linux 默认保护需要 libsecret 0.19 或以上、用户 D-Bus 会话和可访问的 Secret Service;系统可能请求授权或解锁。已有用户反馈 macOS Sample 正常运行,专项平台验收范围见设计第 8.1 节。

服务器、Docker 或跨机器共享配置时,在同一个入口传入部署环境提供的稳定 32 字节随机密钥,即使用内置 AES-256-GCM;Windows、Linux、macOS 使用同一实现。应用无需创建保护器或管理算法参数:

using var config = new ConfigurationFile(
    AppSettingsContext.Default, "Contoso.Mail", encryptionKey: key);

// 普通 DI、原生 Configuration / Options 和 Host 构建入口同样支持 encryptionKey。
services.AddConfigurationFile(
    AppSettingsContext.Default, "Contoso.Mail", encryptionKey: key);

key 由应用的部署或凭据设施提供,不能每次启动重新生成,也不能与密文一起写入配置文件。构造或注册固定一份输入副本,文件实例管理内置保护器及其运行时密钥;高级设施通过 protector 参数传入 IConfigurationProtector,与 encryptionKey 互斥,外部实例始终由调用方管理。密钥服务不可用、密钥丢失及密文认证失败会报错并保留数据。跨机器解密需要相同密钥、应用标识和逻辑字段用途,默认系统保护不承诺文件可直接搬迁。

运行示例

ConfioSample 是 Avalonia 配置读写示例。首页提供四步引导,解释预期变化和对应 API;保存、恢复默认、读取和重新加载等常用按钮始终可见,无需先完成或收起引导。下方并排展示“你正在编辑、程序实际读到、磁盘实际保存”:程序值先用服务器、端口和密码状态展示,完整模型 JSON 按需展开。

从仓库根目录运行:

dotnet run --project samples/ConfioSample/ConfioSample.csproj -c Release

先依次点击引导中的四个按钮,无需准备配置文件:

  1. 填入示例值:左边端口变为 465,中间仍为默认值 587,右边说明文件尚未创建。
  2. 保存邮件配置:中间采用 465,右边出现真实文件,非空密码和整个凭据对象自动加密。保存失败会停在这一步,并保留输入和上次成功结果。
  3. 模拟外部修改:另一个配置实例将文件端口改为 2525,当前程序仍读到 465。
  4. 重新加载文件:程序和表单采用 2525。点击“继续自由探索”可收起引导。

每一步均可通过“查看模型与用法源码”查看实际模型声明。保存或“读取到表单”后,勾选密码旁的“显示”可核对解密后的原值;完整模型与操作记录默认隐藏敏感内容。可以随时收起引导,切到“重试”或“加密与失败演示”也会收起引导。“异步 API”默认勾选,取消勾选后操作和引导代码一起切换到同步入口。

完成引导后可按以下场景继续体验;容器功能放在独立的“进阶:容器接入”页。

界面操作 可观察的行为
顶部:打开目录 用系统文件管理器打开当前配置文件所在目录;尚未保存时只创建空目录,不生成配置文件,也不改变表单草稿
邮件:填入示例、保存 编辑不会改变程序已读取的值或文件;展开“集合与多行文本”可编辑收件人和多行备注
Save / Update 对照:新建文件并运行对照 两份草稿都只改端口,另一个实例修改文件中的主机;重新打开文件后,Save 读到草稿中的旧主机,Update 读到外部修改后的主机。使用两个独立文件,保留当前文件与草稿,可打开对照目录查看原文;同步和异步均可运行
密码与 API 凭据 Password 单字段保护,Credentials 子对象整体保护;快照保留模型结构并隐藏敏感内容,文件展示真实密文
重试:保存重试配置、次数 +1、恢复重试默认值 Retry 使用 record/init;通过 with 基于最新文件值更新,保存或重置一个配置节保留另一个节及其表单草稿
加密与失败演示:正常保存空字符串、enc:v1:plain 四种格式均可保存空字符串,它与 null 不同;前缀密码通过模型 API 保存后加密并完整还原
加密与失败演示:null JSON / YAML 标为“正常保存”;TOML / INI 单列为“预期失败”,点击“验证 null 保存失败”后显示 /Mail/Password 与不支持 null 的原因,保留原文件、旧配置、草稿和通知状态
格式边界:空收件人、多行备注 当前 INI 方言拒绝空集合与多行值,错误分别定位到 /Mail/Recipients、/Mail/Notes,保留文件和编辑草稿;JSON / YAML / TOML 支持这两种值。顶部始终显示当前格式的限制
加密与失败演示:新建手写明文文件 → 加载文件,观察自动加密 两个相邻按钮先展示虚构明文,再用 Reload 自动加密写回,无需 Save;再次重载不重写有效密文,之前的演示文件保留
加密与失败演示:验证空白主机保存失败 标为“预期失败”,调用真实保存并由模型拒绝;文件、快照与通知次数保留,日志记录失败
刷新观察、读取到表单、重新加载文件 刷新观察保留草稿且不重载;读取到表单用已有快照替换草稿;重新加载从文件更新快照
加密与失败演示:写入损坏内容、还原演示文件 重载损坏文件失败后仍可读旧快照;仅撤销这次演示制造的损坏,不代表运行库提供备份恢复
普通 DI:读取当前配置、保存邮件表单 业务服务注入 ISettings<T>,与窗口共享文件实例。读取已有快照,保留文件及未保存草稿;保存使用“读写与加密”页的邮件表单,执行校验、保护并展示变更通知。外部文件修改仍需显式重新加载
Options 对照与原生读取视图 用实际取值表格比较 Configuration、IOptions、原作用域与新作用域的 Snapshot、Monitor 及通知次数;下方展示已解密并脱敏的扁平读取投影
Host 启停 使用空 Host 构建器,加载时执行模型校验,按原生启动时序创建 Options,显示托管服务的启动、读取与停止结果
用法源码、操作记录 查看实际参与编译的模型、读写事件与容器代码;查看最近 100 条操作和直接通知,包含顺序、时间与安全的失败提示

两个配置节模型使用同一个 AppSettingsContext,ApiCredentials 作为 Mail 的受保护子对象。窗口事件直接调用正式 API,入门引导复用相同动作,容器场景按方法组织。窗口拥有文件实例,容器场景借用它并在操作结束后释放各自的根;关闭窗口会先取消并等待正在执行的操作。源码页随构建嵌入实际模型与消费代码,不维护另一份展示代码。示例不发送邮件或自动监听配置文件。UI 采用 Avalonia 12.1.2(MIT,提供 net10.0 资产),相关依赖仅由示例工程引用。

默认使用每次独立的临时 JSON 路径,首次读取默认值不创建文件,窗口顶部显示完整路径。展开“新建演示与设置”,选择 JSON / YAML / TOML / INI 后点击“新建演示”可重新开始,之前的文件会保留。基础示例使用四种格式均可表达的数据;默认凭据为含空字符串成员的对象,主动取消凭据勾选才表示 null。选择只影响下次新建,左上“当前”标签始终显示当前文件使用的格式和保护方式。也可传入一个尚不存在的文件路径,已有文件会被拒绝,以免重置演示覆盖用户数据:

dotnet run --project samples/ConfioSample/ConfioSample.csproj -c Release -- artifacts/sample-run/appsettings.yaml

示例启用 AOT 分析,关闭默认 JSON 反射;TOML 直接处理固定节点,不调用对象反射转换。Windows x64 可通过下列命令发布和运行 Native AOT,需要下文所述 C++ 工具链:

dotnet publish samples/ConfioSample/ConfioSample.csproj -c Release -r win-x64 -o artifacts/sample-aot
./artifacts/sample-aot/ConfioSample.exe

示例的新建演示入口可选择系统用户保护或 AES-GCM。AES-GCM 使用公开测试密钥,只处理示例创建的虚构数据;真实应用应从部署环境取得密钥。启动时传入 --aes 可直接选择该演示方式。

同一桌面程序提供 --verify 验收入口,依次触发可用按钮,断言同步/异步四步引导、失败不推进、可读摘要、JSON 展开与密码显示,以及集合、格式限制、字段及子对象保护、手写明文自动保护、前缀密码通过模型 API 往返、空值区别、校验失败、草稿隔离、不可变更新、通知、损坏文件恢复、独立文件中的 Save / Update 对照、DI / Options 作用域 / Host、源码资源、取消及新会话隔离。INI 会验证多行值与空集合失败,TOML / INI 会验证 null 失败;其他格式验证对应值往返。Save / Update 对照核实实际文件结果、当前文件与草稿保留,以及当前格式和保护选择不受下次新建设置影响。保护结果通过重新创建文件实例读取核对;除明确展示虚构手写文件的步骤外,观察区与日志检查敏感值没有泄漏。完成后自动退出;需要桌面会话及所选保护设施。省略路径时使用新的临时目录,也可在 --verify 后传入一个尚不存在的 .json、.yaml、.toml 或 .ini 路径:

dotnet run --project samples/ConfioSample/ConfioSample.csproj -c Release -- --verify
./artifacts/sample-aot/ConfioSample.exe --verify --aes

成功返回 0,在初始配置路径旁生成 .verification.txt 和默认值、引导关键步骤、完整模型、读写、集合、子对象、边界失败、明文自动保护前后、Save / Update 对照、容器、源码、日志及较小窗口的 PNG 截图;失败返回非零退出码。最小窗口检查引导展开与收起时编辑区及观察区的可用高度,内容较多时可独立滚动。UI 入口用于理解和验证消费体验,运行库的并发、保护认证失败及其他复杂边界仍由行为测试、格式与操作顺序测试、保护测试、明文保护测试、原生接入测试和真实消费者验收。桌面运行当前仅在 Windows x64 验证,运行库平台证据单独维护。

构建与验证

本仓库使用 .NET 10 SDK 构建。运行库与模型类库面向 net462;net6.0;net8.0;net10.0,真实消费者逐一以表中六个 Framework 目标及 .NET 6 / 7 / 8 / 9 / 10 验证资产选择与行为,Avalonia Sample 保持 .NET 10。生成器的 netstandard2.0 只服务编译器加载,不交付 Standard 运行库。完整矩阵需安装相应运行时和 Framework targeting pack;本机 Framework 消费运行于 Windows x64、Framework 4.8.1 宿主。用户曾回传 Windows 7 SP1 x64 下原始 Framework 4.6.2 至 4.8 的完整消费者与密文互读日志;这些既有证据不等于本轮依赖及元数据改动已在原始宿主复验,实际边界见设计第 8.1 节。

Framework 使用 SDK 风格项目、PackageReference 和 C# 12。SDK 自动处理 .NET Standard 依赖所需的兼容程序集与绑定重定向,包传递 record/init 所需的编译期辅助类型,应用无需手写兼容源码。Framework 4.6 / 4.6.1 及 .NET 5 不在当前范围。.NET 6 / 7 已结束官方支持,构建时保留 SDK 生命周期提示;依赖选择遵守其目标支持范围,不通过压制依赖目标警告或 AOT 警告扩大承诺。

已发布的 BaGet 包另在 WSL 2 的 Ubuntu 24.04 x64、ext4 文件系统中完成 .NET 8 / 9 / 10 的 JIT 与 Native AOT 完整消费,覆盖四种格式、系统保护、原生集成与多进程更新,AOT 无裁剪或动态代码警告。Linux Secret Service 的建钥、重启、缺钥、服务不可用和实际等待取消,以及 Windows / Linux 的 AES 密文双向互读均已复验。该结果覆盖上述 Linux 环境,桌面授权交互、其他文件系统与架构的边界见设计第 8.1 节。

完整行为测试包含 Windows DPAPI 场景,在 Windows x64 运行;独立保护测试可跨平台运行。现代 .NET 完整消费者使用当前系统的默认保护,Linux 上需准备可访问的 Secret Service;指定密钥的消费者不依赖桌面密钥设施:

dotnet run --project tests/ConfioConsumer/ConfioConsumer.csproj -c Release -f net10.0 -- encryption
dotnet test tests/ConfioTests/ConfioTests.csproj -c Release --filter FullyQualifiedName~ProtectionTests
dotnet build Confio.sln -c Release
dotnet test tests/ConfioTests/ConfioTests.csproj -c Release --no-build
dotnet run --project tests/ConfioConsumer/ConfioConsumer.csproj -c Release -f net462 --no-build
dotnet run --project tests/ConfioConsumer/ConfioConsumer.csproj -c Release -f net6.0 --no-build
dotnet run --project tests/ConfioConsumer/ConfioConsumer.csproj -c Release -f net7.0 --no-build
dotnet run --project tests/ConfioConsumer/ConfioConsumer.csproj -c Release -f net8.0 --no-build
dotnet run --project tests/ConfioConsumer/ConfioConsumer.csproj -c Release -f net9.0 --no-build
dotnet run --project tests/ConfioConsumer/ConfioConsumer.csproj -c Release -f net10.0 --no-build

候选包验收脚本建立独立 feed、缓存和仅消费候选包的工程,检查四个运行库资产与依赖组、十一种应用目标的资产选择、跨项目模型及生成器交付。消费路径覆盖四种格式、同步异步读写、校验、record/init 默认值与更新、通知、DI / Configuration / Options / Host、保护与多进程协调;还验证 Framework 与现代 .NET 双向读取同密钥 AES 密文。加上 -NativeAot 后发布并运行 .NET 8 / 9 / 10 的 Windows x64 Native AOT,需要安装 Visual Studio C++ 桌面工具和 Windows SDK:

./eng/Verify-Package.ps1 -NativeAot

指定 -Source 时验证该源中与 Directory.Build.props 版本一致的现有包,不重新打包。脚本仍使用独立缓存和包源映射,并检查实际下载来源;源可以是本地目录或 NuGet 服务地址:

./eng/Verify-Package.ps1 -Source https://baget.pluscode.cn/v3/index.json -NativeAot

源码消费者也可以单独发布;该验证项目在 .NET 8 / 9 / 10 指定 RID 时启用 Native AOT:

dotnet publish tests/ConfioConsumer/ConfioConsumer.csproj -c Release -f net10.0 -r win-x64 -o artifacts/aot/net10.0
./artifacts/aot/net10.0/ConfioConsumer.exe

消费者和测试默认使用隔离临时目录,失败返回非零退出码。候选包验收在本次产物目录下执行文件读写,以覆盖工作区所在文件系统;候选包与发布结果位于 artifacts/,不提交 Git。Framework 的禁止反射开关由测试程序的 App.config 设置,现代 .NET 使用运行时配置;应用无需设置这些验收开关。矩阵之外的运行时按明确需求再扩展,平台证据见验收状态。

真实消费者可以在指定文件系统上验收,始终在给定根目录下新建独立子目录,结束时只清理本次子目录。该入口同时验证四种格式的同步异步保存和多进程更新,并在 JSON / YAML 路径验证连续替换、中文与保护值、已有读取句柄:

dotnet run --project tests/ConfioConsumer/ConfioConsumer.csproj -c Release -f net10.0 -- --directory ./artifacts/file-verification

Native AOT 消费者支持同样的 --directory 参数。文件提交对 Windows 短暂占用做有上限的等待,持续失败仍报告错误;重试与取消边界见文件提交契约。

发布预览包

版本由 Directory.Build.props 维护。上传已经验收的同一份 .nupkg,已发布版本不覆盖;下次修改使用新版本。发布密钥从 BAGET_API_KEY 环境变量读取,不写入仓库或 NuGet.Config。

dotnet pack src/Confio/Confio.csproj -c Release -o ./artifacts/packages
./eng/Verify-Package.ps1 -Source ./artifacts/packages -NativeAot
dotnet nuget push ./artifacts/packages/Confio.0.1.0-preview.2.nupkg --source https://baget.pluscode.cn/v3/index.json --api-key $env:BAGET_API_KEY --no-symbols

发布后使用上面的远端包验证命令确认实际安装与消费结果。当前仅通过 BaGet 分发预览版;nuget.org 的包名归属和公开许可证在对外发布前确定。

文档入口

文档 内容
设计方案 产品契约、技术取舍、目标矩阵、消费方式与验收状态
开发规范 工程、命名与 API、数据安全、生成器、AOT 和交付规则
AGENTS.md 项目边界、工作入口与协作约束

Showing the top 20 packages that depend on Confio.

Packages Downloads
Zeek.Core
Package Description
4
Zeek.Core
Package Description
3

Version Downloads Last updated
2.0.0 4 09/24/2026
1.1.0 2 09/18/2026
1.0.0 2 09/18/2026
0.1.0-preview.3 3 09/17/2026
0.1.0-preview.2 2 09/17/2026
0.1.0-preview.1 4 09/17/2026