ar-io/ar-io-mlflow

GitHub: ar-io/ar-io-mlflow

为 MLflow 生命周期提供可验证的机器学习来源追踪插件,将训练、注册和推理过程的加密签名证明锚定到 Arweave 永久存储。

Stars: 0 | Forks: 0

# ar-io-mlflow 为 MLflow 生命周期——训练、注册、晋升、推理——提供可验证的来源。 签名加密证明被锚定到 ar.io,因此即使在您的 MLflow 服务器消失很久之后,审计人员依然可以验证模型 或决策。 ## 安装 ``` pip install ar-io-mlflow ``` 或者,为了跟踪 main 分支: ``` git clone https://github.com/ar-io/ar-io-mlflow.git cd ar-io-mlflow pip install -e . ``` Python 3.10+。支持 MLflow 2.14+ 和 3.x(边界版本 2.14、2.22、3.0、3.12 在 CI 中运行)。引入 MLflow、PyNaCl、ar.io Turbo SDK 和 `cryptography`。 ### MLflow 版本兼容性 **MLflow 2.x (2.14+) 和 3.x 均完全受支持** —— 每个 plugin 流程 (训练锚定、`ArioMlflowClient` 注册/晋升、`VerifiedModel` 加载 + 预测、带有实时事实来源重新获取的 `verify_record`、数据集 锚定)都在真实的 MLflow 2.22 和 3.12 上进行了集成测试;CI 在两个 主要版本上都运行了门控检查。该 plugin 双向处理了 API 表面的差异: 仅限 v3 的更改(废弃的 `mlflow.log-model.history` 标签 -> 模型 通过 `run.outputs.model_outputs` 解析;更轻量的 `_tracing_client.get_trace_info` 用于 trace 标签重新获取)**以及**仅限 v2 的 模式(顶层的 `mlflow.get_active_trace_id` / `mlflow.set_trace_tag` 在 2.x 上不存在 —— predict 路径回退到活动 span 的 `request_id` 并通过 `MlflowClient.set_trace_tag` 写入 trace 标签)。 注意:MLflow 的文件系统跟踪存储(`./mlruns`,默认值)在 上游已于 2026 年 2 月被废弃 —— 对于新设置,建议使用 `sqlite:///...` 后端。有关已验证行为矩阵和 v3 支持历史,请参阅 [`docs/mlflow-v3-support.md`](docs/mlflow-v3-support.md)。 ## 快速开始 ``` import mlflow from sklearn.linear_model import LogisticRegression from sklearn.datasets import load_iris import ario_mlflow # 将 MLflow 指向一个 tracking store。如果 MLFLOW_TRACKING_URI 已经 # 在你的 env 中设置,或者你对 cwd 的 ./mlruns 感到满意,则跳过。 mlflow.set_tracking_uri("file:///tmp/mlruns") X, y = load_iris(return_X_y=True) with mlflow.start_run(): model = LogisticRegression(max_iter=200).fit(X, y) mlflow.log_metric("accuracy", model.score(X, y)) mlflow.sklearn.log_model(model, name="model") # Signs a proof, hashes the logged artifacts, writes ario.* tags, # and uploads ~500 bytes to Arweave via Turbo (free for small payloads). # allow_empty_dataset_inputs=True opts out of dataset anchoring; see # "Dataset anchoring" below for the recommended pattern. result = ario_mlflow.anchor(allow_empty_dataset_inputs=True) print(result["tags"]["ario.training_tx"]) ``` 没有配置 wallet?该 plugin 会在首次运行时自动生成一个,并将其持久化 到 `~/.ario-mlflow/wallet.json`,以便您的签名地址在不同 会话之间保持稳定。设置 `ARIO_MLFLOW_ARWEAVE_WALLET=/path/to/wallet.json` 以使用您自己的 wallet。 自动生成的 wallet 初始未充值 —— 这对于日常使用来说没问题, 因为 Turbo 的免费额度涵盖了小型上传(请参阅下文的“Wallet 与成本”)。 一个完整可运行的示例位于 `examples/sklearn-quickstart/` 中。 ## 三个集成点 ### 1. `ario_mlflow.anchor()` —— 训练来源 在记录您的模型后,于活动的 `mlflow.start_run()` 内部调用。该 plugin 会从 MLflow 的 log-model 历史记录中自动解析已记录模型的 `artifact_path`, 因此您几乎不需要显式传递它。 返回一个包含 `envelope`、`payload`、`payload_bytes`、`payload_hash`、 `previous_hash`、`anchor_result`、`tags`、`artifact_path`、`artifact_status` (`"hashed"` / `"no_artifacts"` / `"hash_failed"`)和 `artifact_error` 的 dict。 **失败模式。** `anchor()` 是同步的,并在 `with` 块退出之前运行完成。 - **Arweave 上传失败**(gateway 宕机,网络问题):envelope 依然会在 本地签名,并且 `ario.verify_status` 会被设置为 `signed`;`ario.training_tx` 不存在。您的 MLflow run 依然会成功。稍后重新运行以重试。 如果您传递了一个显式的 `arweave=` 实例,底层的 `ArweaveAnchor.last_error` 属性会携带原因供您检查。 - **artifact 哈希失败**(尚未记录 artifact,store 无法访问): 引发 `ario_mlflow.anchoring.ArtifactAccessError`。如果您想记录并继续,请包装该调用。 - **调用者提供的 wallet 缺失或格式错误**(当您构建自己的 `ArweaveAnchor(wallet_path=...)` 并将其作为 `arweave=` 传递时):从构造函数引发 `ario_mlflow.WalletLoadError` —— 操作员意图绝不能被在不同链上身份下自动生成的 wallet 静默覆盖。传递 `wallet_path=None`(或省略该参数)以使用自动生成的默认值。 - **没有活动的 run**:引发 `RuntimeError`。该函数需要一个活动的 `mlflow.start_run()` 块。 ### 2. `ario_mlflow.ArioMlflowClient` —— 注册 + 晋升 `mlflow.tracking.MlflowClient` 的直接替代品。注册和阶段 晋升会在后台线程中自动锚定。通过 client 查询结果: ``` from ario_mlflow import ArioMlflowClient client = ArioMlflowClient() mv = client.create_model_version("credit-scorer", "runs://model") # 阻塞直到 async anchor 完成(可选): client.wait_for_anchor("registration", "credit-scorer", mv.version, timeout=30) status = client.anchor_status("registration", "credit-scorer", mv.version) # {"status": "anchored", "tx_id": "...", "error": None, "done": True} ``` **失败模式。** 注册和晋升都会立即返回它们的 MLflow `ModelVersion`;锚定在 daemon 线程中运行。 - MLflow 操作始终独立成功 —— 锚定失败永远不会破坏 `create_model_version()` 或 `transition_model_version_stage()`。 - `anchor_status()` 返回 `{"status": ...}`,其中 status 是 `anchoring`(进行中)、`anchored`(Arweave 上传成功)、`signed` (envelope 已签名但 Arweave 上传失败)、`failed`(锚定崩溃 —— 参见 `error`)或 `unknown`(该 key 没有排队的 anchor)之一。 - `wait_for_anchor()` 在超时时返回 `False`。在 daemon 完成之前退出进程是没有问题的 —— daemon 在设计上是非阻塞的。 ### 3. `ario_mlflow.VerifiedModel` —— 推理 用一个完整性检查包装已注册的模型,该检查在 底层 pyfunc 模型被加载**之前**运行(因此被篡改的 artifact 永远不会有机会执行用户代码): ``` from ario_mlflow import VerifiedModel vm = VerifiedModel("models:/credit-scorer/1") # raises IntegrityError on hash mismatch # Features,顺序如下:annual_income, credit_utilization, debt_to_income_ratio, # months_employed, credit_score。 result = vm.predict([78000, 0.18, 0.22, 72, 745]) print(result.decision_id, result.proof_status) # "anchoring" → "anchored" # 如果你想同步获取 TX,请等待后台 anchor: result.wait_for_anchor(timeout=10) print(result.tx_id, result.anchor_error) ``` **失败模式。** - **被篡改的模型 artifact** —— `VerifiedModel(model_uri)` 会在底层 pyfunc 模型被加载*之前*引发 `ario_mlflow.IntegrityError`,因此被替换的模型永远不会有机会执行用户代码。捕获此异常以提醒您的安全运营部门,而不是静默失败放行。 - **`predict()` 总是返回**模型输出,即使后续锚定失败也是如此。 检查 `result.proof_status`:`anchoring`(进行中)、`anchored` (Arweave 上传成功)、`failed`(参见 `result.anchor_error`)或 `disabled`(没有 wallet / Turbo 不可用)。 - **尚无注册的模型 TX** —— 预测链接到模型版本的 `ario.registration_tx`。如果 `ArioMlflowClient` 的注册 daemon 尚未完成,前几次预测会链接到 `GENESIS`(在模型初始化时读取一次;在每次预测调用时绝不会重新读取注册 TX —— 这避免了竞态)。 ## 数据集锚定 每个 MLflow 数据集都可以有自己的签名 Arweave 证明,独立于任何 特定的训练 run。适用于: - **审计人员**,他们需要证明“此数据集在时间 T 由 X 签名存在”, 而不依赖于特定的模型 run。 - **数据集发布者**,锚定一次并将 TX 交给下游模型 训练者。 - **合规性**(例如欧盟 AI 法案第 53 条 GPAI 训练数据摘要),期望的是数据集级别的 artifact,而不是模型证明内部的片段。 两种使用方式: ``` import mlflow import ario_mlflow ds = mlflow.data.from_pandas(df, source="s3://bucket/train_q1.parquet", name="train_q1") # A) Implicit — 在 training 内部自动 anchored(推荐常规使用) with mlflow.start_run(): mlflow.log_input(ds, context="training") model.fit(...) mlflow.sklearn.log_model(model, "model") ario_mlflow.anchor() # Each logged dataset gets its own Arweave TX automatically; # the training proof references each by TX. # B) Explicit — publisher pattern,不需要 MLflow run result = ario_mlflow.anchor(dataset=ds) print(result["tx_id"]) # standalone dataset proof, hand off to downstream ``` 独立数据集 envelope 承诺数据集的名称、source URI、 digest 和 schema hash —— 而不是它的行。数据集保持私有;该 承诺是可移植的。 ## Wallet 与成本 每个锚定的事件都是一个约 500 字节的签名承诺(由该 plugin 的冒烟测试限制在 400-700 字节)。**Turbo 的免费层级涵盖小于 105 KiB 的上传**, 因此典型使用是免费的 —— 自动生成的 wallet 在零余额下即可开箱即用,大多数团队从不需要为其充值。 上传/资金 wallet 默认为 **Solana** (ed25519),作为 Solana CLI 的 `id.json` 持久化在 `~/.ario-mlflow/wallet.json` 中。**Arweave RSA wallet 依然有效** —— 链会根据 key 的形状自动检测,因此您已经在使用的 wallet 将保持不变继续工作。无论哪种方式,锚定目的地都是通过 Turbo 到达 Arweave;只有资金/签名者链不同。 只有在您达到 Turbo 的每账户免费额度限制或锚定更大的 payload 时,您才需要为 wallet 充值。充值方式: - 访问 [console.ar.io](https://console.ar.io) —— 使用信用卡或加密货币为 plugin 首次使用时记录的 wallet 地址充值 (`wallet:
, mode=persistent`)。 **对于生产部署**,生成一个专用的 wallet(不要依赖自动生成的 wallet),将 `ARIO_MLFLOW_ARWEAVE_WALLET` 指向它(Solana `id.json` / base58 secret **或** Arweave RSA JWK —— 链自动检测),并像对待任何其他生产密钥一样对待该 wallet。源数据(params、metrics、artifact 字节)始终保留在 MLflow 中 —— 没有其他任何东西会进入链上 —— 因此无论您的训练 run 有多大,成本都是固定的。 **规模化使用。** 每个事件都是一次上传,因此成本随锚定数量线性增长,而不是随模型大小增长。一个锚定每次预测的高吞吐量推理服务,每次调用都是一次约 500 字节的上传 —— 远低于单文件免费阈值。账户级别的限制和任何付费层级的费率记录在 [console.ar.io](https://console.ar.io) 和 [ardrive.io Turbo 文档](https://docs.ardrive.io)中;在正式上线之前,请根据当前费率对您的预计使用量进行建模。有关 wallet 运维、监控和余额警报,另请参阅 [`docs/plugin-production.md`](docs/plugin-production.md)。 ## 网络要求 如果您的环境限制了出站流量,请将以下地址加入白名单: | Host | 用途 | |---|---| | `turbo-gateway.com` | 上传(Turbo bundler)和证明获取 | | `arweave.net` *或其他 ar.io gateways* | 证明获取(备用) | | `turbo.ardrive.io` | TX bundler 状态检查 | | 您配置的 `ARIO_MLFLOW_ARIO_VERIFY_URL` | 可选的 ar.io Verify 证明 | 如果您想通过特定的 gateway 运营商进行路由,请使用 `ARIO_MLFLOW_GATEWAY_HOST` 覆盖上传/获取的主机。 ## 性能 阻塞操作与后台运行的操作对比: | 调用 | 行为 | |---|---| | `anchor()` | **同步。** 对 artifact 进行哈希、签名、上传到 Turbo 后返回。通常端到端需要几秒钟;如果 artifact 哈希较大,则需要更长的时间。 | | `ArioMlflowClient.create_model_version()` / `transition_model_version_stage()` | **立即返回**,在 daemon 线程中进行锚定。如果您在继续执行之前需要 TX,请使用 `wait_for_anchor()`。 | | `VerifiedModel.__init__` | **同步。** 重新对 artifact 进行哈希,与 `ario.artifact_hash` 进行比较,如果不匹配则引发 `IntegrityError`。每次模型加载产生一次开销。 | | `VerifiedModel.predict()` | **立即返回**预测结果;锚定在 daemon 线程中运行。锚定不会增加每次预测的延迟。 | 对于高吞吐量推理,predict 路径是热点 —— 只要模型产生输出,预测就会 立即返回。Arweave 上传异步进行,并在完成时写回到 trace 标签中。 ## 弹性 该 plugin 的 HTTP 层旨在吸收瞬时的 ar.io gateway 不稳定状况,而不会冒泡成为用户可见的失败。 - **对瞬时失败进行重试。** 所有上传、获取和 ar.io Verify 请求共享一个带有 `urllib3` Retry 适配器的 `requests.Session`: HTTP 5xx 和 429 响应会通过指数退避策略进行重试 (默认:2 次重试,0.5s/1.0s 等待,遵循 `Retry-After`)。除 429 外的 4xx 响应不会被重试 —— 它们属于硬失败。 可通过 `ArweaveAnchor` 和 `ArioVerifyClient` 构造函数上的 `max_retries` 和 `retry_backoff_factor` kwargs 进行调整。 - **多 gateway 获取回退。** `ArweaveAnchor.fetch_proof()` 按顺序遍历 `self.gateways`:如果其中一个发生瞬时失败,会自动尝试下一个。默认列表为 `["turbo-gateway.com", "ardrive.net"]`;通过 `gateways=` kwarg 或 `ARIO_MLFLOW_GATEWAYS` 环境变量进行覆盖。单个不稳定的 不再会在任何验证器 UI 中显示为硬性的“Proof Found” FAIL。 - **通过 `last_error` 进行故障自省。** 当 `upload_proof()`、 `fetch_proof()` 或 `ArioVerifyClient.submit_verification()` 返回 `None` 时,实例的 `last_error` 属性会携带一个 描述原因的字符串 —— gateway 宕机、重试耗尽、响应 body 无法解析等。无需解析日志即可区分“anchor disabled”和 “我们尝试的一切都失败了”。 - **证明级别的轮询。** `ArioVerifyClient.poll_attestation(tx_id, target_level=2, timeout=120, interval=5)` 重复提交 验证请求,直到达到所需的证明级别 或超时为止。无论哪种方式,都会返回最新结果(以便 调用者可以呈现“级别 1,仍在传播”的状态,而不是 什么都没有)。当您想在完全成熟之前显示 Verified 徽章时非常有用。 ## 环境变量 | 变量 | 用途 | 默认值 | |---|---|---| | `ARIO_MLFLOW_ARWEAVE_WALLET` | 资金 wallet key 的路径 —— 一个 Solana `id.json` / base58 secret **或** Arweave RSA JWK(根据 key 形状自动检测链) | 自动生成一个 **Solana** wallet + 持久化到 `~/.ario-mlflow/wallet.json` | | `ARIO_MLFLOW_GATEWAY_HOST` | 返回 URL 中使用的主要 ar.io gateway | `turbo-gateway.com` | | `ARIO_MLFLOW_GATEWAYS` | 获取失败时按顺序尝试的 ar.io gateways 的逗号分隔列表(例如 `g1.com,g2.com`) | primary + `ardrive.net` 备用 | | `ARIO_MLFLOW_SIGNING_KEY` | Base64 编码的 Ed25519 seed | 自动生成在 `~/.ario-mlflow/keys/` | | `ARIO_MLFLOW_ARIO_VERIFY_URL` | ar.io Verify REST API 基础 URL —— 例如 `https://perma.online/local/verify`(一个 ar.io 运营商的 Verify 端点) | 如果未设置,则禁用 ar.io 证明 | ## Plugin 写入的标签 在训练 run (`anchor()`) 上: - `ario.enabled`, `ario.version` —— 通过注册的 `RunContextProvider` - `ario.public_key`, `ario.verify_status`, `ario.artifact_hash` - `ario.payload_hash` —— 规范 payload 字节的 SHA-256(envelope 中提交的相同哈希) - `ario.training_tx`, `ario.arweave_url` —— 当 Arweave 上传成功时 - `ario.wallet_mode` —— `user-configured` / `persistent` / `ephemeral` 在注册模型上(链头,由 `anchor()` 写入): - `ario.last_training_hash` —— 指向此注册模型最近一次训练证明的指针;下一次训练读取它以设置其 `previous_hash` 在模型版本上 (`ArioMlflowClient`): - `ario.artifact_verified` —— 来自注册时重新哈希的 `true` / `false` - `ario.registration_tx`, `ario.promotion_tx`, `ario.arweave_url` 运行 `ar-io-mlflow verify …` 之后(训练 run 或模型版本): - `ario.verify_status` -> `verified` - `ario.attestation_level` —— `1`、`2` 或 `3`(参见下文的级别部分) - `ario.report_url` —— 指向此证明的 ar.io Verify 面板的链接 - `ario.attested_by`, `ario.attested_at` —— gateway 运营商和时间戳, 仅在运营商配置了签名 wallet 时出现 在 `VerifiedModel.predict()` 发出的 `@mlflow.trace` span 上: - `ario.payload_json` —— 完整的规范 payload(`ario/predictions//payload.json` artifact 的镜像)。由 `verify_source_of_truth` 读取,作为预测检查 3 的第二个 MLflow 表面。 - `ario.decision_id`, `ario.model_name`, `ario.model_version` - `ario.input_hash`, `ario.output_hash`, `ario.payload_hash` - `ario.proof_status`, `ario.prediction_tx`, `ario.arweave_url` - `ario.artifact_verified`(已知时) ## CLI ``` ar-io-mlflow verify run # verify training proof ar-io-mlflow verify model / # verify registration proof ar-io-mlflow verify trace # verify an inference proof ar-io-mlflow audit / # full model-lineage audit (terminal) ar-io-mlflow audit / --format=json # machine-readable evidence bundle ar-io-mlflow audit / --format=json --output lineage.json ``` `audit --format=json` 输出一个 `ario.mlflow.audit/v1` 证据包 —— 训练 -> 注册 -> 晋升 -> artifact-完整性,每个都带有每项检查结果和一个 `overall_ok` —— 用于 SOC2 / ISO 27001 证据。它是与 agent 的 `ariod audit export` 对应的模型谱系。JSON 模式可直接通过管道传输(无终端面板);`--output` 写入文件。 CLI 读取 `MLFLOW_TRACKING_URI`(默认为 `./mlruns`)—— 将其导出以指向 您在训练时使用的同一个 store,否则 run 查找将 失败并显示 `Run '' not found`。设置 `ARIO_MLFLOW_ARIO_VERIFY_URL` 以启用 可选的 ar.io 证明行。 所有 `verify` 命令运行相同的三行验证流程加上可选的 ar.io 证明: 1. **Proof Found** —— 为记录的 TX ID 从 ar.io 获取纯承诺 envelope。 2. **Decision / Training / Registration Record Matches** —— 从 MLflow 下载 `ario/payload.json`,重新哈希,与 envelope 的 `payload_hash` 进行比较,**并且**从*单独的*实时 MLflow 表面重新推导出规范字节,并与锚定的 payload 进行比较。这可以捕获 MLflow 篡改 —— 如果任何一个表面在锚定后被修改,两者将无法匹配。 - `verify run` (`Training Record Matches`) 重新获取 `run.data.params/metrics/artifact_checksums`。 - `verify model` (`Registration Record Matches`) 从源 run 重新推导 artifact 验证状态。 - `verify trace` (`Decision Record Matches`) 重新获取 `ario.payload_json` trace 标签(在写入时由 `VerifiedModel.predict` 镜像)并与 artifact 进行比较。 3. **Signature Confirmed** —— envelope 上的签名针对内嵌的 public key 验证通过。 加上一行 `Attested by` —— 由 ar.io gateway 运营商进行的独立第三方检查(当配置了 `ARIO_MLFLOW_ARIO_VERIFY_URL` 时)。 结果会被写回到 MLflow 标签中,并且 HTML 报告会被重新生成。 如果 MLflow 保留策略已经修剪了预测的 trace,第 2 行会返回 `reason=live_refetch_incomplete` 而不是静默通过 —— 证明 本身(签名 + 锚定的字节 + ar.io)位于永久存储上,并且 保持可验证状态。 ## 程序化验证 当您想在自己的代码内部进行验证时 —— 一个 CI gate、一个计划内的 重新验证作业、一个审计脚本 —— 直接调用 verify 函数, 而不是通过 shell 调用 CLI。三个复合入口点,全部 从顶层 `ario_mlflow` 包重新导出;根据您持有的内容以及 您是否具有 MLflow 访问权限进行选择: | 函数 | 使用时机 | MLflow 访问权限 | 运行实时 MLflow 检查? | |----------|----------|:---:|:---:| | `verify_record(envelope, canonical_bytes, …)` | 拥有可移植包(envelope + 规范字节)的**审计人员**,无运营商基础设施 | 否 | 否 | | `verify_proof_by_tx(tx_id, …)` | 仅拥有 TX ID 的**运营商** —— 从 Arweave 获取 envelope,然后进行验证 | 可选 | 是 | | `full_verify(envelope, …)` | 已经持有 envelope 的**运营商** | 可选 | 是 | ``` from mlflow.tracking import MlflowClient from ario_mlflow import verify_proof_by_tx from ario_mlflow.proof import ProofEngine # not a top-level export from ario_mlflow.arweave import ArweaveAnchor # not a top-level export result = verify_proof_by_tx( tx_id, # e.g. from the ario.training_tx tag anchor=ArweaveAnchor("", "turbo-gateway.com"), proof_engine=ProofEngine(), mlflow_client=MlflowClient(), # enables the anchored-bytes + live checks ) assert result["proof_found"] and result["overall"] is True ``` **仔细阅读结果 —— `ok` 是三态的。** 每次检查都会报告 `ok=True`(通过)、`ok=False`(已运行且失败)或 `ok=None`(未运行 / 不适用,附带一个 `reason`)。一个尖锐的边缘情况:对于训练、注册 和预测事件,MLflow 检查是*必需的*,因此调用 `full_verify` / `verify_proof_by_tx` **不带** `mlflow_client` 将返回 `overall=False`,即使在签名有效的情况下也是如此 —— 缺失的检查不能被解读为 通过。对于故意的离线/仅签名判定,请使用 `verify_record` (审计语义)或检查 `result["signature"]["ok"]`。 该 plugin 还验证由姊妹 [`ar-io-agent`](https://github.com/ar-io/ar-io-agent) daemon (`ario.agent/v1`)铸造的 envelope —— 两者共享 envelope 规范和加密技术。 有关完整参考,请参阅 [`docs/verification.md`](docs/verification.md): 每个结果字段、每个事件类型的细微差别、 `ario/verification.html` 报告、`spec_version` 处理以及可直接复制粘贴的 CI/监控作业。 ## ar.io 证明的含义 `ar-io-mlflow verify` 将 ar.io 证明报告为 `Verified` 或 `Pending verification`。只要 ar.io gateway 满足以下条件,证明即显示为 `Verified`: 1. 在 Arweave 上找到了永久存储的它。 2. 重新下载字节,并将 SHA-256 与 gateway 自己的 digest 匹配。 3. 针对原始签名者的 public key 验证了签名。 对于程序化调用者,`ario.attestation_level` 将相同的状态公开为 整数(1、2 或 3)—— 当您想区分“仍在传播”与“完全验证”时非常有用。 **运营商证明。** 当 ar.io gateway 运营商配置了 签名 wallet 时,验证结果本身即被签名,并且 `ario.attested_by` / `ario.attested_at` 会被写回到您的 MLflow 标签中。 那是来自已知运营商的独立声明,任何第三方都可以 针对他们的 public key 进行验证(标准 RSA-PSS SHA-256)。 这些证明涵盖了锚定记录的**完整性与真实性**。语义验证(即*此模型*是否在*此输入*上产生了*此输出*)是一个独立的问题,该 plugin 有意不解决 —— 设计上超出范围,而不是待定的功能。 ## 在没有 Python 的情况下进行验证 证明 envelope 规范是语言无关的:对经过 RFC-8785 (JCS) 规范化的 JSON 对象进行 Ed25519 签名,并带有对作为 MLflow artifact 存在的规范 payload 字节的 SHA-256 承诺。任何语言中的任何 RFC-8785 + Ed25519 + SHA-256 实现都可以验证证明 —— 无需 安装 `ar-io-mlflow`。 审计方法: 1. **获取 envelope** 从任何 ar.io gateway:`GET https:///raw/`。 2. **验证签名。** 从 envelope 中剥离 `signature` 字段, JCS 规范化其余部分 (RFC 8785),然后使用 Ed25519 针对内嵌的 `public_key`(十六进制)验证原始的 `signature`(十六进制)。 3. **重新对规范 payload 进行哈希。** 从 MLflow run 的 artifact 中下载 `ario/payload.json`。计算原始字节的 SHA-256。与 envelope 的 `payload_hash` 进行比较。 4. **检查 `spec_version`。** 接受您理解的 `ario.mlflow/v*` / `ario.agent/v*` 主要版本并拒绝未知的版本。在此字段存在之前锚定的 envelope 没有 `spec_version` 并且仍然可以验证(遗留) —— 参见 [`docs/architecture.md`](docs/architecture.md#pure-commitment-proofs-500-bytes-on-arweave)。 5. **遍历链**(可选)。每个 envelope 的 `previous_hash` 是 该事件类型的前一个 anchor 的 `payload_hash`,或 `"GENESIS"`。 通过其 TX(记录在相关标签中, 例如 `ario.last_training_hash`)获取前身并进行递归。 JCS 实现存在于 Python (`jcs`)、JavaScript (`canonicalize`)、 Go (`gowebpki/jcs`)、Java、Rust 等中 —— 与 Notary 和 Sigstore 处于同一生态系统,可互操作。 Python plugin 是围绕此配方的便捷包装器;证明 本身不依赖于该 plugin 的持续存在。 ## 测试 ``` pytest tests/test_plugin_smoke.py tests/test_plugin_verify.py tests/test_input_anchoring.py ``` 无需网络或 MLflow 服务器。 ## 相关文档 - [`CHANGELOG.md`](CHANGELOG.md) —— 发布历史和已知限制。 - [`docs/verification.md`](docs/verification.md) —— 完整的验证参考:四项检查、编程 API、结果形态、HTML 报告以及 CI/监控配方。 - [`docs/architecture.md`](docs/architecture.md) —— 系统设计(纯承诺证明、每事件链、JCS 规范化)。 - [`docs/plugin-production.md`](docs/plugin-production.md) —— 生产部署指南:wallet 运维、CI/CD 模式、监控、runbook。 - [`docs/plugin-threat-model.md`](docs/plugin-threat-model.md) —— plugin 防御什么,不防御什么,边界。 - 使用此 plugin 的参考 demo 应用位于 [vilenarios/Verifiable-AI-Decision-Records-Demo](https://github.com/vilenarios/Verifiable-AI-Decision-Records-Demo)。
标签:Apex, MLflow, MLOps, Python, 区块链, 数据防篡改, 无后门, 机器学习, 模型溯源, 逆向工具