cids/* links are signed MMUR2 claims (POST /api/claims); SDK v1.10.0 on gitea
Build and Push Docker Image / build (push) Successful in 2m0s

This commit is contained in:
Worph
2026-10-25 17:36:34 +00:00
parent 41714461df
commit 14c912fd3f
3 changed files with 283 additions and 74 deletions
Generated
+227 -8
View File
@@ -125,6 +125,12 @@ version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
[[package]]
name = "base16ct"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]] [[package]]
name = "base256emoji" name = "base256emoji"
version = "1.0.2" version = "1.0.2"
@@ -141,6 +147,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]] [[package]]
name = "bendy" name = "bendy"
version = "0.3.3" version = "0.3.3"
@@ -165,6 +177,15 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bs58"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"tinyvec",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.19.1" version = "3.19.1"
@@ -211,6 +232,12 @@ dependencies = [
"unsigned-varint", "unsigned-varint",
] ]
[[package]]
name = "const-oid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]] [[package]]
name = "const-str" name = "const-str"
version = "0.4.3" version = "0.4.3"
@@ -244,6 +271,18 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "crypto-bigint"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
dependencies = [
"generic-array",
"rand_core 0.6.4",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.7" version = "0.1.7"
@@ -280,6 +319,16 @@ dependencies = [
"syn 2.0.114", "syn 2.0.114",
] ]
[[package]]
name = "der"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
"const-oid",
"zeroize",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
@@ -287,7 +336,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [ dependencies = [
"block-buffer", "block-buffer",
"const-oid",
"crypto-common", "crypto-common",
"subtle",
] ]
[[package]] [[package]]
@@ -301,6 +352,39 @@ dependencies = [
"syn 2.0.114", "syn 2.0.114",
] ]
[[package]]
name = "ecdsa"
version = "0.16.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
dependencies = [
"der",
"digest",
"elliptic-curve",
"rfc6979",
"signature",
"spki",
]
[[package]]
name = "elliptic-curve"
version = "0.13.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
dependencies = [
"base16ct",
"crypto-bigint",
"digest",
"ff",
"generic-array",
"group",
"pkcs8",
"rand_core 0.6.4",
"sec1",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.14" version = "0.3.14"
@@ -339,6 +423,16 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "ff"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
dependencies = [
"rand_core 0.6.4",
"subtle",
]
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.8" version = "0.1.8"
@@ -423,6 +517,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [ dependencies = [
"typenum", "typenum",
"version_check", "version_check",
"zeroize",
] ]
[[package]] [[package]]
@@ -458,12 +553,32 @@ version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff",
"rand_core 0.6.4",
"subtle",
]
[[package]] [[package]]
name = "hex" name = "hex"
version = "0.4.3" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]] [[package]]
name = "http" name = "http"
version = "1.4.0" version = "1.4.0"
@@ -706,6 +821,20 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "k256"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b"
dependencies = [
"cfg-if",
"ecdsa",
"elliptic-curve",
"once_cell",
"sha2",
"signature",
]
[[package]] [[package]]
name = "keccak" name = "keccak"
version = "0.1.5" version = "0.1.5"
@@ -804,12 +933,19 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]] [[package]]
name = "meta-feeder-sdk" name = "meta-feeder-sdk"
version = "1.7.0" version = "1.10.0"
source = "git+https://github.com/worph/meta-feeder-sdk?tag=v1.7.0#027917c8cffe7ca43dadcf3b426cc5f69ecc443a" source = "git+https://gitea-watch.nsl.sh/metastar/meta-feeder-sdk?tag=v1.10.0#f804d3adb19cc657cd15645e6ad134c767d30b33"
dependencies = [ dependencies = [
"anyhow",
"base64",
"bs58",
"k256",
"libc", "libc",
"rand_core 0.6.4",
"serde", "serde",
"serde_json", "serde_json",
"sha2",
"thiserror 1.0.69",
"tokio", "tokio",
"tracing", "tracing",
] ]
@@ -836,7 +972,7 @@ dependencies = [
"sha2", "sha2",
"sha3", "sha3",
"tempfile", "tempfile",
"thiserror", "thiserror 2.0.18",
"tokio", "tokio",
"tower-http", "tower-http",
"tracing", "tracing",
@@ -957,6 +1093,16 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs8"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
"der",
"spki",
]
[[package]] [[package]]
name = "potential_utf" name = "potential_utf"
version = "0.1.4" version = "0.1.4"
@@ -998,7 +1144,7 @@ dependencies = [
"rustc-hash", "rustc-hash",
"rustls", "rustls",
"socket2", "socket2",
"thiserror", "thiserror 2.0.18",
"tokio", "tokio",
"tracing", "tracing",
"web-time", "web-time",
@@ -1019,7 +1165,7 @@ dependencies = [
"rustls", "rustls",
"rustls-pki-types", "rustls-pki-types",
"slab", "slab",
"thiserror", "thiserror 2.0.18",
"tinyvec", "tinyvec",
"tracing", "tracing",
"web-time", "web-time",
@@ -1061,7 +1207,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [ dependencies = [
"rand_chacha", "rand_chacha",
"rand_core", "rand_core 0.9.5",
] ]
[[package]] [[package]]
@@ -1071,7 +1217,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [ dependencies = [
"ppv-lite86", "ppv-lite86",
"rand_core", "rand_core 0.9.5",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.17",
] ]
[[package]] [[package]]
@@ -1150,6 +1305,16 @@ dependencies = [
"webpki-roots", "webpki-roots",
] ]
[[package]]
name = "rfc6979"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
dependencies = [
"hmac",
"subtle",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.14" version = "0.17.14"
@@ -1242,6 +1407,20 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sec1"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
dependencies = [
"base16ct",
"der",
"generic-array",
"pkcs8",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.228"
@@ -1365,6 +1544,16 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "signature"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest",
"rand_core 0.6.4",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.11" version = "0.4.11"
@@ -1387,6 +1576,16 @@ dependencies = [
"windows-sys 0.60.2", "windows-sys 0.60.2",
] ]
[[package]]
name = "spki"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
]
[[package]] [[package]]
name = "stable_deref_trait" name = "stable_deref_trait"
version = "1.2.1" version = "1.2.1"
@@ -1466,13 +1665,33 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.69",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.18" version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl 2.0.18",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
] ]
[[package]] [[package]]
+1 -1
View File
@@ -32,7 +32,7 @@ serde_json = "1"
# Beacon v2: advertise as metamesh.enrich/full-hash so meta-sort's Plugins # Beacon v2: advertise as metamesh.enrich/full-hash so meta-sort's Plugins
# page finds it (Scan → Add). Only the SDK's light `beacon` feature. # page finds it (Scan → Add). Only the SDK's light `beacon` feature.
meta-feeder-sdk = { git = "https://github.com/worph/meta-feeder-sdk", tag = "v1.7.0", default-features = false, features = ["beacon"] } meta-feeder-sdk = { git = "https://gitea-watch.nsl.sh/metastar/meta-feeder-sdk", tag = "v1.10.0", default-features = false, features = ["beacon", "claims"] }
# HTTP client # HTTP client
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "stream"] } reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "stream"] }
+55 -65
View File
@@ -1,13 +1,34 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use reqwest::Client; use reqwest::Client;
use serde_json::json; use serde::Deserialize;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::OnceLock;
use std::time::Duration; use std::time::Duration;
const REQUEST_TIMEOUT: Duration = Duration::from_secs(30); const REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
const MAX_RETRIES: u32 = 3; const MAX_RETRIES: u32 = 3;
const RETRY_DELAY: Duration = Duration::from_millis(500); const RETRY_DELAY: Duration = Duration::from_millis(500);
/// One per-claim verdict from `POST /api/claims`.
#[derive(Deserialize)]
struct ClaimStatus {
status: String,
#[serde(default)]
reason: Option<String>,
}
/// This plugin's claim signer, loaded once from `METAMESH_SIGNING_KEY` /
/// `METAMESH_SIGNING_KEY_FILE` (generated on first use when only the file is
/// set). A missing key fails every write instead of signing with a silent
/// ephemeral identity.
fn signer() -> Result<&'static meta_feeder_sdk::Signer> {
static SIGNER: OnceLock<std::result::Result<meta_feeder_sdk::Signer, String>> = OnceLock::new();
SIGNER
.get_or_init(|| meta_feeder_sdk::load_or_create_key().map_err(|e| format!("{e:#}")))
.as_ref()
.map_err(|e| anyhow::anyhow!("no signing key: {e}"))
}
pub struct MetaCoreClient { pub struct MetaCoreClient {
client: Client, client: Client,
base_url: String, base_url: String,
@@ -26,88 +47,57 @@ impl MetaCoreClient {
}) })
} }
/// Set a single property for a hash ID /// Sign `metadata` as this plugin's claims on `cid` and submit them to
pub async fn set_property(&self, hash_id: &str, key: &str, value: &str) -> Result<()> { /// `POST /api/claims` (claims model §9) — the replacement for the old
let url = format!("{}/meta/{}/{}", self.base_url, hash_id, key); /// unsigned `PATCH /meta/{hash}` / per-key `PUT`. `cid` must be the content
/// cid the request named, never a meta-core rid. Retries transient
/// failures; a refused claim (bad signature / bad request) is an error.
pub async fn merge_metadata(
&self,
cid: &str,
metadata: &HashMap<String, String>,
) -> Result<()> {
let signer = signer()?;
let fields: std::collections::BTreeMap<String, String> =
metadata.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
let claims = signer.sign_fields(cid, &fields, meta_feeder_sdk::claims::now_ms());
let url = format!("{}/api/claims", self.base_url);
for attempt in 1..=MAX_RETRIES { for attempt in 1..=MAX_RETRIES {
match self match self.client.post(&url).json(&claims).send().await {
.client
.put(&url)
.json(&json!({ "value": value }))
.send()
.await
{
Ok(resp) if resp.status().is_success() => { Ok(resp) if resp.status().is_success() => {
let verdicts: Vec<ClaimStatus> =
resp.json().await.context("decode POST /api/claims")?;
if let Some(bad) = verdicts
.iter()
.find(|v| v.status == "bad_signature" || v.status == "bad_request")
{
anyhow::bail!(
"meta-core refused a claim for {}: {} {}",
cid,
bad.status,
bad.reason.as_deref().unwrap_or("")
);
}
return Ok(()); return Ok(());
} }
Ok(resp) => { Ok(resp) => {
let status = resp.status(); let status = resp.status();
let body = resp.text().await.unwrap_or_default(); let body = resp.text().await.unwrap_or_default();
if attempt == MAX_RETRIES { if attempt == MAX_RETRIES {
anyhow::bail!( anyhow::bail!("POST /api/claims for {}: {} - {}", cid, status, body);
"Failed to set property {} for {}: {} - {}",
key,
hash_id,
status,
body
);
} }
tracing::warn!( tracing::warn!("Retry {}/{} for POST /api/claims: {} - {}", attempt, MAX_RETRIES, status, body);
"Retry {}/{} for set_property: {} - {}",
attempt,
MAX_RETRIES,
status,
body
);
} }
Err(e) => { Err(e) => {
if attempt == MAX_RETRIES { if attempt == MAX_RETRIES {
return Err(e).context(format!( return Err(e).context(format!("POST /api/claims for {cid}"));
"Failed to set property {} for {}",
key, hash_id
));
} }
tracing::warn!("Retry {}/{} for set_property: {}", attempt, MAX_RETRIES, e); tracing::warn!("Retry {}/{} for POST /api/claims: {}", attempt, MAX_RETRIES, e);
} }
} }
tokio::time::sleep(RETRY_DELAY).await; tokio::time::sleep(RETRY_DELAY).await;
} }
Ok(())
}
/// Merge multiple properties at once
pub async fn merge_metadata(
&self,
hash_id: &str,
metadata: &HashMap<String, String>,
) -> Result<()> {
// Try batch endpoint first
let url = format!("{}/meta/{}", self.base_url, hash_id);
match self.client.patch(&url).json(metadata).send().await {
Ok(resp) if resp.status().is_success() => {
return Ok(());
}
Ok(resp) if resp.status().as_u16() == 404 => {
// Batch endpoint not available, fall back to individual sets
tracing::debug!("Batch endpoint not available, using individual sets");
}
Ok(resp) => {
let status = resp.status();
tracing::warn!("Batch merge failed with {}, falling back to individual sets", status);
}
Err(e) => {
tracing::warn!("Batch merge error: {}, falling back to individual sets", e);
}
}
// Fall back to individual property sets
for (key, value) in metadata {
self.set_property(hash_id, key, value).await?;
}
Ok(()) Ok(())
} }