diff --git a/.github/workflows/snippets.yml b/.github/workflows/snippets.yml
new file mode 100644
index 0000000..f76b2b7
--- /dev/null
+++ b/.github/workflows/snippets.yml
@@ -0,0 +1,64 @@
+name: Extract code snippets
+
+on:
+ push:
+ paths:
+ - "fabric/examples/**"
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+jobs:
+ extract:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Extract snippets
+ run: |
+ cd fabric/examples
+ mkdir -p /tmp/snippets
+
+ for lang_file in \
+ rust/src/main.rs \
+ js/index.mjs \
+ go/main.go \
+ python/example.py \
+ kotlin/Example.kt \
+ swift/Example.swift; do
+
+ lang=$(dirname "$lang_file" | cut -d/ -f1)
+ mkdir -p "/tmp/snippets/$lang"
+ ./extract-snippets.sh "$lang_file" "/tmp/snippets/$lang"
+ done
+
+ # Create index
+ echo "# Code Snippets" > /tmp/snippets/index.md
+ echo "" >> /tmp/snippets/index.md
+ for lang in rust js go python kotlin swift; do
+ echo "## $lang" >> /tmp/snippets/index.md
+ for f in /tmp/snippets/$lang/*.txt; do
+ name=$(basename "$f" .txt)
+ echo "- [$name]($lang/$name.txt)" >> /tmp/snippets/index.md
+ done
+ echo "" >> /tmp/snippets/index.md
+ done
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: /tmp/snippets
+
+ deploy:
+ needs: extract
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/Cargo.lock b/Cargo.lock
index 840de17..f3d5a29 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -692,7 +692,7 @@ dependencies = [
[[package]]
name = "borsh_utils"
version = "0.1.0"
-source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#e8df2856d5034fcbece10a33eb93da4de6d04baf"
+source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#8bfdffc02d7a49c07f7bf7da77d63a606a502515"
dependencies = [
"bitcoin",
"borsh",
@@ -744,9 +744,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "cc"
-version = "1.2.58"
+version = "1.2.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1"
+checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -1746,9 +1746,9 @@ checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784"
[[package]]
name = "indexmap"
-version = "2.13.0"
+version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
+checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
@@ -2008,7 +2008,7 @@ dependencies = [
[[package]]
name = "libveritas"
version = "0.1.0"
-source = "git+https://github.com/spacesprotocol/libveritas.git#c040c016837900bb36cc3d85452f8f6494d9552d"
+source = "git+https://github.com/spacesprotocol/libveritas.git#a7d86225bac32cf26383cd8b9d2fcff6d8ce7829"
dependencies = [
"base64 0.22.1",
"borsh",
@@ -2026,7 +2026,7 @@ dependencies = [
[[package]]
name = "libveritas_testutil"
version = "0.1.0"
-source = "git+https://github.com/spacesprotocol/libveritas.git#c040c016837900bb36cc3d85452f8f6494d9552d"
+source = "git+https://github.com/spacesprotocol/libveritas.git#a7d86225bac32cf26383cd8b9d2fcff6d8ce7829"
dependencies = [
"bitcoin",
"borsh",
@@ -2044,7 +2044,7 @@ dependencies = [
[[package]]
name = "libveritas_zk"
version = "0.1.0"
-source = "git+https://github.com/spacesprotocol/libveritas.git#c040c016837900bb36cc3d85452f8f6494d9552d"
+source = "git+https://github.com/spacesprotocol/libveritas.git#a7d86225bac32cf26383cd8b9d2fcff6d8ce7829"
dependencies = [
"borsh",
"serde",
@@ -3241,9 +3241,9 @@ dependencies = [
[[package]]
name = "semver"
-version = "1.0.27"
+version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
+checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
dependencies = [
"serde",
"serde_core",
@@ -3373,7 +3373,7 @@ checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
name = "sip7"
version = "0.1.0"
-source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#e8df2856d5034fcbece10a33eb93da4de6d04baf"
+source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#8bfdffc02d7a49c07f7bf7da77d63a606a502515"
dependencies = [
"base64 0.22.1",
"hex",
@@ -3445,7 +3445,7 @@ dependencies = [
[[package]]
name = "spaces_checkpoint"
version = "0.1.0"
-source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#e8df2856d5034fcbece10a33eb93da4de6d04baf"
+source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#8bfdffc02d7a49c07f7bf7da77d63a606a502515"
dependencies = [
"anyhow",
"clap",
@@ -3464,7 +3464,7 @@ dependencies = [
[[package]]
name = "spaces_client"
version = "0.0.7"
-source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#e8df2856d5034fcbece10a33eb93da4de6d04baf"
+source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#8bfdffc02d7a49c07f7bf7da77d63a606a502515"
dependencies = [
"anyhow",
"base64 0.22.1",
@@ -3498,7 +3498,7 @@ dependencies = [
[[package]]
name = "spaces_nums"
version = "0.1.0"
-source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#e8df2856d5034fcbece10a33eb93da4de6d04baf"
+source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#8bfdffc02d7a49c07f7bf7da77d63a606a502515"
dependencies = [
"bech32",
"bitcoin",
@@ -3513,7 +3513,7 @@ dependencies = [
[[package]]
name = "spaces_protocol"
version = "0.0.7"
-source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#e8df2856d5034fcbece10a33eb93da4de6d04baf"
+source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#8bfdffc02d7a49c07f7bf7da77d63a606a502515"
dependencies = [
"bitcoin",
"borsh",
@@ -3524,7 +3524,7 @@ dependencies = [
[[package]]
name = "spaces_wallet"
version = "0.0.7"
-source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#e8df2856d5034fcbece10a33eb93da4de6d04baf"
+source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#8bfdffc02d7a49c07f7bf7da77d63a606a502515"
dependencies = [
"anyhow",
"bdk_wallet",
@@ -4506,9 +4506,9 @@ checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
name = "writeable"
-version = "0.6.2"
+version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
+checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "xattr"
diff --git a/fabric/examples/extract-snippets.sh b/fabric/examples/extract-snippets.sh
new file mode 100755
index 0000000..e01f330
--- /dev/null
+++ b/fabric/examples/extract-snippets.sh
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+set -eo pipefail
+#
+# Extract code snippets from example files.
+#
+# Snippets are delimited by and tags.
+# Output: one file per snippet in the output directory.
+#
+# Usage:
+# ./extract-snippets.sh rust/src/main.rs snippets/rust
+# ./extract-snippets.sh js/index.mjs snippets/js
+#
+
+INPUT="${1:?Usage: extract-snippets.sh }"
+OUTDIR="${2:?Usage: extract-snippets.sh }"
+
+mkdir -p "$OUTDIR"
+
+# Detect language from file extension
+EXT="txt"
+
+# Extract each ... block
+current_tag=""
+current_file=""
+
+while IFS= read -r line; do
+ # Check for opening tag
+ if [[ "$line" =~ \ ]]; then
+ current_tag="${BASH_REMATCH[1]}"
+ current_file="$OUTDIR/${current_tag}.${EXT}"
+ > "$current_file" # truncate
+ continue
+ fi
+
+ # Check for closing tag
+ if [[ "$line" =~ \ $current_file"
+ fi
+ current_tag=""
+ current_file=""
+ continue
+ fi
+
+ # Write line to current snippet (strip common leading whitespace)
+ if [ -n "$current_file" ]; then
+ echo "$line" >> "$current_file"
+ fi
+done < "$INPUT"
+
+# Clean up: strip common leading whitespace from each snippet
+for f in "$OUTDIR"/*."$EXT"; do
+ [ -f "$f" ] || continue
+ # Find minimum indentation (ignoring blank lines)
+ min_indent=$(sed -n '/[^ ]/{ s/^\( *\).*/\1/; p; }' "$f" | awk '{ print length }' | sort -n | head -1)
+ if [ -n "$min_indent" ] && [ "$min_indent" -gt 0 ]; then
+ if sed -i '' "s/^ \{1,$min_indent\}//" "$f" 2>/dev/null; then
+ : # macOS sed
+ else
+ sed -i "s/^ \{1,$min_indent\}//" "$f" # Linux sed
+ fi
+ fi
+done
+
+echo "Done: $(ls "$OUTDIR"/*."$EXT" 2>/dev/null | wc -l | tr -d ' ') snippets extracted"
\ No newline at end of file
diff --git a/fabric/examples/go/go.mod b/fabric/examples/go/go.mod
new file mode 100644
index 0000000..c95635f
--- /dev/null
+++ b/fabric/examples/go/go.mod
@@ -0,0 +1,13 @@
+module example-go
+
+go 1.22
+
+require github.com/spacesprotocol/fabric-go v0.0.0-dev.20260401232107
+
+require (
+ github.com/spacesprotocol/libveritas-go v0.0.0-dev.20260401232107 // indirect
+ github.com/btcsuite/btcd/btcec/v2 v2.3.6 // indirect
+ github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
+ github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+)
diff --git a/fabric/examples/go/main.go b/fabric/examples/go/main.go
new file mode 100644
index 0000000..728278b
--- /dev/null
+++ b/fabric/examples/go/main.go
@@ -0,0 +1,325 @@
+package main
+
+import (
+ "encoding/hex"
+ "fmt"
+ "log"
+
+ fabric "github.com/spacesprotocol/fabric-go"
+ lv "github.com/spacesprotocol/libveritas-go"
+)
+
+/// Resolve a single handle
+func exampleResolve() error {
+ //
+ f := fabric.New()
+ resolved, err := f.Resolve("alice@bitcoin")
+ if err != nil {
+ return err
+ }
+ if resolved == nil {
+ fmt.Println("handle not found")
+ return nil
+ }
+
+ fmt.Printf("Handle found: %s\n", resolved.Zone.Handle)
+ //
+
+ return nil
+}
+
+/// Verification
+func exampleTrustAndVerification() error {
+ f := fabric.New()
+
+ //
+ // Before pinning a trust id: resolve uses observed (peer) state
+ // badge() returns Unverified
+ resolved, err := f.Resolve("alice@bitcoin")
+ if err != nil {
+ return err
+ }
+
+ f.Badge(resolved) // Unverified
+
+ // Pin trust from a QR scan
+ qr := "veritas://scan?id=14ef902621df01bdeee0b23fedf67458563a20df600af8979a4748dcd9d1b9f9"
+
+ // For highest level of trust (scan QR code from Veritas desktop)
+ if err := f.TrustFromQr(qr); err != nil {
+ return err
+ }
+
+ // Does not require re-resolving, badge now checks
+ // whether resolved was against a trusted root
+ f.Badge(resolved) // Orange if handle is sovereign (final certificate)
+
+ // Or from a semi-trusted source (e.g. an explorer you trust with qr scanned over HTTPS)
+ // .Badge() will not show Orange for roots in this trust pool,
+ // but it will not report it as "Unverified".
+ if err := f.SemiTrustFromQr(qr); err != nil {
+ return err
+ }
+
+ // Check current trust ids
+ f.Trusted() // pinned id from local verification
+ f.SemiTrusted() // pinned id from semi-trusted source
+ f.Observed() // latest from peers
+
+ // Clear trusted state
+ f.ClearTrusted()
+ f.ClearSemiTrusted()
+
+ //
+
+ return nil
+}
+
+/// Unpack records from a resolved handle
+func exampleUnpackRecords() error {
+ f := fabric.New()
+ resolved, err := f.Resolve("alice@bitcoin")
+ if err != nil {
+ return err
+ }
+
+ //
+ records, err := resolved.Zone.Records.Unpack()
+ if err != nil {
+ return err
+ }
+
+ for _, record := range records {
+ switch r := record.(type) {
+ case lv.ParsedRecordTxt:
+ fmt.Printf("txt %s=%s\n", r.Key, joinStrings(r.Value))
+ case lv.ParsedRecordAddr:
+ fmt.Printf("addr %s=%s\n", r.Key, joinStrings(r.Value))
+ }
+ }
+ //
+
+ return nil
+}
+
+/// Resolve multiple handles
+func exampleResolveAll() error {
+ f := fabric.New()
+
+ //
+ batch, err := f.ResolveAll([]string{"alice@bitcoin", "bob@bitcoin"})
+ if err != nil {
+ return err
+ }
+
+ for _, zone := range batch.Zones {
+ fmt.Printf("%s: %s\n", zone.Handle, zone.Sovereignty)
+ }
+ //
+
+ return nil
+}
+
+/// Pack records into a RecordSet
+func examplePackRecords() error {
+ //
+ records, err := lv.RecordSetPack([]lv.Record{
+ lv.RecordSeq{Version: 1},
+ lv.RecordTxt{Key: "website", Value: []string{"https://example.com"}},
+ lv.RecordAddr{Key: "btc", Value: []string{"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"}},
+ lv.RecordAddr{Key: "nostr", Value: []string{
+ "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6",
+ "wss://relay.example.com",
+ }},
+ })
+ //
+
+ if err != nil {
+ return err
+ }
+ _ = records
+ return nil
+}
+
+/// Publish signed records
+func examplePublish() error {
+ f := fabric.New()
+ secretKey, err := hex.DecodeString(
+ "0000000000000000000000000000000000000000000000000000000000000001",
+ )
+ if err != nil {
+ return err
+ }
+
+ rs, err := lv.RecordSetPack([]lv.Record{
+ lv.RecordSeq{Version: 1},
+ lv.RecordTxt{Key: "website", Value: []string{"https://example.com"}},
+ lv.RecordAddr{Key: "btc", Value: []string{"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"}},
+ })
+ if err != nil {
+ return err
+ }
+
+ //
+ cert, err := f.Export("alice@bitcoin")
+ if err != nil {
+ return err
+ }
+ err = f.Publish(cert, rs, secretKey, true)
+ if err != nil {
+ return err
+ }
+ //
+
+ return nil
+}
+
+/// Resolve by numeric ID
+func exampleResolveById() error {
+ f := fabric.New()
+
+ //
+ resolved, err := f.ResolveById("num1qx8dtlzq...")
+ if err != nil {
+ return err
+ }
+ if resolved == nil {
+ fmt.Println("handle not found")
+ return nil
+ }
+
+ fmt.Printf("Handle found: %s\n", resolved.Zone.Handle)
+ //
+
+ return nil
+}
+
+/// Search by address
+func exampleSearchAddr() error {
+ f := fabric.New()
+
+ //
+ batch, err := f.SearchAddr("btc", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
+ if err != nil {
+ return err
+ }
+
+ for _, zone := range batch.Zones {
+ fmt.Printf("%s: %s\n", zone.Handle, zone.Sovereignty)
+ }
+ //
+
+ return nil
+}
+
+/// Advanced: Build and broadcast a message manually
+func exampleMessageBuilder() error {
+ f := fabric.New()
+ secretKey, err := hex.DecodeString(
+ "0000000000000000000000000000000000000000000000000000000000000001",
+ )
+ if err != nil {
+ return err
+ }
+
+ certBytes, err := f.Export("alice@bitcoin")
+ if err != nil {
+ return err
+ }
+ records, err := lv.RecordSetPack([]lv.Record{
+ lv.RecordSeq{Version: 1},
+ lv.RecordAddr{Key: "btc", Value: []string{"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"}},
+ })
+ if err != nil {
+ return err
+ }
+
+ //
+ builder := lv.NewMessageBuilder()
+ if err := builder.AddHandle(certBytes, records.ToBytes()); err != nil {
+ return err
+ }
+
+ proofReq, err := builder.ChainProofRequest()
+ if err != nil {
+ return err
+ }
+ proofBytes, err := f.Prove([]byte(proofReq))
+ if err != nil {
+ return err
+ }
+
+ result, err := builder.Build(proofBytes)
+ if err != nil {
+ return err
+ }
+
+ for _, u := range result.Unsigned {
+ u.SetFlags(u.Flags() | lv.SigPrimaryZone())
+ sig, err := fabric.SignSchnorr(u.SigningId(), secretKey)
+ if err != nil {
+ return err
+ }
+ if err := result.Message.SetRecords(u.Canonical(), u.PackSig(sig)); err != nil {
+ return err
+ }
+ }
+
+ if err := f.Broadcast(result.Message.ToBytes()); err != nil {
+ return err
+ }
+ //
+
+ return nil
+}
+
+func joinStrings(ss []string) string {
+ result := ""
+ for i, s := range ss {
+ if i > 0 {
+ result += ", "
+ }
+ result += s
+ }
+ return result
+}
+
+func main() {
+ if err := exampleResolve(); err != nil {
+ log.Fatal(err)
+ }
+
+ if err := exampleTrustAndVerification(); err != nil {
+ fmt.Printf("verification example failed (expected): %v\n", err)
+ }
+
+ if err := exampleUnpackRecords(); err != nil {
+ log.Fatal(err)
+ }
+
+ if err := exampleResolveAll(); err != nil {
+ log.Fatal(err)
+ }
+
+ if err := examplePackRecords(); err != nil {
+ log.Fatal(err)
+ }
+
+ if err := examplePublish(); err != nil {
+ log.Fatal(err)
+ }
+
+ if err := exampleResolveById(); err != nil {
+ log.Fatal(err)
+ }
+
+ if err := exampleSearchAddr(); err != nil {
+ log.Fatal(err)
+ }
+
+ if err := exampleMessageBuilder(); err != nil {
+ log.Fatal(err)
+ }
+
+ fmt.Println("Done!")
+}
diff --git a/fabric/examples/js/index.mjs b/fabric/examples/js/index.mjs
new file mode 100644
index 0000000..a8c4da1
--- /dev/null
+++ b/fabric/examples/js/index.mjs
@@ -0,0 +1,206 @@
+import { Fabric, RecordSet, Record, MessageBuilder } from "@spacesprotocol/fabric-web";
+import { signSchnorr } from "@spacesprotocol/fabric-web/signing";
+
+/// Resolve a single handle
+async function exampleResolve() {
+ //
+ const fabric = new Fabric();
+ const resolved = await fabric.resolve("alice@bitcoin");
+ if (!resolved) {
+ console.log("handle not found");
+ return;
+ }
+
+ console.log(`Handle found: ${resolved.zone.handle}`);
+ //
+}
+
+/// Verification
+async function exampleTrustAndVerification() {
+ const fabric = new Fabric();
+
+ //
+ // Before pinning a trust id: resolve uses observed (peer) state
+ // badge() returns "unverified"
+ const resolved = await fabric.resolve("alice@bitcoin");
+
+ fabric.badge(resolved); // "unverified"
+
+ // Pin trust from a QR scan
+ const qr = "veritas://scan?id=14ef902621df01bdeee0b23fedf67458563a20df600af8979a4748dcd9d1b9f9";
+
+ // For highest level of trust (scan QR code from Veritas desktop)
+ await fabric.trustFromQr(qr);
+
+ // Does not require re-resolving, badge now checks
+ // whether resolved was against a trusted root
+ fabric.badge(resolved); // "orange" if handle is sovereign (final certificate)
+
+ // Or from a semi-trusted source (e.g. an explorer you trust with qr scanned over HTTPS)
+ // .badge() will not show "orange" for roots in this trust pool,
+ // but it will not report it as "unverified".
+ await fabric.semiTrustFromQr(qr);
+
+ // Check current trust ids
+ fabric.trusted(); // pinned id from local verification
+ fabric.semiTrusted(); // pinned id from semi-trusted source
+ fabric.observed(); // latest from peers
+
+ // Clear trusted state
+ fabric.clearTrusted();
+ //
+}
+
+/// Unpack records from a resolved handle
+async function exampleUnpackRecords() {
+ const fabric = new Fabric();
+ const resolved = await fabric.resolve("alice@bitcoin");
+
+ //
+ const json = resolved.zone.toJson();
+
+ for (const record of json.records) {
+ if (record.type === "txt") {
+ console.log(`txt ${record.key}=${record.value.join(", ")}`);
+ } else if (record.type === "addr") {
+ console.log(`addr ${record.key}=${record.value.join(", ")}`);
+ }
+ }
+ //
+}
+
+/// Resolve multiple handles
+async function exampleResolveAll() {
+ const fabric = new Fabric();
+
+ //
+ const batch = await fabric.resolveAll(["alice@bitcoin", "bob@bitcoin"]);
+
+ for (const zone of batch.zones) {
+ console.log(`${zone.handle}`);
+ }
+ //
+}
+
+/// Pack records into a RecordSet
+function examplePackRecords() {
+ //
+ const records = RecordSet.pack([
+ Record.seq(1n),
+ Record.txt("website", ["https://example.com"]),
+ Record.addr("btc", ["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"]),
+ Record.addr("nostr", [
+ "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6",
+ "wss://relay.example.com",
+ ]),
+ ]);
+ //
+}
+
+/// Publish signed records
+async function examplePublish() {
+ const fabric = new Fabric();
+ const secretKey = new Uint8Array(Buffer.from(
+ "0000000000000000000000000000000000000000000000000000000000000001", "hex"
+ ));
+
+ const rs = RecordSet.pack([
+ Record.seq(1n),
+ Record.txt("website", ["https://example.com"]),
+ Record.addr("btc", ["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"]),
+ ]);
+
+ //
+ const cert = await fabric.export("alice@bitcoin");
+ await fabric.publish({
+ cert,
+ records: rs.toBytes(),
+ sign: (digest) => signSchnorr(digest, secretKey),
+ primary: true,
+ });
+ //
+}
+
+/// Resolve by numeric ID
+async function exampleResolveById() {
+ const fabric = new Fabric();
+
+ //
+ const resolved = await fabric.resolveById("num1qx8dtlzq...");
+ if (!resolved) {
+ console.log("handle not found");
+ return;
+ }
+
+ console.log(`Handle found: ${resolved.zone.handle}`);
+ //
+}
+
+/// Search by address
+async function exampleSearchAddr() {
+ const fabric = new Fabric();
+
+ //
+ const batch = await fabric.searchAddr("btc", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4");
+
+ for (const zone of batch.zones) {
+ console.log(`${zone.handle}`);
+ }
+ //
+}
+
+/// Advanced: Build and broadcast a message manually
+async function exampleMessageBuilder() {
+ const fabric = new Fabric();
+ const secretKey = new Uint8Array(Buffer.from(
+ "0000000000000000000000000000000000000000000000000000000000000001", "hex"
+ ));
+
+ const certBytes = await fabric.export("alice@bitcoin");
+ const rs = RecordSet.pack([
+ Record.seq(1n),
+ Record.addr("btc", ["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"]),
+ ]);
+
+ //
+ const builder = new MessageBuilder();
+ builder.addHandle(certBytes, rs.toBytes());
+
+ const chainProofReq = builder.chainProofRequest();
+ const chainProof = await fabric.prove(
+ typeof chainProofReq === "string" ? chainProofReq : JSON.stringify(chainProofReq)
+ );
+
+ const { message, unsigned } = builder.build(chainProof);
+
+ for (const u of unsigned) {
+ u.setFlags(u.flags() | 0x01); // SIG_PRIMARY_ZONE
+ const sig = signSchnorr(u.signingId(), secretKey);
+ message.setRecords(u.canonical(), u.packSig(sig));
+ }
+
+ await fabric.broadcast(message.toBytes());
+ //
+}
+
+async function main() {
+ await exampleResolve();
+
+ try {
+ await exampleTrustAndVerification();
+ } catch (e) {
+ // Expected to fail with example QR ID
+ }
+
+ await exampleUnpackRecords();
+ await exampleResolveAll();
+ examplePackRecords();
+ await examplePublish();
+ await exampleResolveById();
+ await exampleSearchAddr();
+ await exampleMessageBuilder();
+
+ console.log("Done!");
+}
+
+main().catch(console.error);
diff --git a/fabric/examples/kotlin/Example.kt b/fabric/examples/kotlin/Example.kt
new file mode 100644
index 0000000..afa2e3c
--- /dev/null
+++ b/fabric/examples/kotlin/Example.kt
@@ -0,0 +1,206 @@
+import org.spacesprotocol.fabric.Fabric
+import org.spacesprotocol.fabric.RecordSet
+import org.spacesprotocol.fabric.Record
+import org.spacesprotocol.fabric.SignSchnorr
+import org.spacesprotocol.fabric.CertificateChain
+import org.spacesprotocol.fabric.MessageBuilder
+import org.spacesprotocol.fabric.ChainProof
+import org.spacesprotocol.fabric.SIG_PRIMARY_ZONE
+
+/// Resolve a single handle
+suspend fun exampleResolve() {
+ //
+ val fabric = Fabric()
+ val resolved = fabric.resolve("alice@bitcoin")
+ if (resolved == null) {
+ println("handle not found")
+ return
+ }
+
+ println("Handle found: ${resolved.zone.handle}")
+ //
+}
+
+/// Verification
+suspend fun exampleTrustAndVerification() {
+ val fabric = Fabric()
+
+ //
+ // Before pinning a trust id: resolve uses observed (peer) state
+ // badge() returns Unverified
+ val resolved = fabric.resolve("alice@bitcoin")
+ ?: throw IllegalStateException("handle exists")
+
+ fabric.badge(resolved) // Unverified
+
+ // Pin trust from a QR scan
+ val qr = "veritas://scan?id=14ef902621df01bdeee0b23fedf67458563a20df600af8979a4748dcd9d1b9f9"
+
+ // For highest level of trust (scan QR code from Veritas desktop)
+ fabric.trustFromQr(qr)
+
+ // Does not require re-resolving, badge now checks
+ // whether resolved was against a trusted root
+ fabric.badge(resolved) // Orange if handle is sovereign (final certificate)
+
+ // Or from a semi-trusted source (e.g. an explorer you trust with qr scanned over HTTPS)
+ // .badge() will not show Orange for roots in this trust pool,
+ // but it will not report it as "Unverified".
+ fabric.semiTrustFromQr(qr)
+
+ // Check current trust ids
+ fabric.trusted() // pinned id from local verification
+ fabric.semiTrusted() // pinned id from semi-trusted source
+ fabric.observed() // latest from peers
+
+ // Clear trusted state
+ fabric.clearTrusted()
+ fabric.clearSemiTrusted()
+
+ //
+}
+
+/// Unpack records from a resolved handle
+suspend fun exampleUnpackRecords() {
+ val fabric = Fabric()
+ val resolved = fabric.resolve("alice@bitcoin")
+ ?: throw IllegalStateException("handle exists")
+
+ //
+ val records = resolved.zone.records.unpack()
+
+ for (record in records) {
+ when (record) {
+ is Record.Txt -> println("txt ${record.key}=${record.value.joinToString(", ")}")
+ is Record.Addr -> println("addr ${record.key}=${record.value.joinToString(", ")}")
+ else -> {}
+ }
+ }
+ //
+}
+
+/// Resolve multiple handles
+suspend fun exampleResolveAll() {
+ val fabric = Fabric()
+
+ //
+ val batch = fabric.resolveAll(listOf("alice@bitcoin", "bob@bitcoin"))
+
+ for (zone in batch.zones) {
+ println("${zone.handle}: ${zone.sovereignty}")
+ }
+ //
+}
+
+/// Pack records into a RecordSet
+fun examplePackRecords() {
+ //
+ val records = RecordSet.pack(listOf(
+ Record.seq(1),
+ Record.txt("website", listOf("https://example.com")),
+ Record.addr("btc", listOf("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")),
+ Record.addr("nostr", listOf(
+ "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6",
+ "wss://relay.example.com",
+ )),
+ ))
+ //
+}
+
+/// Publish signed records
+suspend fun examplePublish() {
+ val fabric = Fabric()
+ val secretKey = "0000000000000000000000000000000000000000000000000000000000000001"
+ .chunked(2).map { it.toInt(16).toByte() }.toByteArray()
+
+ val rs = RecordSet.pack(listOf(
+ Record.seq(1),
+ Record.txt("website", listOf("https://example.com")),
+ Record.addr("btc", listOf("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")),
+ ))
+
+ //
+ val cert = fabric.export("alice@bitcoin")
+ fabric.publish(cert, rs, secretKey, primary = true)
+ //
+}
+
+/// Resolve by numeric ID
+suspend fun exampleResolveById() {
+ val fabric = Fabric()
+
+ //
+ val resolved = fabric.resolveById("num1qx8dtlzq...")
+ if (resolved == null) {
+ println("handle not found")
+ return
+ }
+
+ println("Handle found: ${resolved.zone.handle}")
+ //
+}
+
+/// Search by address
+suspend fun exampleSearchAddr() {
+ val fabric = Fabric()
+
+ //
+ val batch = fabric.searchAddr("btc", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
+
+ for (zone in batch.zones) {
+ println("${zone.handle}: ${zone.sovereignty}")
+ }
+ //
+}
+
+/// Advanced: Build and broadcast a message manually
+suspend fun exampleMessageBuilder() {
+ val fabric = Fabric()
+ val secretKey = "0000000000000000000000000000000000000000000000000000000000000001"
+ .chunked(2).map { it.toInt(16).toByte() }.toByteArray()
+
+ val certBytes = fabric.export("alice@bitcoin")
+ val records = RecordSet.pack(listOf(
+ Record.seq(1),
+ Record.addr("btc", listOf("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")),
+ ))
+
+ //
+ val cert = CertificateChain.fromSlice(certBytes)
+ val builder = MessageBuilder()
+ builder.addHandle(cert, records)
+
+ val proofBytes = fabric.prove(builder.chainProofRequest())
+ val proof = ChainProof.fromSlice(proofBytes)
+
+ val (msg, unsigned) = builder.build(proof)
+
+ for (u in unsigned) {
+ u.flags = u.flags or SIG_PRIMARY_ZONE
+ val sig = SignSchnorr(u.signingId(), secretKey)
+ msg.setRecords(u.canonical, u.packSig(sig))
+ }
+
+ fabric.broadcast(msg.toBytes())
+ //
+}
+
+suspend fun main() {
+ exampleResolve()
+
+ try {
+ exampleTrustAndVerification()
+ } catch (e: Exception) {
+ println("verification example failed (expected): ${e.message}")
+ }
+
+ exampleUnpackRecords()
+ exampleResolveAll()
+ examplePackRecords()
+ examplePublish()
+ exampleResolveById()
+ exampleSearchAddr()
+ exampleMessageBuilder()
+
+ println("Done!")
+}
diff --git a/fabric/examples/python/example.py b/fabric/examples/python/example.py
new file mode 100644
index 0000000..836b0db
--- /dev/null
+++ b/fabric/examples/python/example.py
@@ -0,0 +1,198 @@
+import asyncio
+
+from fabric import Fabric, RecordSet, Record, sign_schnorr
+from fabric import CertificateChain, MessageBuilder, ChainProof, SIG_PRIMARY_ZONE
+
+
+async def example_resolve():
+ """Resolve a single handle"""
+ #
+ fabric = Fabric()
+ resolved = await fabric.resolve("alice@bitcoin")
+ if resolved is None:
+ print("handle not found")
+ return
+
+ print(f"Handle found: {resolved.zone.handle}")
+ #
+
+
+async def example_trust_and_verification():
+ """Verification"""
+ fabric = Fabric()
+
+ #
+ # Before pinning a trust id: resolve uses observed (peer) state
+ # badge() returns Unverified
+ resolved = await fabric.resolve("alice@bitcoin")
+
+ fabric.badge(resolved) # Unverified
+
+ # Pin trust from a QR scan
+ qr = "veritas://scan?id=14ef902621df01bdeee0b23fedf67458563a20df600af8979a4748dcd9d1b9f9"
+
+ # For highest level of trust (scan QR code from Veritas desktop)
+ await fabric.trust_from_qr(qr)
+
+ # Does not require re-resolving, badge now checks
+ # whether resolved was against a trusted root
+ fabric.badge(resolved) # Orange if handle is sovereign (final certificate)
+
+ # Or from a semi-trusted source (e.g. an explorer you trust with qr scanned over HTTPS)
+ # .badge() will not show Orange for roots in this trust pool,
+ # but it will not report it as "Unverified".
+ await fabric.semi_trust_from_qr(qr)
+
+ # Check current trust ids
+ fabric.trusted() # pinned id from local verification
+ fabric.semi_trusted() # pinned id from semi-trusted source
+ fabric.observed() # latest from peers
+
+ # Clear trusted state
+ fabric.clear_trusted()
+ fabric.clear_semi_trusted()
+
+ #
+
+
+async def example_unpack_records():
+ """Unpack records from a resolved handle"""
+ fabric = Fabric()
+ resolved = await fabric.resolve("alice@bitcoin")
+
+ #
+ records = resolved.zone.records.unpack()
+
+ for record in records:
+ if record.type == "txt":
+ print(f"txt {record.key}={', '.join(record.value)}")
+ elif record.type == "addr":
+ print(f"addr {record.key}={', '.join(record.value)}")
+ #
+
+
+async def example_resolve_all():
+ """Resolve multiple handles"""
+ fabric = Fabric()
+
+ #
+ batch = await fabric.resolve_all(["alice@bitcoin", "bob@bitcoin"])
+
+ for zone in batch.zones:
+ print(f"{zone.handle}: {zone.sovereignty}")
+ #
+
+
+def example_pack_records():
+ """Pack records into a RecordSet"""
+ #
+ records = RecordSet.pack([
+ Record.seq(1),
+ Record.txt("website", ["https://example.com"]),
+ Record.addr("btc", ["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"]),
+ Record.addr("nostr", [
+ "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6",
+ "wss://relay.example.com",
+ ]),
+ ])
+ #
+
+
+async def example_publish():
+ """Publish signed records"""
+ fabric = Fabric()
+ secret_key = bytes.fromhex(
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ )
+
+ rs = RecordSet.pack([
+ Record.seq(1),
+ Record.txt("website", ["https://example.com"]),
+ Record.addr("btc", ["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"]),
+ ])
+
+ #
+ cert = await fabric.export("alice@bitcoin")
+ await fabric.publish(cert, rs, secret_key, primary=True)
+ #
+
+
+async def example_resolve_by_id():
+ """Resolve by numeric ID"""
+ fabric = Fabric()
+
+ #
+ resolved = await fabric.resolve_by_id("num1qx8dtlzq...")
+ if resolved is None:
+ print("handle not found")
+ return
+
+ print(f"Handle found: {resolved.zone.handle}")
+ #
+
+
+async def example_search_addr():
+ """Search by address"""
+ fabric = Fabric()
+
+ #
+ batch = await fabric.search_addr("btc", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
+
+ for zone in batch.zones:
+ print(f"{zone.handle}: {zone.sovereignty}")
+ #
+
+
+async def example_message_builder():
+ """Advanced: Build and broadcast a message manually"""
+ fabric = Fabric()
+ secret_key = bytes.fromhex(
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ )
+
+ cert_bytes = await fabric.export("alice@bitcoin")
+ records = RecordSet.pack([
+ Record.seq(1),
+ Record.addr("btc", ["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"]),
+ ])
+
+ #
+ cert = CertificateChain.from_slice(cert_bytes)
+ builder = MessageBuilder()
+ builder.add_handle(cert, records)
+
+ proof_bytes = await fabric.prove(builder.chain_proof_request())
+ proof = ChainProof.from_slice(proof_bytes)
+
+ msg, unsigned = builder.build(proof)
+
+ for u in unsigned:
+ u.flags |= SIG_PRIMARY_ZONE
+ sig = sign_schnorr(u.signing_id(), secret_key)
+ msg.set_records(u.canonical, u.pack_sig(sig))
+
+ await fabric.broadcast(msg.to_bytes())
+ #
+
+
+async def main():
+ await example_resolve()
+
+ try:
+ await example_trust_and_verification()
+ except Exception as e:
+ print(f"verification example failed (expected): {e}")
+
+ await example_unpack_records()
+ await example_resolve_all()
+ example_pack_records()
+ await example_publish()
+ await example_resolve_by_id()
+ await example_search_addr()
+ await example_message_builder()
+
+ print("Done!")
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
diff --git a/fabric/examples/rust/Cargo.lock b/fabric/examples/rust/Cargo.lock
new file mode 100644
index 0000000..fe7525c
--- /dev/null
+++ b/fabric/examples/rust/Cargo.lock
@@ -0,0 +1,2833 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "ahash"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
+
+[[package]]
+name = "anyhow"
+version = "1.0.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
+
+[[package]]
+name = "ark-bn254"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc"
+dependencies = [
+ "ark-ec",
+ "ark-ff",
+ "ark-r1cs-std",
+ "ark-std",
+]
+
+[[package]]
+name = "ark-crypto-primitives"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0c292754729c8a190e50414fd1a37093c786c709899f29c9f7daccecfa855e"
+dependencies = [
+ "ahash",
+ "ark-crypto-primitives-macros",
+ "ark-ec",
+ "ark-ff",
+ "ark-relations",
+ "ark-serialize",
+ "ark-snark",
+ "ark-std",
+ "blake2",
+ "derivative",
+ "digest",
+ "fnv",
+ "merlin",
+ "sha2",
+]
+
+[[package]]
+name = "ark-crypto-primitives-macros"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7e89fe77d1f0f4fe5b96dfc940923d88d17b6a773808124f21e764dfb063c6a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "ark-ec"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce"
+dependencies = [
+ "ahash",
+ "ark-ff",
+ "ark-poly",
+ "ark-serialize",
+ "ark-std",
+ "educe",
+ "fnv",
+ "hashbrown 0.15.5",
+ "itertools",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+ "zeroize",
+]
+
+[[package]]
+name = "ark-ff"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70"
+dependencies = [
+ "ark-ff-asm",
+ "ark-ff-macros",
+ "ark-serialize",
+ "ark-std",
+ "arrayvec",
+ "digest",
+ "educe",
+ "itertools",
+ "num-bigint",
+ "num-traits",
+ "paste",
+ "zeroize",
+]
+
+[[package]]
+name = "ark-ff-asm"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60"
+dependencies = [
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "ark-ff-macros"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3"
+dependencies = [
+ "num-bigint",
+ "num-traits",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "ark-groth16"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88f1d0f3a534bb54188b8dcc104307db6c56cdae574ddc3212aec0625740fc7e"
+dependencies = [
+ "ark-crypto-primitives",
+ "ark-ec",
+ "ark-ff",
+ "ark-poly",
+ "ark-relations",
+ "ark-serialize",
+ "ark-std",
+]
+
+[[package]]
+name = "ark-poly"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27"
+dependencies = [
+ "ahash",
+ "ark-ff",
+ "ark-serialize",
+ "ark-std",
+ "educe",
+ "fnv",
+ "hashbrown 0.15.5",
+]
+
+[[package]]
+name = "ark-r1cs-std"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1"
+dependencies = [
+ "ark-ec",
+ "ark-ff",
+ "ark-relations",
+ "ark-std",
+ "educe",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+ "tracing",
+]
+
+[[package]]
+name = "ark-relations"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384"
+dependencies = [
+ "ark-ff",
+ "ark-std",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "ark-serialize"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7"
+dependencies = [
+ "ark-serialize-derive",
+ "ark-std",
+ "arrayvec",
+ "digest",
+ "num-bigint",
+]
+
+[[package]]
+name = "ark-serialize-derive"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "ark-snark"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d368e2848c2d4c129ce7679a7d0d2d612b6a274d3ea6a13bad4445d61b381b88"
+dependencies = [
+ "ark-ff",
+ "ark-relations",
+ "ark-serialize",
+ "ark-std",
+]
+
+[[package]]
+name = "ark-std"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a"
+dependencies = [
+ "num-traits",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "base58ck"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f"
+dependencies = [
+ "bitcoin-internals",
+ "bitcoin_hashes",
+]
+
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bech32"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f"
+
+[[package]]
+name = "bit-vec"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
+
+[[package]]
+name = "bitcoin"
+version = "0.32.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e499f9fc0407f50fe98af744ab44fa67d409f76b6772e1689ec8485eb0c0f66"
+dependencies = [
+ "base58ck",
+ "base64 0.21.7",
+ "bech32",
+ "bitcoin-internals",
+ "bitcoin-io",
+ "bitcoin-units",
+ "bitcoin_hashes",
+ "hex-conservative",
+ "hex_lit",
+ "secp256k1",
+ "serde",
+]
+
+[[package]]
+name = "bitcoin-internals"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bitcoin-io"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953"
+
+[[package]]
+name = "bitcoin-units"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2"
+dependencies = [
+ "bitcoin-internals",
+ "serde",
+]
+
+[[package]]
+name = "bitcoin_hashes"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b"
+dependencies = [
+ "bitcoin-io",
+ "hex-conservative",
+ "serde",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
+
+[[package]]
+name = "blake2"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "borsh"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a"
+dependencies = [
+ "borsh-derive",
+ "bytes",
+ "cfg_aliases",
+]
+
+[[package]]
+name = "borsh-derive"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59"
+dependencies = [
+ "once_cell",
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "borsh_utils"
+version = "0.1.0"
+source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#8bfdffc02d7a49c07f7bf7da77d63a606a502515"
+dependencies = [
+ "bitcoin",
+ "borsh",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
+
+[[package]]
+name = "bytemuck"
+version = "1.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
+
+[[package]]
+name = "cc"
+version = "1.2.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283"
+dependencies = [
+ "find-msvc-tools",
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "cobs"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "libc",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "dashmap"
+version = "6.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+ "hashbrown 0.14.5",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "derivative"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "derive_more"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
+dependencies = [
+ "derive_more-impl",
+]
+
+[[package]]
+name = "derive_more-impl"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 2.0.117",
+ "unicode-xid",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "const-oid",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
+[[package]]
+name = "educe"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417"
+dependencies = [
+ "enum-ordinalize",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "elf"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b"
+
+[[package]]
+name = "embedded-io"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
+
+[[package]]
+name = "embedded-io"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
+
+[[package]]
+name = "enum-ordinalize"
+version = "4.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0"
+dependencies = [
+ "enum-ordinalize-derive",
+]
+
+[[package]]
+name = "enum-ordinalize-derive"
+version = "4.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "fabric"
+version = "0.1.0"
+dependencies = [
+ "dashmap",
+ "hex",
+ "libveritas",
+ "rand 0.9.2",
+ "reqwest",
+ "secp256k1",
+ "serde",
+ "serde_json",
+ "sha2",
+ "spaces_nums",
+ "tokio",
+]
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
+
+[[package]]
+name = "futures-task"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
+
+[[package]]
+name = "futures-util"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "r-efi",
+ "wasip2",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+dependencies = [
+ "allocator-api2",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hex-conservative"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f"
+dependencies = [
+ "arrayvec",
+]
+
+[[package]]
+name = "hex-literal"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
+
+[[package]]
+name = "hex_lit"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd"
+
+[[package]]
+name = "http"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
+dependencies = [
+ "bytes",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+
+[[package]]
+name = "hyper"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "http",
+ "http-body",
+ "httparse",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.27.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
+dependencies = [
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+ "webpki-roots",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "ipnet",
+ "libc",
+ "percent-encoding",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "utf8_iter",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
+dependencies = [
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
+
+[[package]]
+name = "icu_properties"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
+dependencies = [
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
+
+[[package]]
+name = "icu_provider"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "include_bytes_aligned"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784"
+
+[[package]]
+name = "indexmap"
+version = "2.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.16.1",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
+
+[[package]]
+name = "iri-string"
+version = "0.7.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+
+[[package]]
+name = "js-sys"
+version = "0.3.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "keccak"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653"
+dependencies = [
+ "cpufeatures",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+dependencies = [
+ "spin",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.184"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
+
+[[package]]
+name = "libm"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
+
+[[package]]
+name = "libveritas"
+version = "0.1.0"
+source = "git+https://github.com/spacesprotocol/libveritas.git#a7d86225bac32cf26383cd8b9d2fcff6d8ce7829"
+dependencies = [
+ "base64 0.22.1",
+ "borsh",
+ "hex",
+ "libveritas_zk",
+ "risc0-zkvm",
+ "serde",
+ "serde_json",
+ "sip7",
+ "spacedb",
+ "spaces_nums",
+ "spaces_protocol",
+]
+
+[[package]]
+name = "libveritas_zk"
+version = "0.1.0"
+source = "git+https://github.com/spacesprotocol/libveritas.git#a7d86225bac32cf26383cd8b9d2fcff6d8ce7829"
+dependencies = [
+ "borsh",
+ "serde",
+ "spacedb",
+]
+
+[[package]]
+name = "litemap"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
+
+[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
+[[package]]
+name = "lru-slab"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
+
+[[package]]
+name = "merlin"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d"
+dependencies = [
+ "byteorder",
+ "keccak",
+ "rand_core 0.6.4",
+ "zeroize",
+]
+
+[[package]]
+name = "metal"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21"
+dependencies = [
+ "bitflags 2.11.0",
+ "block",
+ "core-graphics-types",
+ "foreign-types",
+ "log",
+ "objc",
+ "paste",
+]
+
+[[package]]
+name = "mio"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "no_std_strings"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5b0c77c1b780822bc749a33e39aeb2c07584ab93332303babeabb645298a76e"
+
+[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
+dependencies = [
+ "num_enum_derive",
+ "rustversion",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-link",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
+[[package]]
+name = "postcard"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
+dependencies = [
+ "cobs",
+ "embedded-io 0.4.0",
+ "embedded-io 0.6.1",
+ "serde",
+]
+
+[[package]]
+name = "potential_utf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
+dependencies = [
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proptest"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
+dependencies = [
+ "bitflags 2.11.0",
+ "num-traits",
+ "rand 0.9.2",
+ "rand_chacha 0.9.0",
+ "rand_xorshift",
+ "unarray",
+]
+
+[[package]]
+name = "quinn"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
+dependencies = [
+ "bytes",
+ "cfg_aliases",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash",
+ "rustls",
+ "socket2",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
+dependencies = [
+ "bytes",
+ "getrandom 0.3.4",
+ "lru-slab",
+ "rand 0.9.2",
+ "ring",
+ "rustc-hash",
+ "rustls",
+ "rustls-pki-types",
+ "slab",
+ "thiserror",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
+dependencies = [
+ "rand_chacha 0.9.0",
+ "rand_core 0.9.5",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "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"
+
+[[package]]
+name = "rand_core"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
+dependencies = [
+ "getrandom 0.3.4",
+]
+
+[[package]]
+name = "rand_xorshift"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
+dependencies = [
+ "rand_core 0.9.5",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags 2.11.0",
+]
+
+[[package]]
+name = "reqwest"
+version = "0.12.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-rustls",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tokio-rustls",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "webpki-roots",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.17",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "risc0-binfmt"
+version = "3.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1883f0c5d19b865f395209a137dcb29e56dc49951424967b8d0114c129f46e77"
+dependencies = [
+ "anyhow",
+ "borsh",
+ "bytemuck",
+ "derive_more",
+ "elf",
+ "lazy_static",
+ "postcard",
+ "rand 0.9.2",
+ "risc0-zkp",
+ "risc0-zkvm-platform",
+ "ruint",
+ "semver",
+ "serde",
+ "tracing",
+]
+
+[[package]]
+name = "risc0-circuit-keccak"
+version = "4.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f543c60287fece797a5da4209384ab1bfebd9644fcfe591e11b1aa85f1a02f8"
+dependencies = [
+ "anyhow",
+ "bytemuck",
+ "paste",
+ "risc0-binfmt",
+ "risc0-circuit-recursion",
+ "risc0-core",
+ "risc0-zkp",
+ "tracing",
+]
+
+[[package]]
+name = "risc0-circuit-recursion"
+version = "4.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2347e909c6b2a65584b5898f3802eec5b8c1b4b45329edfdd8587b6a04dd3357"
+dependencies = [
+ "anyhow",
+ "bytemuck",
+ "hex",
+ "metal",
+ "risc0-core",
+ "risc0-zkp",
+ "tracing",
+]
+
+[[package]]
+name = "risc0-circuit-rv32im"
+version = "4.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61676419814a818fdb5e10066b13c5488b3f54aa9668794bd06c99bc91bff1f2"
+dependencies = [
+ "anyhow",
+ "bit-vec",
+ "bytemuck",
+ "derive_more",
+ "paste",
+ "risc0-binfmt",
+ "risc0-core",
+ "risc0-zkp",
+ "serde",
+ "tracing",
+]
+
+[[package]]
+name = "risc0-core"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b956a976b8ce4713694dcc6c370b522a42ccef4ba45da5b6e57dbf26cdb7b1"
+dependencies = [
+ "bytemuck",
+ "rand_core 0.9.5",
+]
+
+[[package]]
+name = "risc0-groth16"
+version = "3.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc57e76bb87193d154ac5ee6ee352fbd7edabddab36f02a81f40a048e5ca14f9"
+dependencies = [
+ "anyhow",
+ "ark-bn254",
+ "ark-ec",
+ "ark-ff",
+ "ark-groth16",
+ "ark-serialize",
+ "bytemuck",
+ "hex",
+ "num-bigint",
+ "num-traits",
+ "risc0-binfmt",
+ "risc0-zkp",
+ "serde",
+]
+
+[[package]]
+name = "risc0-zkos-v1compat"
+version = "2.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bd70cb45b5d37d025f25663b87c6b9dc9df7f413ee2068531a57f50b0eb95db"
+dependencies = [
+ "include_bytes_aligned",
+ "no_std_strings",
+ "risc0-zkvm-platform",
+]
+
+[[package]]
+name = "risc0-zkp"
+version = "3.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f40d362a6c146ec6dc69208f539b92fd86e47b0dbc2083801423034a38155a2"
+dependencies = [
+ "anyhow",
+ "blake2",
+ "borsh",
+ "bytemuck",
+ "cfg-if",
+ "digest",
+ "hex",
+ "hex-literal",
+ "metal",
+ "paste",
+ "rand_core 0.9.5",
+ "risc0-core",
+ "risc0-zkvm-platform",
+ "serde",
+ "sha2",
+ "stability",
+ "tracing",
+]
+
+[[package]]
+name = "risc0-zkvm"
+version = "3.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22b7eafb5d85be59cbd9da83f662cf47d834f1b836e14f675d1530b12c666867"
+dependencies = [
+ "anyhow",
+ "borsh",
+ "bytemuck",
+ "derive_more",
+ "hex",
+ "risc0-binfmt",
+ "risc0-circuit-keccak",
+ "risc0-circuit-recursion",
+ "risc0-circuit-rv32im",
+ "risc0-core",
+ "risc0-groth16",
+ "risc0-zkos-v1compat",
+ "risc0-zkp",
+ "risc0-zkvm-platform",
+ "rrs-lib",
+ "semver",
+ "serde",
+ "sha2",
+ "stability",
+ "tracing",
+]
+
+[[package]]
+name = "risc0-zkvm-platform"
+version = "2.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4db893788c416287e2e1a87e6b8f5302511a04a45329e699d6a32a16874fd24f"
+dependencies = [
+ "bytemuck",
+ "cfg-if",
+ "getrandom 0.2.17",
+ "getrandom 0.3.4",
+ "libm",
+ "num_enum",
+ "paste",
+ "stability",
+]
+
+[[package]]
+name = "rrs-lib"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4382d3af3a4ebdae7f64ba6edd9114fff92c89808004c4943b393377a25d001"
+dependencies = [
+ "downcast-rs",
+ "paste",
+]
+
+[[package]]
+name = "ruint"
+version = "1.17.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a"
+dependencies = [
+ "borsh",
+ "proptest",
+ "rand 0.8.5",
+ "rand 0.9.2",
+ "ruint-macro",
+ "serde_core",
+ "valuable",
+ "zeroize",
+]
+
+[[package]]
+name = "ruint-macro"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18"
+
+[[package]]
+name = "rust"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "fabric",
+ "hex",
+ "tokio",
+]
+
+[[package]]
+name = "rustc-hash"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
+dependencies = [
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
+dependencies = [
+ "web-time",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "ryu"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "secp256k1"
+version = "0.29.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113"
+dependencies = [
+ "bitcoin_hashes",
+ "secp256k1-sys",
+ "serde",
+]
+
+[[package]]
+name = "secp256k1-sys"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
+dependencies = [
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.149"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "sip7"
+version = "0.1.0"
+source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#8bfdffc02d7a49c07f7bf7da77d63a606a502515"
+dependencies = [
+ "base64 0.22.1",
+ "hex",
+ "serde",
+ "spaces_protocol",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "socket2"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "spacedb"
+version = "0.0.12"
+source = "git+https://github.com/spacesprotocol/spacedb.git#43f23e78e4e5fffb8d89661a4b7f39ab43a5a644"
+dependencies = [
+ "borsh",
+ "sha2",
+]
+
+[[package]]
+name = "spaces_nums"
+version = "0.1.0"
+source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#8bfdffc02d7a49c07f7bf7da77d63a606a502515"
+dependencies = [
+ "bech32",
+ "bitcoin",
+ "borsh",
+ "borsh_utils",
+ "hex",
+ "log",
+ "serde",
+ "spaces_protocol",
+]
+
+[[package]]
+name = "spaces_protocol"
+version = "0.0.7"
+source = "git+https://github.com/spacesprotocol/spaces.git?branch=subspaces#8bfdffc02d7a49c07f7bf7da77d63a606a502515"
+dependencies = [
+ "bitcoin",
+ "borsh",
+ "borsh_utils",
+ "serde",
+]
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "stability"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac"
+dependencies = [
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd"
+dependencies = [
+ "bytes",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "1.1.1+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.25.10+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a82418ca169e235e6c399a84e395ab6debeb3bc90edc959bf0f48647c6a32d1b"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "toml_parser",
+ "winnow",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.1.2+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
+dependencies = [
+ "winnow",
+]
+
+[[package]]
+name = "tower"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
+dependencies = [
+ "bitflags 2.11.0",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "iri-string",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71"
+dependencies = [
+ "tracing-core",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "typenum"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
+
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "url"
+version = "2.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasip2"
+version = "1.0.2+wasi-0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.67"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
+[[package]]
+name = "winnow"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+
+[[package]]
+name = "writeable"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
+
+[[package]]
+name = "yoke"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
+dependencies = [
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "synstructure",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "synstructure",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "zerotrie"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "zmij"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
diff --git a/fabric/examples/rust/Cargo.toml b/fabric/examples/rust/Cargo.toml
new file mode 100644
index 0000000..928bef0
--- /dev/null
+++ b/fabric/examples/rust/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "rust"
+version = "0.1.0"
+edition = "2024"
+
+[[bin]]
+name = "example-rust"
+path = "src/main.rs"
+
+[workspace]
+
+
+[dependencies]
+fabric = { path = "../../../fabric/rust", features = ["client", "signing"] }
+anyhow = "1.0.102"
+tokio = "1.38.2"
+hex = "0.4.3"
diff --git a/fabric/examples/rust/src/main.rs b/fabric/examples/rust/src/main.rs
new file mode 100644
index 0000000..3caab8c
--- /dev/null
+++ b/fabric/examples/rust/src/main.rs
@@ -0,0 +1,228 @@
+use fabric::client::{Fabric};
+use fabric::libveritas::builder::MessageBuilder;
+use fabric::libveritas::cert::CertificateChain;
+use fabric::libveritas::msg::ChainProof;
+use fabric::libveritas::sip7::{ParsedRecord, Record, RecordSet, SIG_PRIMARY_ZONE};
+use fabric::signing::sign_schnorr;
+
+/// Resolve a single handle
+async fn example_resolve() -> anyhow::Result<()> {
+ //
+ let fabric = Fabric::new();
+ let Some(resolved) = fabric.resolve("alice@bitcoin").await? else {
+ println!("handle not found");
+ return Ok(());
+ };
+
+ println!("Handle found: {}", resolved.zone.handle);
+ //
+
+ Ok(())
+}
+
+/// Verification
+async fn example_trust_and_verification() -> anyhow::Result<()> {
+ let fabric = Fabric::new();
+
+ //
+ // Before pinning a trust id: resolve uses observed (peer) state
+ // badge() returns Unverified
+ let resolved = fabric.resolve("alice@bitcoin").await?
+ .expect("handle exists");
+ fabric.badge(&resolved); // Unverified
+
+ // Pin trust from a QR scan
+ let qr = "veritas://scan?id=14ef902621df01bdeee0b23fedf67458563a20df600af8979a4748dcd9d1b9f9";
+
+ // For highest level of trust (scan QR code from Veritas desktop)
+ fabric.trust_from_qr(qr).await?;
+
+ // Does not require re-resolving, badge now checks
+ // whether resolved was against a trusted root
+ fabric.badge(&resolved); // Orange if handle is sovereign (final certificate)
+
+ // Or from a semi-trusted source (e.g. an explorer you trust with qr scanned over HTTPS)
+ // .badge() will not show Orange for roots in this trust pool,
+ // but it will not report it as "Unverified".
+ fabric.semi_trust_from_qr(qr).await?;
+
+ // Check current trust ids
+ fabric.trusted(); // pinned id from local verification
+ fabric.semi_trusted(); // pinned id from semi-trusted source
+ fabric.observed(); // latest from peers
+
+ // Clear trusted state
+ fabric.clear_trusted();
+ fabric.clear_semi_trusted();
+
+ //
+
+ Ok(())
+}
+
+/// Unpack records from a resolved handle
+async fn example_unpack_records() -> anyhow::Result<()> {
+ let fabric = Fabric::new();
+ let resolved = fabric.resolve("alice@bitcoin").await?.expect("handle exists");
+
+ //
+ for record in resolved.zone.records.unpack()? {
+ match record {
+ ParsedRecord::Txt { key, value } => {
+ println!("txt {}={}", key, value.to_vec().join(", "))
+ }
+ ParsedRecord::Addr { key, value } => {
+ println!("addr {}={}", key, value.to_vec().join(", "))
+ }
+ _ => {}
+ }
+ }
+ //
+
+ Ok(())
+}
+
+/// Resolve multiple handles
+async fn example_resolve_all() -> anyhow::Result<()> {
+ let fabric = Fabric::new();
+
+ //
+ let batch = fabric
+ .resolve_all(&["alice@bitcoin", "bob@bitcoin"])
+ .await?;
+
+ for zone in &batch.zones {
+ println!("{}: {:?}", zone.handle, zone.sovereignty);
+ }
+ //
+
+ Ok(())
+}
+
+/// Pack records into a RecordSet
+fn example_pack_records() -> anyhow::Result<()> {
+ //
+ let records = RecordSet::pack(vec![
+ Record::seq(1),
+ Record::txt("website", &["https://example.com"]),
+ Record::addr("btc", &["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"]),
+ Record::addr("nostr", &[
+ "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6",
+ "wss://relay.example.com",
+ ]),
+ ])?;
+ //
+
+ let _ = records;
+ Ok(())
+}
+
+/// Publish signed records
+async fn example_publish() -> anyhow::Result<()> {
+ let fabric = Fabric::new();
+ let secret_key: [u8; 32] = hex::decode(
+ "0000000000000000000000000000000000000000000000000000000000000001",
+ )?
+ .try_into()
+ .unwrap();
+
+ let records = RecordSet::pack(vec![
+ Record::seq(1),
+ Record::txt("website", &["https://example.com"]),
+ Record::addr("btc", &["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"]),
+ ])?;
+
+ //
+ let cert = fabric.export("alice@bitcoin").await?;
+ fabric.publish(&cert, records, &secret_key, true).await?;
+ //
+
+ Ok(())
+}
+
+/// Resolve by numeric ID
+async fn example_resolve_by_id() -> anyhow::Result<()> {
+ let fabric = Fabric::new();
+
+ //
+ let Some(resolved) = fabric.resolve_by_id("num1qx8dtlzq...").await? else {
+ println!("handle not found");
+ return Ok(());
+ };
+
+ println!("Handle found: {}", resolved.zone.handle);
+ //
+
+ Ok(())
+}
+
+/// Search by address
+async fn example_search_addr() -> anyhow::Result<()> {
+ let fabric = Fabric::new();
+
+ //
+ let batch = fabric
+ .search_addr("btc", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
+ .await?;
+
+ for zone in &batch.zones {
+ println!("{}: {:?}", zone.handle, zone.sovereignty);
+ }
+ //
+
+ Ok(())
+}
+
+/// Advanced: Build and broadcast a message manually
+async fn example_message_builder() -> anyhow::Result<()> {
+ let fabric = Fabric::new();
+ let secret_key: [u8; 32] = hex::decode(
+ "0000000000000000000000000000000000000000000000000000000000000001",
+ )?
+ .try_into()
+ .unwrap();
+
+ let cert_bytes = fabric.export("alice@bitcoin").await?;
+ let records = RecordSet::pack(vec![
+ Record::seq(1),
+ Record::addr("btc", &["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"]),
+ ])?;
+
+ //
+ let cert = CertificateChain::from_slice(&cert_bytes)?;
+ let mut builder = MessageBuilder::new();
+ builder.add_handle(cert, records);
+
+ let proof = ChainProof::from_slice(
+ &fabric.prove(&builder.chain_proof_request()).await?,
+ )?;
+
+ let (mut msg, unsigned) = builder.build(proof)?;
+
+ for mut u in unsigned {
+ u.flags |= SIG_PRIMARY_ZONE;
+ let sig = sign_schnorr(&u.signing_id(), &secret_key)?;
+ msg.set_records(&u.canonical, u.pack_sig(sig.to_vec()));
+ }
+
+ fabric.broadcast(&msg.to_bytes()).await?;
+ //
+
+ Ok(())
+}
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ example_resolve().await?;
+ let _ = example_trust_and_verification().await;
+ example_unpack_records().await?;
+ example_resolve_all().await?;
+ example_pack_records()?;
+ example_publish().await?;
+ example_resolve_by_id().await?;
+ example_search_addr().await?;
+ example_message_builder().await?;
+
+ println!("Done!");
+ Ok(())
+}
\ No newline at end of file
diff --git a/fabric/examples/swift/Example.swift b/fabric/examples/swift/Example.swift
new file mode 100644
index 0000000..faa32c4
--- /dev/null
+++ b/fabric/examples/swift/Example.swift
@@ -0,0 +1,213 @@
+import Fabric
+
+/// Resolve a single handle
+func exampleResolve() async throws {
+ //
+ let fabric = Fabric()
+ guard let resolved = try await fabric.resolve("alice@bitcoin") else {
+ print("handle not found")
+ return
+ }
+
+ print("Handle found: \(resolved.zone.handle)")
+ //
+}
+
+/// Verification
+func exampleTrustAndVerification() async throws {
+ let fabric = Fabric()
+
+ //
+ // Before pinning a trust id: resolve uses observed (peer) state
+ // badge() returns Unverified
+ let resolved = try await fabric.resolve("alice@bitcoin")!
+
+ fabric.badge(resolved) // Unverified
+
+ // Pin trust from a QR scan
+ let qr = "veritas://scan?id=14ef902621df01bdeee0b23fedf67458563a20df600af8979a4748dcd9d1b9f9"
+
+ // For highest level of trust (scan QR code from Veritas desktop)
+ try await fabric.trustFromQr(qr)
+
+ // Does not require re-resolving, badge now checks
+ // whether resolved was against a trusted root
+ fabric.badge(resolved) // Orange if handle is sovereign (final certificate)
+
+ // Or from a semi-trusted source (e.g. an explorer you trust with qr scanned over HTTPS)
+ // .badge() will not show Orange for roots in this trust pool,
+ // but it will not report it as "Unverified".
+ try await fabric.semiTrustFromQr(qr)
+
+ // Check current trust ids
+ fabric.trusted() // pinned id from local verification
+ fabric.semiTrusted() // pinned id from semi-trusted source
+ fabric.observed() // latest from peers
+
+ // Clear trusted state
+ fabric.clearTrusted()
+ fabric.clearSemiTrusted()
+
+ //
+}
+
+/// Unpack records from a resolved handle
+func exampleUnpackRecords() async throws {
+ let fabric = Fabric()
+ let resolved = try await fabric.resolve("alice@bitcoin")!
+
+ //
+ let records = try resolved.zone.records.unpack()
+
+ for record in records {
+ switch record {
+ case .txt(let key, let value):
+ print("txt \(key)=\(value.joined(separator: ", "))")
+ case .addr(let key, let value):
+ print("addr \(key)=\(value.joined(separator: ", "))")
+ default:
+ break
+ }
+ }
+ //
+}
+
+/// Resolve multiple handles
+func exampleResolveAll() async throws {
+ let fabric = Fabric()
+
+ //
+ let batch = try await fabric.resolveAll(["alice@bitcoin", "bob@bitcoin"])
+
+ for zone in batch.zones {
+ print("\(zone.handle): \(zone.sovereignty)")
+ }
+ //
+}
+
+/// Pack records into a RecordSet
+func examplePackRecords() throws {
+ //
+ let records = try RecordSet.pack([
+ Record.seq(1),
+ Record.txt("website", ["https://example.com"]),
+ Record.addr("btc", ["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"]),
+ Record.addr("nostr", [
+ "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6",
+ "wss://relay.example.com",
+ ]),
+ ])
+ //
+
+ _ = records
+}
+
+/// Publish signed records
+func examplePublish() async throws {
+ let fabric = Fabric()
+ let secretKey = Data(hex:
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ )
+
+ let rs = try RecordSet.pack([
+ Record.seq(1),
+ Record.txt("website", ["https://example.com"]),
+ Record.addr("btc", ["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"]),
+ ])
+
+ //
+ let cert = try await fabric.export("alice@bitcoin")
+ try await fabric.publish(cert, rs, secretKey, primary: true)
+ //
+}
+
+/// Resolve by numeric ID
+func exampleResolveById() async throws {
+ let fabric = Fabric()
+
+ //
+ guard let resolved = try await fabric.resolveById("num1qx8dtlzq...") else {
+ print("handle not found")
+ return
+ }
+
+ print("Handle found: \(resolved.zone.handle)")
+ //
+}
+
+/// Search by address
+func exampleSearchAddr() async throws {
+ let fabric = Fabric()
+
+ //
+ let batch = try await fabric.searchAddr("btc", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
+
+ for zone in batch.zones {
+ print("\(zone.handle): \(zone.sovereignty)")
+ }
+ //
+}
+
+/// Advanced: Build and broadcast a message manually
+func exampleMessageBuilder() async throws {
+ let fabric = Fabric()
+ let secretKey = Data(hex:
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ )
+
+ let certBytes = try await fabric.export("alice@bitcoin")
+ let records = try RecordSet.pack([
+ Record.seq(1),
+ Record.addr("btc", ["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"]),
+ ])
+
+ //
+ let cert = try CertificateChain.fromSlice(certBytes)
+ var builder = MessageBuilder()
+ builder.addHandle(cert, records)
+
+ let proofBytes = try await fabric.prove(builder.chainProofRequest())
+ let proof = try ChainProof.fromSlice(proofBytes)
+
+ var (msg, unsigned) = try builder.build(proof)
+
+ for var u in unsigned {
+ u.flags |= SIG_PRIMARY_ZONE
+ let sig = try signSchnorr(u.signingId(), secretKey)
+ msg.setRecords(u.canonical, u.packSig(sig))
+ }
+
+ try await fabric.broadcast(msg.toBytes())
+ //
+}
+
+@main
+struct Example {
+ static func main() async {
+ do {
+ try await exampleResolve()
+ } catch {
+ print("resolve failed: \(error)")
+ }
+
+ do {
+ try await exampleTrustAndVerification()
+ } catch {
+ print("verification example failed (expected): \(error)")
+ }
+
+ do {
+ try await exampleUnpackRecords()
+ try await exampleResolveAll()
+ try examplePackRecords()
+ try await examplePublish()
+ try await exampleResolveById()
+ try await exampleSearchAddr()
+ try await exampleMessageBuilder()
+ } catch {
+ print("error: \(error)")
+ }
+
+ print("Done!")
+ }
+}
diff --git a/fabric/examples/test-example.sh b/fabric/examples/test-example.sh
new file mode 100755
index 0000000..a821c6e
--- /dev/null
+++ b/fabric/examples/test-example.sh
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+set -eo pipefail
+#
+# Test an example by replacing placeholder values with real ones.
+#
+# Usage:
+# HANDLE=@buffrr SECRET_KEY=abc... ./test-example.sh rust
+#
+# Required env vars:
+# HANDLE — handle to resolve/publish (e.g. @buffrr)
+# SECRET_KEY — 64-char hex secret key for signing
+#
+# Optional:
+# HANDLES — comma-separated batch (default: HANDLE repeated)
+# BTC_ADDR — Bitcoin address to use
+# NPUB — Nostr npub to use
+# NUM_ID — Numeric ID for resolve_by_id
+#
+
+LANG="${1:?Usage: test-example.sh }"
+
+HANDLE="${HANDLE:?Set HANDLE env var}"
+SECRET_KEY="${SECRET_KEY:?Set SECRET_KEY env var}"
+HANDLES="${HANDLES:-$HANDLE}"
+BTC_ADDR="${BTC_ADDR:-bc1qtest$(date +%s)}"
+NPUB="${NPUB:-npub1placeholder}"
+NUM_ID="${NUM_ID:-num1placeholder}"
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+TMPDIR=$(mktemp -d)
+trap "rm -rf $TMPDIR" EXIT
+
+echo "==> Copying $LANG example to $TMPDIR"
+cp -r "$SCRIPT_DIR/$LANG" "$TMPDIR/$LANG"
+
+REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
+
+echo "==> Replacing placeholders"
+find "$TMPDIR/$LANG" -type f \( -name "*.rs" -o -name "*.ts" -o -name "*.js" -o -name "*.go" -o -name "*.py" -o -name "*.kt" -o -name "*.swift" -o -name "*.toml" \) | while read f; do
+ sed -i.bak \
+ -e "s|alice@bitcoin|$HANDLE|g" \
+ -e "s|bob@bitcoin|$HANDLES|g" \
+ -e "s|@bitcoin|$(echo $HANDLE | grep -o '[@#].*')|g" \
+ -e "s|bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4|$BTC_ADDR|g" \
+ -e "s|npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6|$NPUB|g" \
+ -e "s|num1qx8dtlzq\.\.\.|$NUM_ID|g" \
+ -e "s|0000000000000000000000000000000000000000000000000000000000000001|$SECRET_KEY|g" \
+ -e "s|path = \"../../../fabric/rust\"|path = \"$REPO_ROOT/fabric/rust\"|g" \
+ "$f"
+ rm -f "$f.bak"
+done
+
+echo "==> Building and running"
+case "$LANG" in
+ rust)
+ cd "$TMPDIR/$LANG"
+ cargo run 2>&1
+ ;;
+ *)
+ echo "Language '$LANG' not yet supported"
+ exit 1
+ ;;
+esac
\ No newline at end of file
diff --git a/fabric/go/fabric.go b/fabric/go/fabric.go
index 683c2c1..4fd370e 100644
--- a/fabric/go/fabric.go
+++ b/fabric/go/fabric.go
@@ -147,10 +147,8 @@ type Fabric struct {
mu sync.Mutex
}
-func New(seeds []string) *Fabric {
- if seeds == nil {
- seeds = DefaultSeeds
- }
+func New() *Fabric {
+ seeds := DefaultSeeds
return &Fabric{
client: &http.Client{},
seeds: seeds,
@@ -161,6 +159,7 @@ func New(seeds []string) *Fabric {
func (f *Fabric) SetDevMode(v bool) { f.devMode = v }
func (f *Fabric) SetPreferLatest(v bool) { f.preferLatest = v }
+func (f *Fabric) SetSeeds(seeds []string) { f.seeds = seeds }
func (f *Fabric) Relays() []string { return f.pool.URLs() }
@@ -553,7 +552,7 @@ func (f *Fabric) SearchAddr(name, addr string) (ResolvedBatch, error) {
continue
}
for _, r := range records {
- if a, ok := r.(libveritas.RecordAddr); ok {
+ if a, ok := r.(libveritas.ParsedRecordAddr); ok {
if a.Key == name && len(a.Value) > 0 && a.Value[0] == addr {
matching = append(matching, z)
break
diff --git a/fabric/go/go.mod b/fabric/go/go.mod
index 68190a7..25a4b87 100644
--- a/fabric/go/go.mod
+++ b/fabric/go/go.mod
@@ -4,7 +4,7 @@ go 1.22
require (
github.com/btcsuite/btcd/btcec/v2 v2.3.6
- github.com/spacesprotocol/libveritas-go v0.0.0-dev.20260401232107
+ github.com/spacesprotocol/libveritas-go v0.0.0-dev.20260407170108
)
require (
diff --git a/fabric/go/go.sum b/fabric/go/go.sum
index 1c20193..6c01b1f 100644
--- a/fabric/go/go.sum
+++ b/fabric/go/go.sum
@@ -8,5 +8,5 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
-github.com/spacesprotocol/libveritas-go v0.0.0-dev.20260401232107 h1:G6iAm4v0s08uKtVys3h8oDeJ2gMTwjpXcODHq1Mazis=
-github.com/spacesprotocol/libveritas-go v0.0.0-dev.20260401232107/go.mod h1:HXnX2FNL43ueJuedsQwX9GF5jRHYZLqXYfFLWz900H8=
+github.com/spacesprotocol/libveritas-go v0.0.0-dev.20260407170108 h1:tpFQOIBRJpM0dYYc4VpAh/OayA8ZPvqr1taTuSZdQ98=
+github.com/spacesprotocol/libveritas-go v0.0.0-dev.20260407170108/go.mod h1:HXnX2FNL43ueJuedsQwX9GF5jRHYZLqXYfFLWz900H8=
diff --git a/fabric/go/libveritas.go b/fabric/go/libveritas.go
deleted file mode 100644
index 05b010b..0000000
--- a/fabric/go/libveritas.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package fabric
-
-// Re-export libveritas types so consumers can use them without a separate import.
-
-import libveritas "github.com/spacesprotocol/libveritas-go"
-
-type Zone = libveritas.Zone
-type Message = libveritas.Message
-type MessageBuilder = libveritas.MessageBuilder
-type Anchors = libveritas.Anchors
-type Veritas = libveritas.Veritas
-type QueryContext = libveritas.QueryContext
-type VerifiedMessage = libveritas.VerifiedMessage
-type Lookup = libveritas.Lookup
-type RecordSet = libveritas.RecordSet
-type Record = libveritas.Record
-type DataUpdateEntry = libveritas.DataUpdateEntry
-type CommitmentState = libveritas.CommitmentState
-type DelegateState = libveritas.DelegateState
-type VeritasError = libveritas.VeritasError
-
-var CreateCertificateChain = libveritas.CreateCertificateChain
diff --git a/fabric/js/fabric-react-native/package.json b/fabric/js/fabric-react-native/package.json
index 329b71c..01a19b8 100644
--- a/fabric/js/fabric-react-native/package.json
+++ b/fabric/js/fabric-react-native/package.json
@@ -23,7 +23,7 @@
},
"dependencies": {
"@spacesprotocol/fabric-core": "*",
- "@spacesprotocol/react-native-libveritas": "0.0.0-dev.20260401232841",
+ "@spacesprotocol/react-native-libveritas": "0.0.0-dev.20260407170644",
"uniffi-bindgen-react-native": "0.29.3-1"
},
"devDependencies": {
diff --git a/fabric/js/fabric-web/package.json b/fabric/js/fabric-web/package.json
index b6a5300..4b40008 100644
--- a/fabric/js/fabric-web/package.json
+++ b/fabric/js/fabric-web/package.json
@@ -27,7 +27,7 @@
"dependencies": {
"@noble/curves": "^1.8.0",
"@spacesprotocol/fabric-core": "*",
- "@spacesprotocol/libveritas": "^0.0.0-dev.20260401231450"
+ "@spacesprotocol/libveritas": "^0.0.0-dev.20260407165333"
},
"devDependencies": {
"@types/node": "^25.5.0",
diff --git a/fabric/js/package-lock.json b/fabric/js/package-lock.json
index 9579d0b..aba5223 100644
--- a/fabric/js/package-lock.json
+++ b/fabric/js/package-lock.json
@@ -28,7 +28,7 @@
"license": "MIT",
"dependencies": {
"@spacesprotocol/fabric-core": "*",
- "@spacesprotocol/react-native-libveritas": "0.0.0-dev.20260401232841",
+ "@spacesprotocol/react-native-libveritas": "0.0.0-dev.20260407170644",
"uniffi-bindgen-react-native": "0.29.3-1"
},
"devDependencies": {
@@ -42,7 +42,7 @@
"dependencies": {
"@noble/curves": "^1.8.0",
"@spacesprotocol/fabric-core": "*",
- "@spacesprotocol/libveritas": "^0.0.0-dev.20260401231450"
+ "@spacesprotocol/libveritas": "^0.0.0-dev.20260407165333"
},
"bin": {
"fabric": "dist/cli.js"
@@ -972,14 +972,14 @@
"link": true
},
"node_modules/@spacesprotocol/libveritas": {
- "version": "0.0.0-dev.20260401231450",
- "resolved": "https://registry.npmjs.org/@spacesprotocol/libveritas/-/libveritas-0.0.0-dev.20260401231450.tgz",
- "integrity": "sha512-XjMSrkUaXOfkLdScIUmaMb0OabFcB9sh6ZITVr2czsYFgphrl7htSlRuzC33NK6fsIGOw/I6BYuY/OHH14ZVNQ=="
+ "version": "0.0.0-dev.20260407165333",
+ "resolved": "https://registry.npmjs.org/@spacesprotocol/libveritas/-/libveritas-0.0.0-dev.20260407165333.tgz",
+ "integrity": "sha512-YTBvFbODHzr5XhpApkDuFXf6eWy+kqhCV+7rYHXDW/YYd25uWqqcAOIRXRPPGxRNgzbviDFqMRNAuhy5Vtc7UQ=="
},
"node_modules/@spacesprotocol/react-native-libveritas": {
- "version": "0.0.0-dev.20260401232841",
- "resolved": "https://registry.npmjs.org/@spacesprotocol/react-native-libveritas/-/react-native-libveritas-0.0.0-dev.20260401232841.tgz",
- "integrity": "sha512-YgDrRKwc7vFHvCK/xU3nPHQXR8F3lpS217hqeqGMV6KEWphe1L+ev2OsBdQKucYbAymagSpCpJt1C8Y1YVZcaQ==",
+ "version": "0.0.0-dev.20260407170644",
+ "resolved": "https://registry.npmjs.org/@spacesprotocol/react-native-libveritas/-/react-native-libveritas-0.0.0-dev.20260407170644.tgz",
+ "integrity": "sha512-EZ4ZFmd/ik8DeIqq8gBN0ZInTNDF9QkLUeeHkzEe6C7MGEb7aXW8hegi2n+voyfd8XV6hozXaCaapKyUzCWl6Q==",
"license": "MIT",
"peerDependencies": {
"react": "*",
diff --git a/fabric/kotlin/build.gradle.kts b/fabric/kotlin/build.gradle.kts
index 9434ae1..dab75f6 100644
--- a/fabric/kotlin/build.gradle.kts
+++ b/fabric/kotlin/build.gradle.kts
@@ -19,9 +19,9 @@ dependencies {
// compileOnly — consumers provide the right variant at runtime:
// Android: org.spacesprotocol:libveritas (AAR)
// JVM: org.spacesprotocol:libveritas-jvm (JAR)
- compileOnly("org.spacesprotocol:libveritas-jvm:0.0.0-dev.20260401232452")
+ compileOnly("org.spacesprotocol:libveritas-jvm:0.0.0-dev.20260407170200")
// CLI needs it at runtime
- runtimeOnly("org.spacesprotocol:libveritas-jvm:0.0.0-dev.20260401232452")
+ runtimeOnly("org.spacesprotocol:libveritas-jvm:0.0.0-dev.20260407170200")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
compileOnly("fr.acinq.secp256k1:secp256k1-kmp:0.17.3")
diff --git a/fabric/kotlin/src/main/kotlin/org/spacesprotocol/fabric/Fabric.kt b/fabric/kotlin/src/main/kotlin/org/spacesprotocol/fabric/Fabric.kt
index cf0448e..325a207 100644
--- a/fabric/kotlin/src/main/kotlin/org/spacesprotocol/fabric/Fabric.kt
+++ b/fabric/kotlin/src/main/kotlin/org/spacesprotocol/fabric/Fabric.kt
@@ -285,7 +285,7 @@ class Fabric(
try {
val rs = RecordSet(bytes)
rs.unpack().any { r ->
- r is Record.Addr && r.key == name && r.value.isNotEmpty() && r.value[0] == addr
+ r is ParsedRecord.Addr && r.key == name && r.value.isNotEmpty() && r.value[0] == addr
}
} catch (_: Exception) { false }
} ?: false
diff --git a/fabric/kotlin/src/main/kotlin/org/spacesprotocol/fabric/Libveritas.kt b/fabric/kotlin/src/main/kotlin/org/spacesprotocol/fabric/Libveritas.kt
index 83dde40..a904999 100644
--- a/fabric/kotlin/src/main/kotlin/org/spacesprotocol/fabric/Libveritas.kt
+++ b/fabric/kotlin/src/main/kotlin/org/spacesprotocol/fabric/Libveritas.kt
@@ -14,6 +14,7 @@ typealias VerifiedMessage = org.spacesprotocol.libveritas.VerifiedMessage
typealias Lookup = org.spacesprotocol.libveritas.Lookup
typealias RecordSet = org.spacesprotocol.libveritas.RecordSet
typealias Record = org.spacesprotocol.libveritas.Record
+typealias ParsedRecord = org.spacesprotocol.libveritas.ParsedRecord
typealias DataUpdateEntry = org.spacesprotocol.libveritas.DataUpdateEntry
typealias CommitmentState = org.spacesprotocol.libveritas.CommitmentState
typealias DelegateState = org.spacesprotocol.libveritas.DelegateState
diff --git a/fabric/python/pyproject.toml b/fabric/python/pyproject.toml
index ebfda41..85274f1 100644
--- a/fabric/python/pyproject.toml
+++ b/fabric/python/pyproject.toml
@@ -3,7 +3,7 @@ name = "fabric-resolver"
version = "0.1.0"
description = "Python client for the certrelay fabric network"
requires-python = ">=3.10"
-dependencies = ["libveritas==0.0.0.dev20260401231357"]
+dependencies = ["libveritas==0.0.0.dev20260407165216"]
[project.optional-dependencies]
signing = ["coincurve"]
diff --git a/fabric/rust/src/client.rs b/fabric/rust/src/client.rs
index 12f003e..cd04b13 100644
--- a/fabric/rust/src/client.rs
+++ b/fabric/rust/src/client.rs
@@ -112,8 +112,8 @@ enum TrustKind {
Observed,
}
-/// UI badge state derived from trust + sovereignty.
-pub enum VerificationBadge {
+/// UI badge status derived from trust + sovereignty.
+pub enum Badge {
/// Sovereign handle verified against a trusted root. Show orange checkmark.
Orange,
/// Resolved against an observed root that differs from the trusted set.
@@ -123,6 +123,16 @@ pub enum VerificationBadge {
None,
}
+impl fmt::Display for Badge {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Badge::Orange => write!(f, "orange"),
+ Badge::Unverified => write!(f, "unverified"),
+ Badge::None => write!(f, "none"),
+ }
+ }
+}
+
#[derive(Clone, Serialize, Deserialize)]
pub struct ResolvedBatch {
pub zones: Vec,
@@ -204,21 +214,21 @@ impl Fabric {
true
}
- pub fn badge(&self, resolved: &Resolved) -> VerificationBadge {
+ pub fn badge(&self, resolved: &Resolved) -> Badge {
self.badge_for(resolved.zone.sovereignty, resolved.roots.as_slice())
}
- pub fn badge_for(&self, sov: SovereigntyState, roots: &[TrustId]) -> VerificationBadge {
+ pub fn badge_for(&self, sov: SovereigntyState, roots: &[TrustId]) -> Badge {
let is_trusted = self.are_roots_trusted(roots);
let is_observed = is_trusted || self.are_roots_observed(roots);
let is_semi_trusted = is_trusted || self.are_roots_semi_trusted(roots);
if is_trusted && matches!(sov, SovereigntyState::Sovereign) {
- VerificationBadge::Orange
+ Badge::Orange
} else if is_observed && !is_trusted && !is_semi_trusted {
- VerificationBadge::Unverified
+ Badge::Unverified
} else {
- VerificationBadge::None
+ Badge::None
}
}
@@ -292,6 +302,11 @@ impl Fabric {
*self.trusted.lock().unwrap() = None;
}
+ /// Clear the semi trusted state.
+ pub fn clear_semi_trusted(&self) {
+ *self.semi_trusted.lock().unwrap() = None;
+ }
+
/// Set whether to query multiple relays for freshness hints before resolving.
pub fn set_prefer_latest(&self, latest: bool) {
self.prefer_latest.store(latest, Ordering::Relaxed);
@@ -383,31 +398,26 @@ impl Fabric {
/// Resolve a single handle and return its verified Zone.
/// Supports dotted names like `hello.alice@bitcoin`.
- pub async fn resolve(&self, handle: &str) -> Result {
+ pub async fn resolve(&self, handle: &str) -> Result