From acd894e260b9c0fe470d2126b8954e387e00fb2e Mon Sep 17 00:00:00 2001 From: Pietro <17928339+BRA1L0R@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:38:46 +0200 Subject: [PATCH 1/3] dev container fixes and local policies for rego rules --- .devcontainer/Dockerfile | 1 + .gitignore | 1 + Cargo.lock | 5 +- attestation-server/Cargo.toml | 2 +- libattest/src/validation.rs | 18 --- reticle/Cargo.toml | 1 + reticle/examples/local-policies.rs | 20 +++ reticle/examples/policies/tdx.rego | 223 +++++++++++++++++++++++++++++ reticle/src/lib.rs | 22 ++- reticle/src/rego.rs | 101 ++----------- reticle/src/rego/file.rs | 35 +++++ reticle/src/rego/url.rs | 98 +++++++++++++ reticle/tests/policies.rs | 4 +- 13 files changed, 408 insertions(+), 123 deletions(-) create mode 100644 reticle/examples/local-policies.rs create mode 100644 reticle/examples/policies/tdx.rego create mode 100644 reticle/src/rego/file.rs create mode 100644 reticle/src/rego/url.rs diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 0568c2fc..1db52e5b 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -173,6 +173,7 @@ RUN \ && apt-get update \ && apt-get install -y \ docker-ce-cli + Types: deb URIs: https://download.docker.com/linux/debian Suites: $(. /etc/os-release && echo "$VERSION_CODENAME") diff --git a/.gitignore b/.gitignore index f6fc2f2c..a3f1ebad 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ settings.local.json # editors .zed/ .vscode/ +# .claude/ diff --git a/Cargo.lock b/Cargo.lock index 6a63c599..995b4f75 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -2312,7 +2312,7 @@ dependencies = [ [[package]] name = "nvat" version = "0.1.0" -source = "git+ssh://git@github.com/prem-research/nvat-rs.git?branch=main#cd102730c8a82026b395ca5d6e8ff5f37f53feef" +source = "git+https://github.com/prem-research/nvat-rs.git?branch=main#cd102730c8a82026b395ca5d6e8ff5f37f53feef" dependencies = [ "log", "nvat_sys", @@ -2322,7 +2322,7 @@ dependencies = [ [[package]] name = "nvat_sys" version = "1.0.0" -source = "git+ssh://git@github.com/prem-research/nvat-rs.git?branch=main#cd102730c8a82026b395ca5d6e8ff5f37f53feef" +source = "git+https://github.com/prem-research/nvat-rs.git?branch=main#cd102730c8a82026b395ca5d6e8ff5f37f53feef" dependencies = [ "bindgen", "pkg-config", @@ -3114,6 +3114,7 @@ name = "reticle" version = "0.5.0" dependencies = [ "async-once-cell", + "async-trait", "azure-attest", "bytes", "console_error_panic_hook", diff --git a/attestation-server/Cargo.toml b/attestation-server/Cargo.toml index d512c4c0..2da7a97d 100644 --- a/attestation-server/Cargo.toml +++ b/attestation-server/Cargo.toml @@ -15,7 +15,7 @@ anyhow = "1.0.100" base64 = "0.22.1" hex = "0.4.3" thiserror = "2" -nvat = { git = "ssh://git@github.com/prem-research/nvat-rs.git", branch = "main" } +nvat = { git = "https://github.com/prem-research/nvat-rs.git", branch = "main" } libattest = { path = "../libattest" } nvidia-attest = { path = "../nvidia-attest" } diff --git a/libattest/src/validation.rs b/libattest/src/validation.rs index 49e0af8f..a83910df 100644 --- a/libattest/src/validation.rs +++ b/libattest/src/validation.rs @@ -202,21 +202,3 @@ impl ValidationResult { } } } - -// pub struct Appraisal { -// regorous: Engine, -// } - -// impl Appraisal { -// pub fn new() -> Self { -// Appraisal { -// regorous: Engine::default(), -// } -// } - -// pub fn set_policy(&self, policy: &str) { -// self.regorous.add_polic -// } - -// pub fn set_claims(claims: impl Claims) {} -// } diff --git a/reticle/Cargo.toml b/reticle/Cargo.toml index b873dc30..24c4216e 100755 --- a/reticle/Cargo.toml +++ b/reticle/Cargo.toml @@ -23,6 +23,7 @@ hex = "0.4.3" futures = "0.3.32" web-sys = { version = "0.3.98", features = ["Request", "RequestInit"] } async-once-cell = "0.5.4" +async-trait = "0.1.89" [dev-dependencies] tokio = {version = "*", features = ["full"]} diff --git a/reticle/examples/local-policies.rs b/reticle/examples/local-policies.rs new file mode 100644 index 00000000..9969e661 --- /dev/null +++ b/reticle/examples/local-policies.rs @@ -0,0 +1,20 @@ +use reticle::{ClientBuilder, rego::FilePolicies}; +use snp_attest::kds::Kds; + +#[tokio::main] +async fn main() { + let api_url = std::env::args() + .nth(1) + .expect("must supply api url as first argument"); + + let kds = Kds::default(); + let client = ClientBuilder::new(&api_url) + .with_kds(kds) + .with_policy_provider(FilePolicies::new("./examples/policies/")) + .build() + .await + .unwrap(); + + let result = client.attest().await.unwrap(); + println!("{result:?}"); +} diff --git a/reticle/examples/policies/tdx.rego b/reticle/examples/policies/tdx.rego new file mode 100644 index 00000000..91e67a56 --- /dev/null +++ b/reticle/examples/policies/tdx.rego @@ -0,0 +1,223 @@ +# TDX attestation policy +# ============================================================================ +# Queried by reticle as `data.tdx.allow` (see reticle/src/lib.rs -> attest_tdx, +# WithPolicy::new("tdx.allow", quote)). The engine expects `allow` to evaluate +# to a BOOLEAN; anything else makes libattest error out (see validation.rs). +# +# `input` is the serialized `TdxQuoteBody` (tdx-attest/src/dcap/types.rs). Only +# the TD *measurement* body reaches Rego — every cryptographic check (QE report +# signature, ISV signature, QE identity, platform TCB level, and the +# report_data/nonce match) has ALREADY been enforced in Rust by +# TdxQuoteVerifier before this policy runs. This policy's job is purely to +# appraise *which* TD measurements you are willing to trust. +# +# Serialized input shape (field names are the raw struct identifiers, note the +# `seamsttributes` typo is intentional and must be matched verbatim): +# +# { +# "tee_tcb_svn": [ 16 ints ], +# "mrseam": "<96 hex chars>", # SHA-384 +# "mrsignerseam": "<96 hex chars>", # SHA-384 +# "seamsttributes": [ 8 ints ], +# "tdattributes": [ 8 ints ], +# "xfam": [ 8 ints ], +# "mrtd": "<96 hex chars>", # SHA-384 +# "mrconfigid": "<96 hex chars>", +# "mrowner": "<96 hex chars>", +# "mrownerconfig": "<96 hex chars>", +# "rtmr": [ "<96 hex>", "<96 hex>", "<96 hex>", "<96 hex>" ], +# "report_data": "<128 hex chars>" # the nonce; verified in Rust +# } +# +# Appraisal strategy — a baseline that stays portable across hosts: +# +# * INVARIANTS (always enforced, machine-independent): +# - DEBUG attribute bit clear — off-TD debug exposes TD memory +# - tdattributes restricted to an allowed-bits mask +# - xfam restricted to an allowed-bits mask +# - mrsignerseam == all zeros — Intel-signed production TDX module +# - seamsttributes == all zeros — required for production modules +# - tee_tcb_svn >= a component-wise minimum — anti-rollback floor for +# the TDX module (SVNs only increase with security patches) +# - mrconfigid / mrowner / mrownerconfig == all zeros — no +# owner-defined config in use (change if you use them) +# +# * IMAGE MEASUREMENTS (environment-specific): mrtd, mrseam and the RTMRs +# are left unpinned by default because they legitimately vary per host +# (TDX module version), per firmware build (mrtd) or per boot/VM config +# (memory map, kernel, cmdline, initrd land in RTMR[0..2]). The values +# observed on the known-good baseline TD are documented next to each +# field — pin them once your fleet's values are catalogued. +# ============================================================================ +package tdx + +import rego.v1 + +# --------------------------------------------------------------------------- +# Reference values +# --------------------------------------------------------------------------- +# Baseline captured from a known-good TD (Azure TDX host, July 2026). Each +# entry may also be supplied out-of-band via `data.tdx.reference.` +# (reticle loads data JSON files alongside policies), which takes precedence +# over the inline value. +# +# Hex fields are compared case-insensitively. Any reference field left as its +# empty default (empty string / empty array) is treated as "don't care" and +# skips that check, so you can enable checks incrementally. +# --------------------------------------------------------------------------- + +reference := object.union(inline_reference, data_reference) + +inline_reference := { + # MRTD: SHA-384 over the initial TD contents (the TDVF/OVMF firmware + # image). Varies per firmware build, so it is not pinned yet; pin it (or + # override per-environment via a data file) once your images are fixed. + # Observed baseline: 7348651a34b2d2d3462822e3a750ec6110125f36757c78480bbfc69cc0d21fb001a1ced3ee19747dda8f750c3bc8f876 + "mrtd": "", + # MRSEAM: hash of the TDX module itself. Varies with the module version + # installed on each host, so it is not pinned; trust is anchored instead + # on mrsignerseam (Intel-signed) plus the tee_tcb_svn_min floor below. + # Observed baseline: aef4ed3e686fe6cf44c9a8d1cc63105443b558ac4f40c282abf78271e9a4586c50408c8584c7b43fd21edb736700ba5f + "mrseam": "", + # All zeros == TDX module signed by Intel (production). Keep pinned. + "mrsignerseam": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + # No owner-provided TD configuration in use; all three stay zero. + "mrconfigid": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "mrowner": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "mrownerconfig": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + # RTMRs vary per host/boot config, so they are unpinned by default: + # rtmr[0] firmware config + TD HOB (memory map -> differs per VM shape) + # rtmr[1] kernel rtmr[2] cmdline / initrd + # rtmr[3] runtime / application extensions + # Observed baseline (pin once your fleet's values are catalogued): + # [0] 1186572fbf5ba745c0a78870c91b136407a28523c8cd5914c86c1bca61c9e767245b403ad4966b8322728bff66893640 + # [1] 94f3c46bfc3898702ccbdbd4bcaf13b65319bae159d7f6b69d7407e229a5cca453453082233fbf1a8802bdc9a620b929 + # [2] eb58944d4dcde0d57e09664799f2e3e18aed927d52aac4342b21b9d39132ed3cca75901257543e5670dc64e7311bb594 + # [3] 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + "rtmr": ["", "", "", ""], + # Component-wise MINIMUM for the TDX module TCB SVNs (input must be >= in + # every position). SVNs only increase with security patches, so this is an + # anti-rollback floor rather than an exact pin. [] to skip. + "tee_tcb_svn_min": [7, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + # Must be all zeros on production TDX modules; exact match. [] to skip. + "seamsttributes": [0, 0, 0, 0, 0, 0, 0, 0], + # ALLOWED-bits mask (little-endian bytes of the 64-bit ATTRIBUTES field): + # any input bit outside the mask rejects the TD. Baseline permits only + # SEPT_VE_DISABLE (bit 28 -> byte 3, 0x10). DEBUG (bit 0) stays + # hard-rejected by `debug_disabled` even if you widen this mask. [] to + # skip. + "tdattributes_allowed": [0, 0, 0, 16, 0, 0, 0, 0], + # ALLOWED-bits mask for XFAM (extended CPU features the TD may use). A TD + # using a subset passes; unexpected features reject. Baseline = x87/SSE + # (bits 0-1), AVX (2), AVX-512 (5-7), PKRU (9), AMX (17-18). Widen when + # your fleet enables more (e.g. CET bits 11-12). [] to skip. + "xfam_allowed": [231, 2, 6, 0, 0, 0, 0, 0], +} + +# path-based lookup so this is safe even when no `data.tdx` document is loaded +data_reference := object.get(data, ["tdx", "reference"], {}) + +# --------------------------------------------------------------------------- +# Top-level decision +# --------------------------------------------------------------------------- +default allow := false + +allow if { + debug_disabled + mrtd_ok + mrseam_ok + mrsignerseam_ok + mrconfigid_ok + mrowner_ok + mrownerconfig_ok + rtmrs_ok + tee_tcb_svn_ok + seamsttributes_ok + xfam_ok + tdattributes_ok +} + +# --------------------------------------------------------------------------- +# TD ATTRIBUTES — the DEBUG bit (bit 0 of octet 0) MUST be clear, otherwise the +# TD can be single-stepped / introspected and offers no confidentiality. This +# check is unconditional; it cannot be skipped via the reference values. +# --------------------------------------------------------------------------- +debug_disabled if bits.and(input.tdattributes[0], 1) == 0 + +# --------------------------------------------------------------------------- +# Hex measurement checks. Each rule passes when the reference is unset ("") +# OR the (case-insensitive) hex matches. +# --------------------------------------------------------------------------- +mrtd_ok if hex_match(reference.mrtd, input.mrtd) + +mrseam_ok if hex_match(reference.mrseam, input.mrseam) + +mrsignerseam_ok if hex_match(reference.mrsignerseam, input.mrsignerseam) + +mrconfigid_ok if hex_match(reference.mrconfigid, input.mrconfigid) + +mrowner_ok if hex_match(reference.mrowner, input.mrowner) + +mrownerconfig_ok if hex_match(reference.mrownerconfig, input.mrownerconfig) + +# every RTMR index must match its (optional) reference entry +rtmrs_ok if { + every i, ref in reference.rtmr { + hex_match(ref, input.rtmr[i]) + } +} + +# --------------------------------------------------------------------------- +# Byte-array checks. +# --------------------------------------------------------------------------- + +# anti-rollback floor: every SVN component must be >= its reference minimum +tee_tcb_svn_ok if svn_at_least(reference.tee_tcb_svn_min, input.tee_tcb_svn) + +# exact match: production TDX modules report all-zero SEAM attributes +seamsttributes_ok if bytes_match(reference.seamsttributes, input.seamsttributes) + +# masks: no input bit may fall outside the allowed set +xfam_ok if bytes_within_mask(reference.xfam_allowed, input.xfam) + +tdattributes_ok if bytes_within_mask(reference.tdattributes_allowed, input.tdattributes) + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +# case-insensitive hex equality; empty reference == "don't care" +hex_match(ref, got) if ref == "" + +hex_match(ref, got) if { + ref != "" + lower(ref) == lower(got) +} + +# exact byte-array equality; empty reference == "don't care" +bytes_match(ref, got) if count(ref) == 0 + +bytes_match(ref, got) if { + count(ref) > 0 + ref == got +} + +# component-wise minimum; empty reference == "don't care" +svn_at_least(min, got) if count(min) == 0 + +svn_at_least(min, got) if { + count(min) > 0 + every i, m in min { + got[i] >= m + } +} + +# every input bit must lie within the allowed mask; empty == "don't care" +bytes_within_mask(allowed, got) if count(allowed) == 0 + +bytes_within_mask(allowed, got) if { + count(allowed) > 0 + every i, b in got { + bits.and(b, bits.xor(allowed[i], 255)) == 0 + } +} diff --git a/reticle/src/lib.rs b/reticle/src/lib.rs index d065378b..ae747724 100644 --- a/reticle/src/lib.rs +++ b/reticle/src/lib.rs @@ -30,7 +30,10 @@ use tdx_attest::{TdxQuote, nonce::TdxNonce, pcs::Pcs, verify::TdxQuoteVerifier}; #[cfg(target_family = "wasm")] use wasm_bindgen::prelude::*; -use crate::{query::QueryParams, rego::PoliciesClient}; +use crate::{ + query::QueryParams, + rego::{PolicyProvider, UrlPolicies}, +}; #[cfg(feature = "debug")] #[cfg(target_family = "wasm")] @@ -105,7 +108,7 @@ pub struct ClientBuilder { // amd collateral server kds: Kds, // prem OPA policies url - policies: Cow<'static, str>, + policies: Box, headers: HeaderMap, } @@ -122,7 +125,7 @@ impl ClientBuilder { url: url.to_string(), pcs: Pcs::new(PREM_PCCS).unwrap(), kds: Kds::new(PREM_KCDS).unwrap(), - policies: PREM_POLICIES.into(), + policies: Box::new(UrlPolicies::new(PREM_POLICIES).unwrap()), headers: HeaderMap::default(), } } @@ -148,8 +151,12 @@ impl ClientBuilder { } /// sets custom url for OPA policies index - pub fn with_policies_url(mut self, url: &str) -> Self { - self.policies = url.to_string().into(); + // pub fn with_policies_url(mut self, url: &str) -> Self { + // self.policies = url.to_string().into(); + // self + // } + pub fn with_policy_provider(mut self, policy: impl PolicyProvider + 'static) -> Self { + self.policies = Box::new(policy); self } @@ -158,10 +165,11 @@ impl ClientBuilder { .default_headers(self.headers) .build()?; - let validator = PoliciesClient::new(self.policies.as_ref())? + let validator = self + .policies .fetch_validator() .await - .context("failed fetching OPA policies from url")?; + .context("failed fetching OPA policies from provider")?; Ok(Client { url: self diff --git a/reticle/src/rego.rs b/reticle/src/rego.rs index 4d95a1fb..09c527de 100644 --- a/reticle/src/rego.rs +++ b/reticle/src/rego.rs @@ -1,97 +1,12 @@ -use futures::{TryStreamExt, stream::FuturesUnordered}; -use libattest::{ - bail, - error::{AttestationError, Context}, - validation::Validator, -}; -use reqwest::{IntoUrl, Url}; -use serde::Deserialize; +pub mod file; +pub mod url; -pub struct PoliciesClient { - client: reqwest::Client, - base_url: Url, -} - -impl PoliciesClient { - pub fn new(url: impl IntoUrl) -> Result { - let client = reqwest::Client::default(); - let base_url = url.into_url()?; - - Ok(Self { client, base_url }) - } - - async fn fetch_index(&self, index_file: &str) -> Result { - let url = self.base_url.join(index_file).unwrap(); - let index: Index = self - .client - .get(url) - .send() - .await? - .error_for_status()? - .json() - .await - .context("received wrongly formatted policies index")?; - - Ok(index) - } - - // async fn fetch_single(&self, - - async fn fetch_multiple(&self, paths: I) -> Result, AttestationError> - where - S: AsRef, - I: IntoIterator, - { - let fetch_tasks = FuturesUnordered::new(); - - for path in paths { - let url = self - .base_url - .join(path.as_ref()) - .context("failed parsing url from index")?; +pub use file::FilePolicies; +pub use url::UrlPolicies; - if url.authority() != self.base_url.authority() { - bail!("changed authority when joining url!") - } - - let rego_fetch = async move { - self.client - .get(url) - .send() - .await? - .error_for_status()? - .text() - .await - }; - - // push all fetch tasks into a joinset - fetch_tasks.push(rego_fetch); - } - - // poll that joinset - fetch_tasks - .try_collect::>() - .await - .context("failed fetching one or more rego policies") - } - - pub async fn fetch_validator(&self) -> Result { - let index = self.fetch_index("policies.json").await?; - let policies = self.fetch_multiple(index.policies).await?; - let data = self.fetch_multiple(index.data).await?; - - let validator = Validator::builder() - .add_policies(policies) - .add_datas_json(data)? - .build() - .context("failed building validator from source")?; - - Ok(validator) - } -} +use libattest::{error::AttestationError, validation::Validator}; -#[derive(Deserialize)] -struct Index { - policies: Vec, - data: Vec, +#[async_trait::async_trait] +pub trait PolicyProvider { + async fn fetch_validator(&self) -> Result; } diff --git a/reticle/src/rego/file.rs b/reticle/src/rego/file.rs new file mode 100644 index 00000000..1cf1fe92 --- /dev/null +++ b/reticle/src/rego/file.rs @@ -0,0 +1,35 @@ +use std::path::PathBuf; + +use libattest::{AttestationError, validation::Validator}; + +use crate::rego::PolicyProvider; + +/// fetch rego policies from the filesystem. Useful for local testing. +/// Will just list all .rego files in a directory and load them all +pub struct FilePolicies { + path: PathBuf, +} + +impl FilePolicies { + pub fn new(path: impl Into) -> Self { + Self { path: path.into() } + } +} + +#[async_trait::async_trait] +impl PolicyProvider for FilePolicies { + async fn fetch_validator(&self) -> Result { + if !self.path.is_dir() { + libattest::bail!("not a directory"); + } + + let mut policies = vec![]; + + for dir_entry in std::fs::read_dir(&self.path)? { + let policy = std::fs::read_to_string(dir_entry?.path())?; + policies.push(policy); + } + + Validator::builder().add_policies(policies).build() + } +} diff --git a/reticle/src/rego/url.rs b/reticle/src/rego/url.rs new file mode 100644 index 00000000..93de7104 --- /dev/null +++ b/reticle/src/rego/url.rs @@ -0,0 +1,98 @@ +use futures::{TryStreamExt, stream::FuturesUnordered}; +use libattest::{AttestationError, bail, error::Context, validation::Validator}; +use reqwest::{IntoUrl, Url}; +use serde::Deserialize; + +use crate::rego::PolicyProvider; + +pub struct UrlPolicies { + client: reqwest::Client, + base_url: Url, +} + +impl UrlPolicies { + pub fn new(url: impl IntoUrl) -> Result { + let client = reqwest::Client::default(); + let base_url = url.into_url()?; + + Ok(Self { client, base_url }) + } + + async fn fetch_index(&self, index_file: &str) -> Result { + let url = self.base_url.join(index_file).unwrap(); + let index: Index = self + .client + .get(url) + .send() + .await? + .error_for_status()? + .json() + .await + .context("received wrongly formatted policies index")?; + + Ok(index) + } + + // async fn fetch_single(&self, + + async fn fetch_multiple(&self, paths: I) -> Result, AttestationError> + where + S: AsRef, + I: IntoIterator, + { + let fetch_tasks = FuturesUnordered::new(); + + for path in paths { + let url = self + .base_url + .join(path.as_ref()) + .context("failed parsing url from index")?; + + if url.authority() != self.base_url.authority() { + bail!("changed authority when joining url!") + } + + let rego_fetch = async move { + self.client + .get(url) + .send() + .await? + .error_for_status()? + .text() + .await + }; + + // push all fetch tasks into a joinset + fetch_tasks.push(rego_fetch); + } + + // poll that joinset + fetch_tasks + .try_collect::>() + .await + .context("failed fetching one or more rego policies") + } +} + +#[async_trait::async_trait] +impl PolicyProvider for UrlPolicies { + async fn fetch_validator(&self) -> Result { + let index = self.fetch_index("policies.json").await?; + let policies = self.fetch_multiple(index.policies).await?; + let data = self.fetch_multiple(index.data).await?; + + let validator = Validator::builder() + .add_policies(policies) + .add_datas_json(data)? + .build() + .context("failed building validator from source")?; + + Ok(validator) + } +} + +#[derive(Deserialize)] +struct Index { + policies: Vec, + data: Vec, +} diff --git a/reticle/tests/policies.rs b/reticle/tests/policies.rs index c221dffe..95f0f914 100644 --- a/reticle/tests/policies.rs +++ b/reticle/tests/policies.rs @@ -1,8 +1,8 @@ -use reticle::rego::PoliciesClient; +use reticle::rego::{PolicyProvider, UrlPolicies}; #[tokio::test] async fn fetch_validator() { - let policies = PoliciesClient::new("https://policies.prem.io").unwrap(); + let policies = UrlPolicies::new("https://policies.prem.io").unwrap(); let validator = policies.fetch_validator().await.unwrap(); // validator.verify_claims(claims); From e6b712bf85a81519f52093b4b09ec8ce2f4c7653 Mon Sep 17 00:00:00 2001 From: Pietro T <17928339+BRA1L0R@users.noreply.github.com> Date: Fri, 17 Jul 2026 16:41:37 +0000 Subject: [PATCH 2/3] resolved wasm compilation problem --- reticle/.cargo/config.toml | 4 ++-- reticle/src/lib.rs | 26 ++++++++++++++------------ reticle/src/rego.rs | 2 +- reticle/src/rego/file.rs | 2 +- reticle/src/rego/url.rs | 2 +- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/reticle/.cargo/config.toml b/reticle/.cargo/config.toml index 69572640..f4e8c002 100644 --- a/reticle/.cargo/config.toml +++ b/reticle/.cargo/config.toml @@ -1,2 +1,2 @@ -# [build] -# target = "wasm32-unknown-unknown" +[build] +target = "wasm32-unknown-unknown" diff --git a/reticle/src/lib.rs b/reticle/src/lib.rs index ae747724..05ba270f 100644 --- a/reticle/src/lib.rs +++ b/reticle/src/lib.rs @@ -5,7 +5,7 @@ pub mod gateway; pub mod query; pub mod rego; -use std::{borrow::Cow, pin::Pin}; +use std::pin::Pin; use azure_attest::{AzureQuote, collateral::ReportVerifierBuilder, nonce::AzureNonce}; use futures::future::OptionFuture; @@ -108,7 +108,7 @@ pub struct ClientBuilder { // amd collateral server kds: Kds, // prem OPA policies url - policies: Box, + policies: Box, headers: HeaderMap, } @@ -150,16 +150,6 @@ impl ClientBuilder { self } - /// sets custom url for OPA policies index - // pub fn with_policies_url(mut self, url: &str) -> Self { - // self.policies = url.to_string().into(); - // self - // } - pub fn with_policy_provider(mut self, policy: impl PolicyProvider + 'static) -> Self { - self.policies = Box::new(policy); - self - } - pub async fn build(self) -> Result { let reqwest_client = reqwest::Client::builder() .default_headers(self.headers) @@ -186,6 +176,18 @@ impl ClientBuilder { } } +impl ClientBuilder { + /// sets custom url for OPA policies index + // pub fn with_policies_url(mut self, url: &str) -> Self { + // self.policies = url.to_string().into(); + // self + // } + pub fn with_policy_provider(mut self, policy: impl PolicyProvider + Send + 'static) -> Self { + self.policies = Box::new(policy); + self + } +} + #[cfg_attr(target_family = "wasm", wasm_bindgen)] pub struct Client { /// base url diff --git a/reticle/src/rego.rs b/reticle/src/rego.rs index 09c527de..7b0dd542 100644 --- a/reticle/src/rego.rs +++ b/reticle/src/rego.rs @@ -6,7 +6,7 @@ pub use url::UrlPolicies; use libattest::{error::AttestationError, validation::Validator}; -#[async_trait::async_trait] +#[async_trait::async_trait(?Send)] pub trait PolicyProvider { async fn fetch_validator(&self) -> Result; } diff --git a/reticle/src/rego/file.rs b/reticle/src/rego/file.rs index 1cf1fe92..13211046 100644 --- a/reticle/src/rego/file.rs +++ b/reticle/src/rego/file.rs @@ -16,7 +16,7 @@ impl FilePolicies { } } -#[async_trait::async_trait] +#[async_trait::async_trait(?Send)] impl PolicyProvider for FilePolicies { async fn fetch_validator(&self) -> Result { if !self.path.is_dir() { diff --git a/reticle/src/rego/url.rs b/reticle/src/rego/url.rs index 93de7104..21411cb9 100644 --- a/reticle/src/rego/url.rs +++ b/reticle/src/rego/url.rs @@ -74,7 +74,7 @@ impl UrlPolicies { } } -#[async_trait::async_trait] +#[async_trait::async_trait(?Send)] impl PolicyProvider for UrlPolicies { async fn fetch_validator(&self) -> Result { let index = self.fetch_index("policies.json").await?; From 39efd806da48e96ad197e334e77e308a1f779d0b Mon Sep 17 00:00:00 2001 From: Pietro T <17928339+BRA1L0R@users.noreply.github.com> Date: Fri, 17 Jul 2026 16:57:27 +0000 Subject: [PATCH 3/3] cleanup cargo directory --- reticle/.cargo/config.toml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 reticle/.cargo/config.toml diff --git a/reticle/.cargo/config.toml b/reticle/.cargo/config.toml deleted file mode 100644 index f4e8c002..00000000 --- a/reticle/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-unknown-unknown"