diff --git a/crates/auths-witness-node/Cargo.toml b/crates/auths-witness-node/Cargo.toml index a53bf1b6..a4bbf4f5 100644 --- a/crates/auths-witness-node/Cargo.toml +++ b/crates/auths-witness-node/Cargo.toml @@ -52,7 +52,7 @@ git2 = { version = "0.21.0", default-features = false, features = ["vendored-lib axum = { version = "0.8" } clap = { version = "4", features = ["derive", "env"] } tower = { version = "0.5", features = ["limit"] } -tower-http = { version = "0.6", features = ["timeout"] } +tower-http = { version = "0.6", features = ["timeout", "cors"] } tokio = { workspace = true, features = ["rt-multi-thread", "macros", "net", "signal", "sync", "process", "io-util"] } # Decompress gzip-encoded git-upload-pack request bodies (the `registry` role's # git smart-HTTP surface); standard git clients gzip the fetch negotiation. diff --git a/crates/auths-witness-node/src/bin/witness_node.rs b/crates/auths-witness-node/src/bin/witness_node.rs index a6141055..8ca33f2f 100644 --- a/crates/auths-witness-node/src/bin/witness_node.rs +++ b/crates/auths-witness-node/src/bin/witness_node.rs @@ -21,8 +21,10 @@ use auths_witness_node::signer::{FileSigner, Signer as _}; use auths_witness_node::sqlite_store::SqliteAnchorStore; use axum::Router; use axum::extract::DefaultBodyLimit; +use axum::http::Method; use clap::{Parser, Subcommand}; use tower::limit::ConcurrencyLimitLayer; +use tower_http::cors::{Any, CorsLayer}; use tower_http::timeout::TimeoutLayer; /// A witness request body is one anchor plus the party naming — a few KiB at @@ -286,6 +288,22 @@ async fn main() -> std::process::ExitCode { let mut app = Router::new(); + // W0.1 — the public status page (`GET /`), served regardless of the + // role mix. The member key is derived from the seed best-effort: a + // registry-only node started without one simply renders "—". + let member_did = parse_seed(&args.seed).ok().map(|seed| { + let signer = FileSigner::from_seed(args.witness_name.clone(), seed); + auths_crypto::did_key_encode(auths_crypto::CurveType::Ed25519, &signer.public_key()) + }); + app = app.merge(auths_witness_node::status::status_router( + auths_witness_node::status::StatusState { + witness_name: args.witness_name.clone(), + member_did, + roles: args.roles.clone(), + registry: args.registry.clone(), + }, + )); + if has("anchor") { let state = match build_state(&args) { Ok(state) => state, @@ -362,6 +380,20 @@ async fn main() -> std::process::ExitCode { } else { core }; + + // W0.2 — CORS on the public GET reads, so the explorer's + // browser-direct mode (and any third-party lens) can query this + // witness straight from a browser without a proxy. Scoped to GET + // with no credentials: a cross-origin write is a non-simple request, + // so its preflight fails against `allow_methods([GET])` and the + // browser blocks it — the wildcard ACAO never enables a cross-site + // POST. Mirrors auths-mcp-server's opt-in CORS, tightened to reads. + let app = app.layer( + CorsLayer::new() + .allow_origin(Any) + .allow_methods([Method::GET]) + .allow_headers(Any), + ); // Housekeeping for the write path: appends create loose git // objects; a periodic `git gc --auto` keeps the served registry // packed (bench-measured ~45 KB/identity unpacked otherwise). diff --git a/crates/auths-witness-node/src/lib.rs b/crates/auths-witness-node/src/lib.rs index 90f37782..66a411d3 100644 --- a/crates/auths-witness-node/src/lib.rs +++ b/crates/auths-witness-node/src/lib.rs @@ -49,6 +49,7 @@ pub mod serve_registry; pub mod signer; pub mod sqlite_store; pub mod standup; +pub mod status; pub mod sync; pub mod vocabulary; diff --git a/crates/auths-witness-node/src/status.rs b/crates/auths-witness-node/src/status.rs new file mode 100644 index 00000000..34178ac0 --- /dev/null +++ b/crates/auths-witness-node/src/status.rs @@ -0,0 +1,165 @@ +//! The node's public status page (`GET /`) — epic W0.1. +//! +//! One embedded-HTML handler: no JS, no build step, no dependency the node +//! doesn't already link. It renders the facts a human — often a would-be witness +//! operator — wants when they point a browser at `https://`: the node's +//! name, its member key, the roles it serves, how many members it holds, and the +//! one-liners to mirror its registry and add it to a principal's declared set. +//! +//! It is deliberately the WHOLE web surface the node has. Growth pressure never +//! goes into making the node bigger (network-auths-dev §6): everything richer — +//! browsing KELs, re-verifying receipts, inspecting anchors — lives in the +//! untrusted explorer at `explorer.auths.dev`, off the audited node binary. A +//! stranger's conformant witness gets this same page for free from the same +//! code. + +use std::path::PathBuf; + +use axum::extract::State; +use axum::http::HeaderMap; +use axum::response::Html; +use axum::routing::get; +use axum::Router; + +/// The slice of node identity the status page renders. Cloneable so it can back +/// a dedicated `/` router independent of any role's typed state. +#[derive(Clone)] +pub struct StatusState { + /// This node's public name, as carried in its cosignatures. + pub witness_name: String, + /// The node's member verifying key (`did:key:…`), or `None` when the seed + /// couldn't be read (a registry-only node started without one). + pub member_did: Option, + /// The roles this node serves (`anchor`, `kel`, `cosign`, `registry`). + pub roles: Vec, + /// The party registry, read live to count members held. + pub registry: PathBuf, +} + +/// A `/`-only router serving the status page. Merge it into the node's app so +/// `GET /` answers regardless of role mix. +pub fn status_router(state: StatusState) -> Router { + Router::new().route("/", get(status_page)).with_state(state) +} + +async fn status_page(State(state): State, headers: HeaderMap) -> Html { + // Members held: a flat ref-enumeration index lookup, never a KEL walk. + let member_count = auths_sdk::storage::PerPrefixKelStore::open(state.registry.as_path()) + .list_prefixes() + .map(|p| p.len()) + .unwrap_or(0); + let base_url = base_url(&headers); + Html(render(&state, member_count, &base_url)) +} + +/// Reconstruct the node's own public base URL from the request, so the shown +/// commands are copy-pasteable. Honors `x-forwarded-proto` (the node sits behind +/// TLS-terminating proxies in every shipped deploy); falls back to a scheme +/// inferred from the host. +fn base_url(headers: &HeaderMap) -> String { + let host = headers + .get("host") + .and_then(|h| h.to_str().ok()) + .unwrap_or("network.auths.dev"); + let scheme = headers + .get("x-forwarded-proto") + .and_then(|h| h.to_str().ok()) + .map(|s| s.to_string()) + .unwrap_or_else(|| { + if host.starts_with("127.0.0.1") || host.starts_with("localhost") { + "http".to_string() + } else { + "https".to_string() + } + }); + format!("{scheme}://{host}") +} + +/// Minimal HTML entity escaping for the untrusted-ish dynamic fields (node name, +/// member key, roles all originate from local config, but escaping keeps the +/// page robust if that ever changes). +fn escape(s: &str) -> String { + s.replace('&', "&") + .replace('<', "<") + .replace('>', ">") + .replace('"', """) +} + +fn render(state: &StatusState, member_count: usize, base_url: &str) -> String { + let name = escape(&state.witness_name); + let member_key = state.member_did.as_deref().unwrap_or("—"); + let member_key_esc = escape(member_key); + let roles = state + .roles + .iter() + .map(|r| escape(r)) + .collect::>() + .join(" · "); + let base = escape(base_url); + let add_witness = format!("auths witness add --url {base}"); + let fetch = format!("git fetch '{base}' '+refs/auths/kel/*:refs/auths/kel/*'"); + + format!( + r##" + + + + +{name} — Auths witness + + + +
+

Auths witness node

+

{name}

+

a small, dumb signer you check — not a service you trust

+ +
+
member key{member_key_esc}
+
roles{roles}
+
members held{member_count}
+
livenessup · see /health
+
+ +

Mirror this node’s key histories

+
# pull every member’s KEL over plain git — no auth, no account
+{fetch}
+ +

Witness a principal with this node

+
# add this witness to your identity’s declared set
+{add_witness}
+# then the principal anchors the set (name + curve + member key) in their own key history
+ + + +

Everything that proves here is open code: the node, the verifier, the conformance harness. This page is a convenience over evidence anyone can re-check.

+
+ + +"## + ) +} diff --git a/packages/auths-node/index.d.ts b/packages/auths-node/index.d.ts index a123a5ed..24fdfc09 100644 --- a/packages/auths-node/index.d.ts +++ b/packages/auths-node/index.d.ts @@ -466,6 +466,40 @@ export declare const enum PresentationStatus { Unknown = 'Unknown' } +/** + * Read a member's Key Event Log out of a `fetchRegistry` mirror as JSON. + * + * Transport only — this does NOT verify. It emits the member's events plus, + * for each event, its CESR attachment **hex-encoded** (controller indexed + * signatures, and any delegation source seal), in exactly the shape + * `@auths-dev/verifier`'s `validateKelJson(kelJson, attachmentsJson)` consumes: + * `attachments[i]` is the hex attachment for `events[i]`, strictly equal + * length and order. The caller (a browser) re-verifies; a compromised mirror + * can withhold or truncate here, but the client recompute catches that — it + * cannot forge. + * + * The registry mirror only carries what rides under `refs/auths/*`, so the + * attachments are the per-event controller/delegation groups actually stored; + * witness receipts (a derived index) are served separately by the node's + * `/witness/{prefix}/receipt/{said}` surface, not here. + * + * Args: + * * `registry_dir`: a directory previously populated by `fetchRegistry`. + * * `prefix`: the member AID (a bare KERI prefix, no `did:keri:`). + * + * Returns JSON `{ prefix, events, attachments, tip, source }`. Throws when this + * registry does not hold the prefix. + * + * Usage: + * ```ignore + * fetchRegistry(witnessGitUrl, dir); + * const { events, attachments } = JSON.parse(readKelJson(dir, prefix)); + * const keyState = JSON.parse(await validateKelJson( + * JSON.stringify(events), JSON.stringify(attachments))); + * ``` + */ +export declare function readKelJson(registryDir: string, prefix: string): string + /** The embedded JSON schema for the `receipts/v1` wire contract. */ export declare function receiptsV1Schema(): string @@ -570,14 +604,6 @@ export declare function signCommit(data: Buffer, identityKeyAlias: string, repoP */ export declare function verifyActionEnvelope(envelopeJson: string, publicKeyHex: string, curve?: string | undefined | null): NapiVerificationResult -/** Options for {@link verifyActivityAttestation}. */ -export interface VerifyActivityOpts { - /** Fail the whole document when there is no verified witness anchor. */ - requireWitness?: boolean - /** An independently-known witness tip index; a tip past the document's count marks the anchor stale. */ - witnessTipIndex?: number -} - /** * Verify a published `activity/v1` attestation against a fetched registry copy: * resolve the agent's current keys from the KEL (identity resolution ONLY — @@ -593,10 +619,12 @@ export interface VerifyActivityOpts { * seller's own claims. `freshness` is `"fresh" | "unknown" | "stale"`; * `headBound` is `true` only when a verified witness anchor cosigns the head. * - * `cumulativeCents` is a SIGNED CLAIM, not a re-derived fact: the per-call log is - * structurally private, so the magnitude is provable only when `anchor` is a + * `cumulativeCents` is a SIGNED CLAIM, not a re-derived fact: the per-call log + * is structurally private, so the magnitude is provable only when `anchor` is a * non-stale `witness` summary. A consumer MUST NOT present `cumulativeCents` as * verified earnings unless `anchor?.tier === "witness" && anchor.stale === false`. + * At first-seen (`anchor === null`) the honest label is "seller-claimed, + * unwitnessed". * * Usage: * ```ignore @@ -606,6 +634,20 @@ export interface VerifyActivityOpts { */ export declare function verifyActivityAttestation(attestationJson: string, registryPath: string, opts?: VerifyActivityOpts | undefined | null): string +/** Options for [`verify_activity_attestation`]. */ +export interface VerifyActivityOpts { + /** + * Fail the whole document when there is no verified witness anchor — promotes + * the anchor from an additive assurance tier to a required gate. + */ + requireWitness?: boolean + /** + * An independently-known witness tip index for this seed, if the caller has + * one. A tip greater than the document's count marks the anchor `stale`. + */ + witnessTipIndex?: number +} + export declare function verifyAttestation(attestationJson: string, issuerPkHex: string): Promise export declare function verifyAtTime(attestationJson: string, issuerPkHex: string, atRfc3339: string): Promise diff --git a/packages/auths-node/index.js b/packages/auths-node/index.js index 0e5b476e..7c67cc44 100644 --- a/packages/auths-node/index.js +++ b/packages/auths-node/index.js @@ -77,8 +77,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-android-arm64') const bindingPackageVersion = require('@auths-dev/sdk-android-arm64/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -93,8 +93,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-android-arm-eabi') const bindingPackageVersion = require('@auths-dev/sdk-android-arm-eabi/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -114,8 +114,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-win32-x64-gnu') const bindingPackageVersion = require('@auths-dev/sdk-win32-x64-gnu/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -130,8 +130,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-win32-x64-msvc') const bindingPackageVersion = require('@auths-dev/sdk-win32-x64-msvc/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -147,8 +147,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-win32-ia32-msvc') const bindingPackageVersion = require('@auths-dev/sdk-win32-ia32-msvc/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -163,8 +163,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-win32-arm64-msvc') const bindingPackageVersion = require('@auths-dev/sdk-win32-arm64-msvc/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -182,8 +182,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-darwin-universal') const bindingPackageVersion = require('@auths-dev/sdk-darwin-universal/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -198,8 +198,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-darwin-x64') const bindingPackageVersion = require('@auths-dev/sdk-darwin-x64/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -214,8 +214,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-darwin-arm64') const bindingPackageVersion = require('@auths-dev/sdk-darwin-arm64/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -234,8 +234,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-freebsd-x64') const bindingPackageVersion = require('@auths-dev/sdk-freebsd-x64/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -250,8 +250,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-freebsd-arm64') const bindingPackageVersion = require('@auths-dev/sdk-freebsd-arm64/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -271,8 +271,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-linux-x64-musl') const bindingPackageVersion = require('@auths-dev/sdk-linux-x64-musl/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -287,8 +287,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-linux-x64-gnu') const bindingPackageVersion = require('@auths-dev/sdk-linux-x64-gnu/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -305,8 +305,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-linux-arm64-musl') const bindingPackageVersion = require('@auths-dev/sdk-linux-arm64-musl/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -321,8 +321,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-linux-arm64-gnu') const bindingPackageVersion = require('@auths-dev/sdk-linux-arm64-gnu/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -339,8 +339,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-linux-arm-musleabihf') const bindingPackageVersion = require('@auths-dev/sdk-linux-arm-musleabihf/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -355,8 +355,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-linux-arm-gnueabihf') const bindingPackageVersion = require('@auths-dev/sdk-linux-arm-gnueabihf/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -373,8 +373,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-linux-loong64-musl') const bindingPackageVersion = require('@auths-dev/sdk-linux-loong64-musl/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -389,8 +389,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-linux-loong64-gnu') const bindingPackageVersion = require('@auths-dev/sdk-linux-loong64-gnu/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -407,8 +407,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-linux-riscv64-musl') const bindingPackageVersion = require('@auths-dev/sdk-linux-riscv64-musl/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -423,8 +423,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-linux-riscv64-gnu') const bindingPackageVersion = require('@auths-dev/sdk-linux-riscv64-gnu/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -440,8 +440,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-linux-ppc64-gnu') const bindingPackageVersion = require('@auths-dev/sdk-linux-ppc64-gnu/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -456,8 +456,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-linux-s390x-gnu') const bindingPackageVersion = require('@auths-dev/sdk-linux-s390x-gnu/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -476,8 +476,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-openharmony-arm64') const bindingPackageVersion = require('@auths-dev/sdk-openharmony-arm64/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -492,8 +492,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-openharmony-x64') const bindingPackageVersion = require('@auths-dev/sdk-openharmony-x64/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -508,8 +508,8 @@ function requireNative() { try { const binding = require('@auths-dev/sdk-openharmony-arm') const bindingPackageVersion = require('@auths-dev/sdk-openharmony-arm/package.json').version - if (bindingPackageVersion !== '0.1.12' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.1.12 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.1.13' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.1.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -607,6 +607,7 @@ module.exports.mintChallengeNonce = nativeBinding.mintChallengeNonce module.exports.pinIdentity = nativeBinding.pinIdentity module.exports.presentationNonce = nativeBinding.presentationNonce module.exports.PresentationStatus = nativeBinding.PresentationStatus +module.exports.readKelJson = nativeBinding.readKelJson module.exports.receiptsV1Schema = nativeBinding.receiptsV1Schema module.exports.removePinnedIdentity = nativeBinding.removePinnedIdentity module.exports.removeWitness = nativeBinding.removeWitness diff --git a/packages/auths-node/src/evidence.rs b/packages/auths-node/src/evidence.rs index 56f14ee9..0582bed0 100644 --- a/packages/auths-node/src/evidence.rs +++ b/packages/auths-node/src/evidence.rs @@ -267,6 +267,95 @@ pub fn fetch_registry(url: String, dest: String) -> Result<()> { Ok(()) } +/// Read a member's Key Event Log out of a `fetchRegistry` mirror as JSON. +/// +/// Transport only — this does NOT verify. It emits the member's events plus, +/// for each event, its CESR attachment **hex-encoded** (controller indexed +/// signatures, and any delegation source seal), in exactly the shape +/// `@auths-dev/verifier`'s `validateKelJson(kelJson, attachmentsJson)` consumes: +/// `attachments[i]` is the hex attachment for `events[i]`, strictly equal +/// length and order. The caller (a browser) re-verifies; a compromised mirror +/// can withhold or truncate here, but the client recompute catches that — it +/// cannot forge. +/// +/// The registry mirror only carries what rides under `refs/auths/*`, so the +/// attachments are the per-event controller/delegation groups actually stored; +/// witness receipts (a derived index) are served separately by the node's +/// `/witness/{prefix}/receipt/{said}` surface, not here. +/// +/// Args: +/// * `registry_dir`: a directory previously populated by `fetchRegistry`. +/// * `prefix`: the member AID (a bare KERI prefix, no `did:keri:`). +/// +/// Returns JSON `{ prefix, events, attachments, tip, source }`. Throws when this +/// registry does not hold the prefix. +/// +/// Usage: +/// ```ignore +/// fetchRegistry(witnessGitUrl, dir); +/// const { events, attachments } = JSON.parse(readKelJson(dir, prefix)); +/// const keyState = JSON.parse(await validateKelJson( +/// JSON.stringify(events), JSON.stringify(attachments))); +/// ``` +#[napi] +pub fn read_kel_json(registry_dir: String, prefix: String) -> Result { + use auths_id::keri::event::Event; + use auths_id::keri::types::Prefix; + use auths_storage::git::PerPrefixKelStore; + use std::ops::ControlFlow; + + #[derive(serde::Serialize)] + struct TipJson { + sequence: u128, + said: String, + } + #[derive(serde::Serialize)] + struct KelReadResult { + prefix: String, + events: Vec, + attachments: Vec, + tip: Option, + source: &'static str, + } + + let store = PerPrefixKelStore::open(std::path::Path::new(®istry_dir)); + let pfx = Prefix::new_unchecked(prefix.clone()); + + // Events in sequence order. + let mut events: Vec = Vec::new(); + store + .visit_events(&pfx, 0, &mut |e: &Event| { + events.push(e.clone()); + ControlFlow::Continue(()) + }) + .map_err(|e| Error::from_reason(format!("read kel for {prefix}: {e}")))?; + + // Pair each event with its hex-encoded CESR attachment (same order/length). + let mut attachments: Vec = Vec::with_capacity(events.len()); + for ev in &events { + let seq = ev.sequence().value(); + let att = store + .get_attachment(&pfx, seq) + .map_err(|e| Error::from_reason(format!("read attachment {seq} for {prefix}: {e}")))? + .unwrap_or_default(); + attachments.push(hex::encode(att)); + } + + let tip = store.get_tip(&pfx).ok().map(|t| TipJson { + sequence: t.sequence, + said: t.said.to_string(), + }); + + let out = KelReadResult { + prefix, + events, + attachments, + tip, + source: "per-prefix", + }; + serde_json::to_string(&out).map_err(|e| Error::from_reason(format!("serialize kel: {e}"))) +} + /// The embedded JSON schema for the `receipts/v1` wire contract. #[napi] pub fn receipts_v1_schema() -> String {