// <doc:install-kotlin-jvm>
// implementation("org.spacesprotocol:fabric:{{VERSION}}")
// implementation("org.spacesprotocol:libveritas-jvm:{{LV_VERSION}}")
// </doc:install-kotlin-jvm>
// <doc:install-kotlin-android>
// implementation("org.spacesprotocol:fabric:{{VERSION}}")
// implementation("org.spacesprotocol:libveritas:{{LV_VERSION}}")
// </doc:install-kotlin-android>
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
suspend fun exampleResolveIntro() {
// <doc:resolve-intro>
val fabric = Fabric()
val zone = fabric.resolve("alice@bitcoin")
// </doc:resolve-intro>
}
/// Resolve a single handle
suspend fun exampleResolve() {
// <doc:resolve>
val fabric = Fabric()
val zone = fabric.resolve("alice@bitcoin")
if (zone == null) {
println("handle not found")
return
}
println("Handle found: ${zone.handle}")
// </doc:resolve>
}
/// Verification
suspend fun exampleTrustAndVerification() {
val fabric = Fabric()
// <doc:verification>
// Before pinning a trust id: resolve uses observed (peer) state
// badge() returns Unverified
val zone = fabric.resolve("alice@bitcoin")
?: throw IllegalStateException("handle exists")
fabric.badge(zone) // 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 zone was against a trusted root
fabric.badge(zone) // 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()
// </doc:verification>
}
/// Unpack records from a resolved handle
suspend fun exampleUnpackRecords() {
val fabric = Fabric()
val zone = fabric.resolve("alice@bitcoin")
?: throw IllegalStateException("handle exists")
// <doc:unpack-records>
val records = 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 -> {}
}
}
// </doc:unpack-records>
}
/// Resolve multiple handles
suspend fun exampleResolveAll() {
val fabric = Fabric()
// <doc:resolve-all>
val zones = fabric.resolveAll(listOf("alice@bitcoin", "bob@bitcoin"))
for (zone in zones) {
println("${zone.handle}: ${zone.sovereignty}")
}
// </doc:resolve-all>
}
/// Pack records into a RecordSet
fun examplePackRecords() {
// <doc:pack-records>
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",
)),
))
// </doc:pack-records>
}
/// 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")),
))
// <doc:publish>
val cert = fabric.export("alice@bitcoin")
fabric.publish(cert, rs, secretKey, primary = true)
// </doc:publish>
}
/// Resolve by numeric ID
suspend fun exampleResolveById() {
val fabric = Fabric()
// <doc:resolve-by-id>
val zone = fabric.resolveById("num1qx8dtlzq...")
if (zone == null) {
println("handle not found")
return
}
println("Handle found: ${zone.handle}")
// </doc:resolve-by-id>
}
/// Search by address
suspend fun exampleSearchAddr() {
val fabric = Fabric()
// <doc:search-addr>
val zones = fabric.searchAddr("nostr", "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6")
for (zone in zones) {
println("${zone.handle}: ${zone.sovereignty}")
}
// </doc:search-addr>
}
/// 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")),
))
// <doc:message-builder>
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())
// </doc:message-builder>
}
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!")
}