@wovin/core 0.1.35 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/README.md +0 -12
  2. package/dist/applog/applog-helpers.d.ts +12 -12
  3. package/dist/applog/applog-helpers.d.ts.map +1 -1
  4. package/dist/applog/applog-utils.d.ts +25 -6
  5. package/dist/applog/applog-utils.d.ts.map +1 -1
  6. package/dist/applog/datom-types.d.ts +4 -5
  7. package/dist/applog/datom-types.d.ts.map +1 -1
  8. package/dist/applog.d.ts +3 -3
  9. package/dist/applog.d.ts.map +1 -1
  10. package/dist/{applog.min.js → applog.js} +6 -7
  11. package/dist/blockstore.d.ts +1 -1
  12. package/dist/blockstore.d.ts.map +1 -1
  13. package/dist/{blockstore.min.js → blockstore.js} +1 -3
  14. package/dist/{blockstore.min.js.map → blockstore.js.map} +1 -1
  15. package/dist/{chunk-KXMTKPF4.min.js → chunk-3JZMOEOD.js} +8 -8
  16. package/dist/chunk-3JZMOEOD.js.map +1 -0
  17. package/dist/chunk-3WZVG277.js +434 -0
  18. package/dist/chunk-3WZVG277.js.map +1 -0
  19. package/dist/chunk-7Z5YDQKK.js +1 -0
  20. package/dist/chunk-CPSDKFBG.js +147 -0
  21. package/dist/chunk-CPSDKFBG.js.map +1 -0
  22. package/dist/chunk-E46VTKTZ.js +1 -0
  23. package/dist/{chunk-H3VQJP56.min.js → chunk-J2FDHGOZ.js} +9 -9
  24. package/dist/chunk-J2FDHGOZ.js.map +1 -0
  25. package/dist/chunk-L5EEEGE6.js +1862 -0
  26. package/dist/chunk-L5EEEGE6.js.map +1 -0
  27. package/dist/{chunk-BRC7LSM6.min.js → chunk-PD3C7XUM.js} +5 -5
  28. package/dist/chunk-PD3C7XUM.js.map +1 -0
  29. package/dist/chunk-QZXKQCAY.js +1026 -0
  30. package/dist/chunk-QZXKQCAY.js.map +1 -0
  31. package/dist/{chunk-QPGEBDMJ.min.js → chunk-YDAKBU6Q.js} +1 -1
  32. package/dist/chunk-YDAKBU6Q.js.map +1 -0
  33. package/dist/chunk-ZAADLBSB.js +36 -0
  34. package/dist/chunk-ZAADLBSB.js.map +1 -0
  35. package/dist/index.d.ts +7 -7
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/{index.min.js → index.js} +73 -46
  38. package/dist/ipfs/car.d.ts +11 -11
  39. package/dist/ipfs/car.d.ts.map +1 -1
  40. package/dist/ipfs/ipfs-utils.d.ts +2 -2
  41. package/dist/ipfs/ipfs-utils.d.ts.map +1 -1
  42. package/dist/ipfs.d.ts +3 -3
  43. package/dist/ipfs.d.ts.map +1 -1
  44. package/dist/{ipfs.min.js → ipfs.js} +7 -10
  45. package/dist/ipns.d.ts +1 -1
  46. package/dist/ipns.d.ts.map +1 -1
  47. package/dist/ipns.js +64 -0
  48. package/dist/ipns.js.map +1 -0
  49. package/dist/pubsub/pub-pull.d.ts +3 -3
  50. package/dist/pubsub/pub-pull.d.ts.map +1 -1
  51. package/dist/pubsub/pubsub-types.d.ts +3 -3
  52. package/dist/pubsub/pubsub-types.d.ts.map +1 -1
  53. package/dist/pubsub/snap-push.d.ts +4 -4
  54. package/dist/pubsub/snap-push.d.ts.map +1 -1
  55. package/dist/pubsub/ucan.d.ts +1 -1
  56. package/dist/pubsub/ucan.d.ts.map +1 -1
  57. package/dist/pubsub.d.ts +4 -4
  58. package/dist/pubsub.d.ts.map +1 -1
  59. package/dist/{pubsub.min.js → pubsub.js} +7 -10
  60. package/dist/query/attr-helpers.d.ts +5 -0
  61. package/dist/query/attr-helpers.d.ts.map +1 -0
  62. package/dist/query/basic.d.ts +85 -21
  63. package/dist/query/basic.d.ts.map +1 -1
  64. package/dist/query/divergences.d.ts +5 -5
  65. package/dist/query/divergences.d.ts.map +1 -1
  66. package/dist/query/entity-collection.d.ts +19 -0
  67. package/dist/query/entity-collection.d.ts.map +1 -0
  68. package/dist/query/matchers.d.ts +1 -1
  69. package/dist/query/matchers.d.ts.map +1 -1
  70. package/dist/query/memoized.d.ts +66 -0
  71. package/dist/query/memoized.d.ts.map +1 -0
  72. package/dist/query/situations.d.ts +2 -1
  73. package/dist/query/situations.d.ts.map +1 -1
  74. package/dist/query/subscribable.d.ts +111 -0
  75. package/dist/query/subscribable.d.ts.map +1 -0
  76. package/dist/query/types.d.ts +54 -14
  77. package/dist/query/types.d.ts.map +1 -1
  78. package/dist/query.d.ts +9 -5
  79. package/dist/query.d.ts.map +1 -1
  80. package/dist/{query.min.js → query.js} +51 -32
  81. package/dist/retrieve/index.d.ts +1 -1
  82. package/dist/retrieve/index.d.ts.map +1 -1
  83. package/dist/retrieve/update-thread.d.ts +3 -3
  84. package/dist/retrieve/update-thread.d.ts.map +1 -1
  85. package/dist/retrieve.d.ts +1 -1
  86. package/dist/retrieve.d.ts.map +1 -1
  87. package/dist/retrieve.js +14 -0
  88. package/dist/thread/basic.d.ts +15 -19
  89. package/dist/thread/basic.d.ts.map +1 -1
  90. package/dist/thread/filters.d.ts +8 -10
  91. package/dist/thread/filters.d.ts.map +1 -1
  92. package/dist/thread/indexes.d.ts +56 -0
  93. package/dist/thread/indexes.d.ts.map +1 -0
  94. package/dist/thread/mapped.d.ts +40 -11
  95. package/dist/thread/mapped.d.ts.map +1 -1
  96. package/dist/thread/utils.d.ts +5 -5
  97. package/dist/thread/utils.d.ts.map +1 -1
  98. package/dist/thread/writeable.d.ts +2 -2
  99. package/dist/thread/writeable.d.ts.map +1 -1
  100. package/dist/thread.d.ts +6 -5
  101. package/dist/thread.d.ts.map +1 -1
  102. package/dist/{thread.min.js → thread.js} +9 -6
  103. package/dist/types/typescript-utils.d.ts +6 -5
  104. package/dist/types/typescript-utils.d.ts.map +1 -1
  105. package/dist/types.d.ts +1 -1
  106. package/dist/types.d.ts.map +1 -1
  107. package/dist/{types.min.js → types.js} +3 -4
  108. package/dist/utils/debug-name.d.ts +13 -0
  109. package/dist/utils/debug-name.d.ts.map +1 -0
  110. package/dist/utils.d.ts +1 -1
  111. package/dist/utils.d.ts.map +1 -1
  112. package/dist/utils.js +9 -0
  113. package/package.json +32 -23
  114. package/src/applog/applog-helpers.ts +155 -0
  115. package/src/applog/applog-utils.test.ts +108 -0
  116. package/src/applog/applog-utils.ts +507 -0
  117. package/src/applog/datom-types.ts +148 -0
  118. package/src/applog.ts +3 -0
  119. package/src/blockstore/index.ts +36 -0
  120. package/src/blockstore.ts +1 -0
  121. package/src/index.ts +8 -0
  122. package/src/ipfs/car.ts +291 -0
  123. package/src/ipfs/fetch-snapshot-chain.ts +135 -0
  124. package/src/ipfs/ipfs-utils.ts +132 -0
  125. package/src/ipfs.ts +3 -0
  126. package/src/ipns/ipns-record.ts +115 -0
  127. package/src/ipns.ts +1 -0
  128. package/src/pubsub/UCAN Specs Overview.md +217 -0
  129. package/src/pubsub/connector.ts +9 -0
  130. package/src/pubsub/pub-pull.ts +31 -0
  131. package/src/pubsub/pubsub-types.ts +90 -0
  132. package/src/pubsub/snap-push.ts +277 -0
  133. package/src/pubsub/ucan-example.ts +61 -0
  134. package/src/pubsub/ucan.ts +56 -0
  135. package/src/pubsub.ts +4 -0
  136. package/src/query/attr-helpers.ts +5 -0
  137. package/src/query/basic.ts +1245 -0
  138. package/src/query/divergences.ts +50 -0
  139. package/src/query/entity-collection.ts +131 -0
  140. package/src/query/liveFilterAndMap.test.ts +102 -0
  141. package/src/query/matchers.ts +8 -0
  142. package/src/query/memoized.test.ts +151 -0
  143. package/src/query/memoized.ts +180 -0
  144. package/src/query/query-steps.ts +4 -0
  145. package/src/query/query.test.ts +538 -0
  146. package/src/query/situations.ts +261 -0
  147. package/src/query/subscribable.test.ts +245 -0
  148. package/src/query/subscribable.ts +234 -0
  149. package/src/query/types.ts +155 -0
  150. package/src/query/withoutDeleted.test.ts +204 -0
  151. package/src/query.ts +9 -0
  152. package/src/retrieve/index.ts +1 -0
  153. package/src/retrieve/update-thread.ts +248 -0
  154. package/src/retrieve.ts +1 -0
  155. package/src/test/perf/query.1m.perf.test.ts +94 -0
  156. package/src/test/perf/query.perf.test.ts +389 -0
  157. package/src/test/perf/query.realdata.perf.test.ts +182 -0
  158. package/src/thread/basic.ts +209 -0
  159. package/src/thread/filters.ts +227 -0
  160. package/src/thread/indexes.ts +250 -0
  161. package/src/thread/joinThreads.test.ts +304 -0
  162. package/src/thread/mapped.ts +226 -0
  163. package/src/thread/utils.ts +144 -0
  164. package/src/thread/writeable.ts +163 -0
  165. package/src/thread.ts +6 -0
  166. package/src/types/typescript-utils.ts +64 -0
  167. package/src/types.ts +1 -0
  168. package/src/utils/debug-name.ts +54 -0
  169. package/src/utils.ts +4 -0
  170. package/dist/chunk-2Y2PYHGR.min.js +0 -65
  171. package/dist/chunk-2Y2PYHGR.min.js.map +0 -1
  172. package/dist/chunk-5MMGBK2U.min.js +0 -1
  173. package/dist/chunk-7IDQIMQO.min.js +0 -1
  174. package/dist/chunk-BRC7LSM6.min.js.map +0 -1
  175. package/dist/chunk-COXXILXC.min.js +0 -512
  176. package/dist/chunk-COXXILXC.min.js.map +0 -1
  177. package/dist/chunk-GDX2OO7L.min.js +0 -9080
  178. package/dist/chunk-GDX2OO7L.min.js.map +0 -1
  179. package/dist/chunk-H3VQJP56.min.js.map +0 -1
  180. package/dist/chunk-HYMC7W6S.min.js +0 -1549
  181. package/dist/chunk-HYMC7W6S.min.js.map +0 -1
  182. package/dist/chunk-KEHU7HGZ.min.js +0 -5216
  183. package/dist/chunk-KEHU7HGZ.min.js.map +0 -1
  184. package/dist/chunk-KXMTKPF4.min.js.map +0 -1
  185. package/dist/chunk-PHITDXZT.min.js +0 -36
  186. package/dist/chunk-QO2KMGDN.min.js +0 -3771
  187. package/dist/chunk-QO2KMGDN.min.js.map +0 -1
  188. package/dist/chunk-QPGEBDMJ.min.js.map +0 -1
  189. package/dist/chunk-WXLCBTHX.min.js +0 -1606
  190. package/dist/chunk-WXLCBTHX.min.js.map +0 -1
  191. package/dist/ipns.min.js +0 -6419
  192. package/dist/ipns.min.js.map +0 -1
  193. package/dist/mobx/mobx-utils.d.ts +0 -82
  194. package/dist/mobx/mobx-utils.d.ts.map +0 -1
  195. package/dist/mobx.d.ts +0 -2
  196. package/dist/mobx.d.ts.map +0 -1
  197. package/dist/mobx.min.js +0 -141
  198. package/dist/retrieve.min.js +0 -17
  199. package/dist/types.min.js.map +0 -1
  200. package/dist/utils.min.js +0 -10
  201. package/dist/utils.min.js.map +0 -1
  202. /package/dist/{applog.min.js.map → applog.js.map} +0 -0
  203. /package/dist/{chunk-5MMGBK2U.min.js.map → chunk-7Z5YDQKK.js.map} +0 -0
  204. /package/dist/{chunk-7IDQIMQO.min.js.map → chunk-E46VTKTZ.js.map} +0 -0
  205. /package/dist/{chunk-PHITDXZT.min.js.map → index.js.map} +0 -0
  206. /package/dist/{index.min.js.map → ipfs.js.map} +0 -0
  207. /package/dist/{ipfs.min.js.map → pubsub.js.map} +0 -0
  208. /package/dist/{mobx.min.js.map → query.js.map} +0 -0
  209. /package/dist/{pubsub.min.js.map → retrieve.js.map} +0 -0
  210. /package/dist/{query.min.js.map → thread.js.map} +0 -0
  211. /package/dist/{retrieve.min.js.map → types.js.map} +0 -0
  212. /package/dist/{thread.min.js.map → utils.js.map} +0 -0
@@ -0,0 +1,132 @@
1
+ import * as dagJson from '@ipld/dag-json'
2
+ import { sha256 } from '@noble/hashes/sha2.js'
3
+ import { Logger } from 'besonders-logger'
4
+ import { CID, digest as Digest } from 'multiformats'
5
+ import { encode as multiformatsEncode } from 'multiformats/block'
6
+ // import { encode } from 'multiformats/block';
7
+ import { Applog, ApplogEncNoCid, ApplogNoCid, ApplogOfSomeSort, CidString, IpnsString, isEncryptedApplog } from '../applog/datom-types.ts'
8
+
9
+ import { base36 } from 'multiformats/bases/base36'
10
+ import { sha256 as sha265Hasher } from 'multiformats/hashes/sha2'
11
+
12
+ /* THIS FILE SHOULD NOT DEPEND ON UI STUFF, SO THAT TESTS CAN RUN WITH MINIMAL DEPENDENCIES */
13
+
14
+ const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars
15
+
16
+ export const MULTICODEC_IPNS_KEY = 0x72
17
+
18
+ export function prepareForPub(log: ApplogOfSomeSort, without: string[] = ['cid']) {
19
+ if (!log) throw ERROR('falsy log', log)
20
+ let cid = (log as Applog).cid
21
+ if (isEncryptedApplog(log)) {
22
+ if (!cid) cid = getCidSync(encodeBlock(log as ApplogEncNoCid).bytes).toString()
23
+ WARN('preparing an encrypted applog - really?')
24
+ return { log, cid }
25
+ }
26
+ const logWithout = {}
27
+ for (let [key, val] of Object.entries(log)) {
28
+ if (val === undefined) {
29
+ WARN(`log.${key} is undefined, which is not allowed - encoding as null`, log)
30
+ val = null
31
+ }
32
+ if (!without.includes(key)) {
33
+ logWithout[key] = val // && key === 'pv' ? CID.parse(val) : val //HACK: disabled until clarified: https://discuss.ipfs.tech/t/pin-dag-with-open-ends/17612
34
+ } else {
35
+ VERBOSE('excluding app log', { key, val })
36
+ }
37
+ }
38
+ return { log: logWithout as Applog, cid }
39
+ }
40
+
41
+ export function encodeApplogAndGetCid(log: ApplogNoCid) {
42
+ return getCidSync(encodeApplog(log).bytes)
43
+ }
44
+ export function encodeApplog(log: ApplogNoCid | ApplogEncNoCid): { bytes: dagJson.ByteView<any>; cid: CID } {
45
+ return encodeBlock(prepareForPub(log)?.log)
46
+ }
47
+
48
+ export function getCidSync(bytes: dagJson.ByteView<any>) {
49
+ // Hacky way to use a sync sha265 lib to create a CID - code inspired by https://github.com/multiformats/js-multiformats#multihash-hashers
50
+ const hash = sha256(bytes)
51
+ const digest = Digest.create(sha265Hasher.code, hash)
52
+ const cid = CID.create(1, dagJson.code, digest)
53
+ VERBOSE(`[getCidSync]`, { bytes, hash, digest, cid })
54
+ return cid
55
+ }
56
+ /** encode the json object into an IPLD block */
57
+ export function encodeBlock(jsonObject: any): { bytes: dagJson.ByteView<any>; cid: CID } {
58
+ DEBUG('[encodeBlock]', jsonObject)
59
+ try {
60
+ const byteView = dagJson.encode(jsonObject)
61
+ return { bytes: byteView, cid: getCidSync(byteView) }
62
+ } catch (err) {
63
+ throw ERROR('[encodeBlock] failed to encode:', jsonObject, err)
64
+ }
65
+ }
66
+
67
+ export async function encodeBlockOriginal(jsonObject: any) {
68
+ // HACK re-added this to verify the sync variant is sane
69
+ const encoded = await multiformatsEncode({ value: jsonObject, codec: dagJson, hasher: sha265Hasher })
70
+ const syncVariant = encodeBlock(jsonObject)
71
+ if (syncVariant.cid.toString() !== encoded.cid.toString()) {
72
+ ERROR(`[encodeBlockOriginal] sync cid mismatch`, { jsonObject, encoded, syncVariant })
73
+ }
74
+ return encoded
75
+ }
76
+
77
+ export function tryParseCID(cidString: CidString) {
78
+ let cid: CID | null = null
79
+ let errors = []
80
+ try {
81
+ cid = CID.parse(cidString)
82
+ } catch (err) {
83
+ VERBOSE(`[retrieveThread] couldn't parse pubID with default base`)
84
+ errors.push(err)
85
+ }
86
+ if (!cid) {
87
+ try {
88
+ cid = CID.parse(cidString, base36) // e.g. for IPNS
89
+ } catch (err) {
90
+ VERBOSE(`[retrieveThread] couldn't parse pubID with base36`)
91
+ errors.push(err)
92
+ }
93
+ }
94
+ return {
95
+ cid,
96
+ errors: cid ? null : errors, // we only care about errors if we failed to parse
97
+ isIpns: cid && isIpnsKeyCid(cid),
98
+ }
99
+ }
100
+ export function isIpnsKeyCid(cid: CID) {
101
+ return cid.code === MULTICODEC_IPNS_KEY
102
+ }
103
+
104
+ export function cidToString(cid: CID) {
105
+ if (cid.code == MULTICODEC_IPNS_KEY) {
106
+ return toIpnsString(cid)
107
+ } else {
108
+ return cid.toString()
109
+ }
110
+ }
111
+ export function toIpnsString(cid: CID) {
112
+ if (cid.code !== MULTICODEC_IPNS_KEY) throw ERROR(`Not an IPNS cid (${cid.code}):`, cid.toString())
113
+ return cid.toString(base36) as IpnsString
114
+ }
115
+ export function ensureValidCIDinstance(cidOrStringA: CID | CidString) {
116
+ return typeof cidOrStringA === 'string'
117
+ ? CID.parse(cidOrStringA)
118
+ : typeof cidOrStringA.toV1 != 'function'
119
+ ? CID.decode(cidOrStringA.bytes)
120
+ : cidOrStringA
121
+ }
122
+ export function areCidsEqual(cidOrStringA: CID | CidString, cidOrStringB: CID | CidString) {
123
+ if (!cidOrStringA || !cidOrStringB) throw new Error(`[areCidsEqual] invalid params: ${cidOrStringA}, ${cidOrStringB}`)
124
+ if (cidOrStringA === cidOrStringB) return true // shortcut if both are strings
125
+ const cidA = ensureValidCIDinstance(cidOrStringA)
126
+ const cidB = ensureValidCIDinstance(cidOrStringB)
127
+ return cidA.toV1().toString() === cidB.toV1().toString()
128
+ }
129
+ export function containsCid(list: (CID | CidString)[] | Set<CidString>, needle: CID | CidString) {
130
+ if (list instanceof Set) return list.has(typeof needle === 'string' ? needle : needle.toV1().toString()) // ? what if the CidString is a different form? (parse and format would cost performance)
131
+ return list.some(cidOrString => areCidsEqual(cidOrString, needle))
132
+ }
package/src/ipfs.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './ipfs/car.ts'
2
+ export * from './ipfs/ipfs-utils.ts'
3
+ export * from './ipfs/fetch-snapshot-chain.ts'
@@ -0,0 +1,115 @@
1
+ import { createIPNSRecord, marshalIPNSRecord, unmarshalIPNSRecord } from 'ipns'
2
+ import { privateKeyFromRaw } from '@libp2p/crypto/keys'
3
+ import { base36 } from 'multiformats/bases/base36'
4
+ import { base64pad } from 'multiformats/bases/base64'
5
+ import type { CID } from 'multiformats/cid'
6
+
7
+ export interface SignedIPNSRecord {
8
+ recordBytes: Uint8Array // marshalled protobuf, signed — the wire format
9
+ ipnsName: string // k51... string
10
+ value: string // /ipfs/<cid>
11
+ sequence: bigint
12
+ }
13
+
14
+ /** Derive IPNS name string (k51...) from Ed25519 private key bytes */
15
+ export function ipnsNameFromPrivateKey(privKeyBytes: Uint8Array): string {
16
+ const privKey = privateKeyFromRaw(privKeyBytes)
17
+ return privKey.publicKey.toCID().toString(base36)
18
+ }
19
+
20
+ /**
21
+ * Create a signed IPNS record (protobuf wire format).
22
+ * Same bytes can be published to any naming service.
23
+ */
24
+ export async function createSignedIPNSRecord(
25
+ privateKey: Uint8Array,
26
+ cid: CID,
27
+ sequence: bigint,
28
+ lifetimeMs = 365 * 24 * 60 * 60 * 1000, // 1 year
29
+ ): Promise<SignedIPNSRecord> {
30
+ const privKey = privateKeyFromRaw(privateKey)
31
+ const value = `/ipfs/${cid.toV1()}`
32
+ const record = await createIPNSRecord(privKey, value, sequence, lifetimeMs)
33
+ const recordBytes = marshalIPNSRecord(record)
34
+ const ipnsName = privKey.publicKey.toCID().toString(base36)
35
+ return { recordBytes, ipnsName, value, sequence }
36
+ }
37
+
38
+ export { unmarshalIPNSRecord }
39
+
40
+ /** A target that can receive a signed IPNS record */
41
+ export interface IPNSPublishTarget {
42
+ name: string
43
+ publish(ipnsName: string, recordBytes: Uint8Array): Promise<void>
44
+ }
45
+
46
+ /**
47
+ * Resolve current IPNS sequence number from a naming service.
48
+ * Returns null if the name was never published (404).
49
+ * Throws on network/server errors.
50
+ */
51
+ export async function resolveIPNSSequence(
52
+ nameServiceUrl: string,
53
+ ipnsName: string,
54
+ ): Promise<bigint | null> {
55
+ const url = `${nameServiceUrl}/name/${ipnsName}`
56
+ let response: Response
57
+ try {
58
+ response = await fetch(url)
59
+ } catch (err) {
60
+ throw new Error(`Network error resolving IPNS ${ipnsName}: ${err}`)
61
+ }
62
+
63
+ if (response.status === 404) return null
64
+ if (!response.ok) {
65
+ throw new Error(`HTTP ${response.status} resolving IPNS ${ipnsName}: ${response.statusText}`)
66
+ }
67
+
68
+ const { record, value } = await response.json()
69
+ if (!record && !value) return null // never published
70
+
71
+ // If raw record is available, unmarshal to get sequence
72
+ if (record) {
73
+ const bytes = base64pad.baseDecode(record)
74
+ const entry = unmarshalIPNSRecord(bytes)
75
+ return entry.sequence
76
+ }
77
+
78
+ // Some servers return value but not raw record — can't get sequence
79
+ return 0n
80
+ }
81
+
82
+ /**
83
+ * Create a signed IPNS record and publish to all configured targets.
84
+ * Resolves sequence from sequenceServiceUrl, creates record once, fans out.
85
+ * Throws if ALL targets fail; warns on partial failure.
86
+ */
87
+ export async function publishIPNSRecord(
88
+ privateKey: Uint8Array,
89
+ cid: CID,
90
+ targets: IPNSPublishTarget[],
91
+ sequenceServiceUrl = 'https://name.web3.storage',
92
+ ): Promise<SignedIPNSRecord> {
93
+ const ipnsName = ipnsNameFromPrivateKey(privateKey)
94
+ const currentSeq = await resolveIPNSSequence(sequenceServiceUrl, ipnsName)
95
+ const sequence = currentSeq != null ? currentSeq + 1n : 0n
96
+ const signed = await createSignedIPNSRecord(privateKey, cid, sequence)
97
+
98
+ const results = await Promise.allSettled(
99
+ targets.map(t => t.publish(ipnsName, signed.recordBytes))
100
+ )
101
+ const failures = results
102
+ .map((r, i) => ({ r, name: targets[i].name }))
103
+ .filter(({ r }) => r.status === 'rejected') as { r: PromiseRejectedResult; name: string }[]
104
+
105
+ if (failures.length > 0 && failures.length < targets.length) {
106
+ // Partial failure — log but don't throw
107
+ for (const { r, name } of failures) {
108
+ console.warn(`[publishIPNSRecord] target '${name}' failed:`, r.reason)
109
+ }
110
+ } else if (failures.length === targets.length) {
111
+ throw new Error(`All IPNS publish targets failed: ${failures.map(({ r, name }) => `${name}: ${r.reason}`).join('; ')}`)
112
+ }
113
+
114
+ return signed
115
+ }
package/src/ipns.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './ipns/ipns-record.ts'
@@ -0,0 +1,217 @@
1
+ # UCAN Specifications and Implementations (2026 Overview)
2
+
3
+ [via Perplexity](https://www.perplexity.ai/search/ucan-specs-and-implementations-L_I1Xih5Se6CVF.FQS2rFQ)
4
+
5
+ ## Overview
6
+
7
+ User Controlled Authorization Networks (UCANs) define a distributed, capability-based authorization system centered on verifiable delegation chains and decentralized identifiers (DIDs). The ecosystem consists of a core v1.0.0-rc.1 specification with several sub-specifications plus multiple language implementations at different maturity and spec levels.[^1][^2]
8
+
9
+ ## Core UCAN v1 Specification
10
+
11
+ The main UCAN specification v1.0.0-rc.1 describes UCAN as a trustless, secure, local-first, user-originated distributed authorization scheme that uses certificate-style capabilities instead of ACLs. It defines a DAG-CBOR/IPLD-based envelope format with content-addressed CIDs, a cryptographic suite (Ed25519, P-256, secp256k1, SHA-256), and separates lifecycle stages into Delegation, Invocation, Promise, and Revocation sub-specs.[^1]
12
+
13
+ ### Key v1 design elements
14
+
15
+ - Capabilities are modeled as triples of subject, command, and policy, capturing what a principal may do to which resource under which constraints.
16
+ - Tokens are encoded as UCAN envelopes (signature bytes plus a signed payload with a type tag like `ucan/<subspec>@version`) instead of traditional JWTs.
17
+ - Payloads include `iss`, `aud`, `sub`, `cmd`, `args`, `nonce`, optional `meta`, and time bounds `nbf`/`exp`, with validation rules over time, principal alignment, and signatures.
18
+
19
+ ## Delegation Sub-Specification
20
+
21
+ The UCAN Delegation spec v1.0.0-rc.1 refines how attenuated authority is represented and delegated between principals. It uses the envelope tag `ucan/[email protected]` and defines a Delegation payload with issuer, audience, subject, command, policy, nonce, metadata, and time bounds.
22
+
23
+ ### Policy language and selectors
24
+
25
+ Delegation introduces a syntactically driven policy language using predicate trees with operators such as equality, inequality, glob matching (`like`), logical connectives, and quantifiers (`all`/`any`). Policies constrain an eventual invocation’s `args` field using jq-inspired selectors (for example `.to[^1]`, `.a[0:2]`, `.field?`) that always resolve to IPLD values.
26
+
27
+ ### Powerline and resource model
28
+
29
+ The spec defines a "Powerline" pattern where `sub: null` allows automatic redelegation of a user’s authority across devices, while still enforcing principal alignment, time bounds, and policy constraints. Resources are treated semantically, with the subject as the default resource, and external resources referenced via conditions in the policy.
30
+
31
+ ## Other v1 Sub-Specifications
32
+
33
+ The main spec lists additional required or recommended sub-specifications: Delegation and Invocation are required, while Promise and Revocation are recommended for a complete lifecycle. Invocation describes how to exercise capabilities proven by delegations, Promise covers awaiting invocation results, and Revocation describes undoing delegations to break chains for malicious or compromised principals.[^3]
34
+
35
+ ## Pre‑v1 UCAN Specifications (JWT-Based)
36
+
37
+ Before v1, the UCAN ecosystem used a JWT-based format where capabilities were carried in an `att` (attenuation) field and proof chains in a `prf` field. The gobengo/ucanto-spec repository documents UCAN Specification v0.8.1 using JWT headers with `alg` and a UCAN version field, and payload fields like `att`, `aud`, `exp`, `fct`, `iss`, `nbf`, and `prf`.[^4][^5]
38
+
39
+ ### Legacy library semantics
40
+
41
+ The ucan-wg/ts-ucan TypeScript library mirrors this earlier spec, describing UCANs as JWTs with a header containing `alg`, `typ`, and `uav` (UCAN version), and a payload including `att`, `aud`, `exp`, `fct`, `iss`, `nbf`, and `prf`. Elixir’s ExUcan library similarly models JWT-based UCANs with header fields and payload fields such as `ucv` (version), `cap` (capabilities), `aud`, `exp`, `iss`, `nbf`, `prf`, and `nnc` (nonce).[^6][^7][^4]
42
+
43
+ ## Migration from JWT UCANs to v1 Envelope UCANs
44
+
45
+ The UCAN Library Implementation Guide explains that v1.0.0-rc.1 introduces a new envelope format, IPLD/CBOR encoding, structured capabilities, a richer policy language, and distinct sub-specs, replacing the earlier JWT representation. Pull requests for the Delegation and Invocation specs emphasize the move to IPLD-based tokens and UCAN-specific type tags like `ucan/[email protected]` as part of this migration.[^8][^9][^3]
46
+
47
+ ### High-level differences
48
+
49
+ - Encoding: legacy UCANs use JWT (base64url JSON), whereas v1 UCANs use CID-addressed DAG-CBOR/IPLD envelopes.[^5]
50
+ - Capability model: legacy specs use `att` and `prf` arrays, while v1 organizes capabilities as subject/command/policy triples and separates Delegation from Invocation.[^5]
51
+ - Policy: v0.x has relatively simple attenuation semantics; v1 adds a full predicate logic policy language with jq-style selectors and quantifiers.[^5]
52
+
53
+ ## Official UCAN Working Group Libraries
54
+
55
+ The UCAN Working Group organization lists libraries in TypeScript (NPM), Rust (Crate), Golang, Haskell, JS IPLD helpers, and UCAN-RPC tools as the primary ecosystem components. It also notes additional specs like a UCAN container and classifies some older specs (HTTP bearer token, IPLD, JWT canonicalization) as outdated or obsolete.[^2]
56
+
57
+ ### Rust: rs-ucan and ucan crate
58
+
59
+ The ucan-wg/rs-ucan repository hosts the Rust implementation, providing the `ucan` and `ucan-key-support` crates. The `ucan` crate offers a `UcanBuilder` abstraction for constructing signed tokens and a `ProofChain` for validating chains and reducing capabilities according to domain-specific semantics.[^10][^11]
60
+
61
+ The `ucan-key-support` crate supplies ready-to-use `SigningKey` implementations compatible with the core crate, simplifying cryptographic integration. This stack targets the modern UCAN spec and is referenced from the main spec and the ucan.xyz site as the Rust implementation.[^12][^10]
62
+
63
+ ### Go: go-ucan
64
+
65
+ The ucan-wg/go-ucan repository and its documentation describe a Go library designed to help web and decentralized applications integrate UCAN authorization flows. The library explicitly states that it supports the required parts of the UCAN specification—main spec, Delegation, and Invocation—while Revocation and Promise are not yet implemented.[^13][^14]
66
+
67
+ Go-ucan integrates with a companion DID library (`go-did-it`), provides encrypted values in token metadata, and includes a UCAN container format for packaging multiple tokens together. The ucan.xyz Go library page reiterates this scope and positions go-ucan as the reference Go implementation.[^14][^13]
68
+
69
+ ### TypeScript (legacy): ts-ucan
70
+
71
+ The ucan-wg/ts-ucan library implements the earlier JWT-based UCAN spec, emphasizing UCANs as JWTs with special keys, DIDs for issuer and audience, and offline/offline-friendly authorization flows. Its README walks through the header structure (`alg`, `typ`, `uav`) and payload fields for attenuation and proofs, matching the v0.8.x spec’s `att` and `prf` fields rather than the v1 envelope.[^4][^6][^5]
72
+
73
+ ## iso-ucan JavaScript Implementation
74
+
75
+ Hugo Dias’s iso-ucan package (within the `hugomrdias/iso-repo` monorepo) is an isomorphic JavaScript implementation oriented around the v1 UCAN semantics. It is documented on ucan.xyz as the canonical JavaScript UCAN library and underpins the site’s examples and getting-started guides.[^15][^16]
76
+
77
+ ### API and semantics
78
+
79
+ The iso-ucan README shows a usage pattern where capabilities are defined via `Capability.from({ schema, cmd })`, delegations are created with `Capability.delegate({ iss, aud, sub, pol, exp })`, and invocations are produced with `Capability.invoke({ iss, sub, args, store, exp })`. A `Store` abstraction backed by an `iso-kv` driver manages delegations, and signing uses EdDSA keys via `iso-signatures` (`EdDSASigner`).
80
+
81
+ The ucan.xyz JavaScript library page and examples demonstrate the same patterns, using iso-ucan to define typed capabilities, delegate between identities, and create invocations with explicit command paths and argument schemas. This aligns closely with the v1 Delegation and Invocation specs’ focus on `cmd` paths, `pol` policy arrays, and argument-based validation.[^17][^15]
82
+
83
+ ## Substrate-System iso-ucan Fork
84
+
85
+ The `substrate-system/iso-ucan` repository is a fork of Hugo Dias’s iso-ucan package, published on NPM as `@substrate-system/iso-ucan`. Its README states explicitly that it is a fork of `hugomrdias/iso-ucan` and focuses on packaging for different module systems and distribution formats.[^18]
86
+
87
+ ### Distribution and packaging differences
88
+
89
+ The fork exposes both ESM and CommonJS entry points via the `exports` field, allowing import through either `import '@substrate-system/iso-ucan'` or `require('@substrate-system/iso-ucan')`. It also ships pre-built, minified JavaScript bundles (`dist/module.min.js`) that can be copied into a web server’s public directory and loaded via a `<script type="module" src="./module.min.js"></script>` tag.
90
+
91
+ Functionally, the fork does not describe any behavioral divergence from upstream iso-ucan; it is presented as a repackaging intended to ease consumption in Substrate-related and browser contexts rather than a new spec variant.
92
+
93
+ ## Additional JavaScript Implementations
94
+
95
+ Fission’s `@fission-codes/ucan` package provides another JavaScript UCAN library, offering a high-level `UCAN.create` API that accepts an issuer, audience, and a capabilities object mapping resources to allowed actions. The README points developers to Fission’s stack documentation for details and promotes dual Apache-2.0 and MIT licensing.[^19]
96
+
97
+ The ucan.xyz examples and getting-started guide consistently highlight iso-ucan as the recommended JavaScript implementation for current v1 semantics. Legacy JWT-style libraries like ts-ucan remain useful for existing deployments but are not the focus of recent documentation.[^16][^15][^8][^4][^5]
98
+
99
+ ## Node.js Wrapper over Rust: @myjoypin/node-ucan
100
+
101
+ The `@myjoypin/node-ucan` NPM package wraps the Rust rs-ucan library in a Node.js module, providing Node 18 binaries for common platforms and building from source elsewhere. Its documentation notes that it targets UCAN Specification v0.10.0, indicating that it lags behind the v1.0.0-rc.1 spec.[^20]
102
+
103
+ The package demonstrates how UCANs can be used as bearer-style tokens and delegated without server involvement, with examples of a server delegating rights to Alice, and Alice delegating a subset of her rights to Bob using `invokeUcan` and `verifyUcan` helpers.[^20]
104
+
105
+ ## Other Community Implementations
106
+
107
+ Beyond the core working-group libraries, there are several community UCAN implementations across languages. These typically implement older JWT-based specs or partial subsets of v1 features.[^21][^22]
108
+
109
+ ### Go community libraries
110
+
111
+ The `github.com/qri-io/ucan` package provides an early Go implementation of UCAN tokens from Fission, describing them as an authenticated digraph in an authorization space and focusing on attenuation and delegation using nested capabilities. Documentation characterizes it as "under heavy construction" and associates it with Fission’s original whitepaper.[^23][^24]
112
+
113
+ A separate `github.com/vibrantgenius/go-ucan` module advertises itself as aligned with UCAN v1.0.0-rc.1 and provides Go module packaging, though detailed documentation is limited in the high-level listing. The ucan-wg go-ucan library remains the primary reference implementation for current v1 semantics.[^25][^14]
114
+
115
+ ### Elixir and other languages
116
+
117
+ The ExUcan library (Elixir) implements decentralized auth with UCANs, following the JWT-based model with header fields and payload fields including `ucv`, `cap`, `aud`, `exp`, `fct`, `nnc`, `iss`, `nbf`, and `prf`. Documentation highlights offline-first authorization and use of DIDs for issuer and audience, mirroring ts-ucan’s conceptual model.[^7]
118
+
119
+ The UCAN Working Group’s organization page notes the existence of Haskell libraries and UCAN IPLD tooling in JavaScript, but detailed documentation for these was not surfaced in the high-level search results. They appear to be earlier or auxiliary efforts relative to the main Rust, Go, and iso-ucan stacks.[^22][^2]
120
+
121
+ ## UCAN Library Implementation Guide
122
+
123
+ The UCAN Library Implementation Guide on ucan.xyz defines requirements for a complete v1 library: delegation creation and validation, invocation creation, capability management, cryptographic operations, and envelope handling. It emphasizes that v1 libraries must implement the envelope format, type tags, structured capabilities, IPLD/CBOR encoding, and the policy language.[^8]
124
+
125
+ The guide lists major changes in v1, including the move away from JWT to UCAN-specific envelopes, structured capabilities that separate subject, command, and policy, and distinct Delegation, Invocation, Promise, and Revocation documents. This guidance underpins the design of rs-ucan, go-ucan, and iso-ucan as the v1-aligned libraries.[^8]
126
+
127
+ ## Comparison of Specifications
128
+
129
+ ### Specification-level differences
130
+
131
+ | Aspect | v0.8.x JWT spec (ucanto-spec, ts-ucan, ExUcan) | v1.0.0-rc.1 UCAN spec and sub-specs |
132
+ |-------|-----------------------------------------------|--------------------------------------|
133
+ | Encoding | JWT (header, payload, signature as base64url JSON) | UCAN envelope: signature bytes plus IPLD DAG-CBOR payload addressed by CID |
134
+ | Capability fields | `att` (attenuation list), `prf` (proof tokens), `cap` in some variants | `sub`, `cmd`, `pol` capabilities; `args` for invocation arguments |
135
+ | Versioning | Header field (`uav`/`ucv`), overall spec version like v0.8.1 | Type tags `ucan/<subspec>@version` (for example `ucan/[email protected]`) and spec version 1.0.0-rc.1 |
136
+ | Policy model | Simple attenuation semantics on `att` and basic field constraints | Full predicate policy language with comparison, glob, logical, and quantifier operators over jq-style selectors |
137
+ | Lifecycle docs | Single spec describing tokens, plus whitepaper | Main spec plus Delegation, Invocation, Promise, Revocation specs with explicit lifecycle and validation rules |
138
+ | Transport | JWT-focused, often HTTP bearer-centric | Transport-agnostic; tokens are IPLD objects identified by CIDs and may be transported via various mechanisms |
139
+
140
+ Sources: v0.8.1 spec, ts-ucan, ExUcan, v1 spec, Delegation spec.[^7][^4][^5]
141
+
142
+ ## Comparison of Implementations
143
+
144
+ ### Library coverage and focus
145
+
146
+ | Library | Language | Maintainer | Spec family | Encoding | Focus / Notes |
147
+ |--------|----------|-----------|------------|----------|---------------|
148
+ | iso-ucan | JavaScript/TypeScript | Hugo Dias | v1 Delegation/Invocation | IPLD/DAG-CBOR via UCAN envelopes | Isomorphic JS library with typed capabilities, EdDSA signing, and pluggable Stores; showcased on ucan.xyz as main JS library.[^15][^16] |
149
+ | @substrate-system/iso-ucan | JavaScript/TypeScript | substrate-system | v1 (fork of iso-ucan) | Same as iso-ucan | Fork providing ESM/CJS exports and pre-built minified bundles for browser use; behavior follows upstream iso-ucan. |
150
+ | ts-ucan | TypeScript | UCAN Working Group | v0.8.x JWT | JWT | Older library using JWT with `att`/`prf` payload; aligned with gobengo/ucanto v0.8.1 spec.[^4][^6][^5] |
151
+ | @fission-codes/ucan | TypeScript | Fission | Legacy UCAN | Likely JWT (not explicitly stated) | High-level Fission stack library with `UCAN.create` helper and EdDSA signing; docs refer to Fission stack.[^19] |
152
+ | ucan (Rust crate) | Rust | UCAN Working Group | v1 | UCAN envelope | Core Rust implementation with `UcanBuilder` and `ProofChain` for building and validating UCAN chains.[^10][^11] |
153
+ | ucan-key-support | Rust | UCAN Working Group | v1 | UCAN envelope | Auxiliary crate providing ready-made `SigningKey` implementations for the Rust UCAN library.[^12] |
154
+ | go-ucan (wg) | Go | UCAN Working Group | v1 (Delegation & Invocation) | UCAN envelope | Official Go implementation; supports main spec, Delegation, Invocation; Revocation and Promise to come; includes DID support and container format.[^13][^14] |
155
+ | qri-io/ucan | Go | Qri | Early UCAN (JWT-style) | JWT | Early Go implementation of Fission’s UCAN tokens based on the whitepaper; marked under heavy construction.[^23][^24] |
156
+ | @myjoypin/node-ucan | Node.js (Rust FFI) | Community | UCAN v0.10.0 | UCAN envelope / hybrid | Node bindings over rs-ucan; documented as targeting UCAN spec v0.10.0, with examples of delegation and verification in Node.[^20] |
157
+ | ExUcan | Elixir | Community | v0.x JWT | JWT | Elixir library replicating JWT-based UCAN fields such as `ucv`, `cap`, `aud`, `exp`, `fct`, `nnc`, `iss`, `nbf`, `prf`.[^7] |
158
+
159
+ ## Practical Implications for Implementers
160
+
161
+ For new projects, the v1.0.0-rc.1 spec family and corresponding libraries (iso-ucan, rs-ucan/ucan crate, and go-ucan) provide the most future-proof foundation, thanks to structured capabilities, a powerful policy language, and CID-addressed IPLD encoding. Developers integrating with existing JWT-based UCAN deployments may still rely on ts-ucan, ExUcan, or qri-io/ucan but should plan for migration if they want to interoperate with v1-only tooling.[^10][^14][^4][^7][^5]
162
+
163
+ The substrate-system/iso-ucan fork is useful where bundling and module system support are primary concerns, since it repackages iso-ucan without altering semantics. Node environments needing Rust-level performance can use @myjoypin/node-ucan, but must be aware that it targets an earlier spec revision (v0.10.0) and may lack newer v1 features.[^20][^8]
164
+
165
+ ---
166
+
167
+ ## References
168
+
169
+ 1. [User Controlled Authorization Network (UCAN) Specification](https://ucan.xyz/specification/) - User-Controlled Authorization Network (UCAN) is a [trustless], secure, [local-first], user-originate...
170
+
171
+ 2. [UCAN Working Group](https://github.com/ucan-wg) - Decentralized Auth — User Controlled Authorization Networks - UCAN Working Group
172
+
173
+ 3. [v1.0.0-rc.1: High level invocation spec by expede · Pull Request #21 · ucan-wg/invocation](http://github.com/ucan-wg/invocation/pull/21) - Preview 📜 Supercedes V0.2 #15 TODOs Bump version to 1.0.0-rc.1 Closes Addressing individual task res...
174
+
175
+ 4. [ts-ucan/README.md at main - GitHub](https://github.com/ucan-wg/ts-ucan/blob/main/README.md) - UCANs are JWTs that contain special keys. At a high level, UCANs (“User Controlled Authorization Net...
176
+
177
+ 5. [GitHub - gobengo/ucanto-spec: User Controlled Authorization Network (UCAN) Specification](https://github.com/gobengo/ucanto-spec) - User Controlled Authorization Network (UCAN) Specification - gobengo/ucanto-spec
178
+
179
+ 6. [GitHub - ucan-wg/ts-ucan: Auth tokens for a distributed, user-controlled world](https://github.com/ucan-wg/ts-ucan) - Auth tokens for a distributed, user-controlled world - ucan-wg/ts-ucan
180
+
181
+ 7. [GitHub - spawnfest/youcan](https://github.com/spawnfest/youcan) - Contribute to spawnfest/youcan development by creating an account on GitHub.
182
+
183
+ 8. [UCAN Library Implementation Guide](https://ucan.xyz/libraries/implementation/) - A comprehensive guide for implementing UCAN libraries in different programming languages
184
+
185
+ 9. [v1.0.0-rc.1 by expede · Pull Request #2 · ucan-wg/delegation](http://github.com/ucan-wg/delegation/pull/2) - I may get pilloried for this version. WIP, obviously Preview 📚 Okay, this version switches to IPLD. ...
186
+
187
+ 10. [Crate ucan Copy item path](https://docs.rs/ucan/latest/ucan/) - Implement UCAN-based authorization with conciseness and ease!
188
+
189
+ 11. [rs-ucan/README.md at main · ucan-wg/rs-ucan](https://github.com/ucan-wg/rs-ucan/blob/main/README.md) - Rust implementation of UCAN. Contribute to ucan-wg/rs-ucan development by creating an account on Git...
190
+
191
+ 12. [ucan-key-support — Rust crypto library // Lib.rs](https://lib.rs/crates/ucan-key-support) - Ready to use SigningKey implementations for the ucan crate
192
+
193
+ 13. [GitHub - ucan-wg/go-ucan: User-Controlled Authorization Network (UCAN) tokens in go](https://github.com/ucan-wg/go-ucan) - User-Controlled Authorization Network (UCAN) tokens in go - ucan-wg/go-ucan
194
+
195
+ 14. [Go Implementation - UCAN](https://ucan.xyz/libraries/go/) - Documentation for Go Implementation
196
+
197
+ 15. [iso-ucan | UCAN](https://ucan.xyz/libraries/javascript/) - Documentation for iso-ucan.
198
+
199
+ 16. [Getting Started with UCAN](https://ucan.xyz/getting-started/) - import { Capability } from "iso-ucan/capability". import { EdDSASigner } ... User Controlled Authori...
200
+
201
+ 17. [UCAN Examples](https://ucan.xyz/guides/examples/) - Note: These examples use the v1.0.0-rc.1 UCAN specification and the iso-ucan JavaScript library. The...
202
+
203
+ 18. [@substrate-system/iso-ucan - npm](https://www.npmjs.com/package/@substrate-system%2Fiso-ucan) - UCAN. Latest version: 0.0.2, last published: 2 months ago. Start using @substrate-system/iso-ucan in...
204
+
205
+ 19. [@fission-codes/ucan](https://www.npmjs.com/package/@fission-codes/ucan?activeTab=readme) - UCAN (User Controlled Authorization Networks) is a decentralized authorization protocol for the web....
206
+
207
+ 20. [@myjoypin/node-ucan](https://www.npmjs.com/package/@myjoypin/node-ucan?activeTab=code) - UCAN for Node.js. Latest version: 0.1.0, last published: 10 months ago. Start using @myjoypin/node-u...
208
+
209
+ 21. [Build software better, together](https://github.com/topics/ucan) - GitHub is where people build software. More than 100 million people use GitHub to discover, fork, an...
210
+
211
+ 22. [Stabilizing the Object Capability System - ForgeFed](https://forgefed.org/blog/stabilizing-ocaps/) - Existing UCAN implementations in both Go (for Forgejo) and Haskell (for Vervis); Capabilities are cr...
212
+
213
+ 23. [README ¶](https://pkg.go.dev/github.com/dholms/ucan) - Package ucan implements User-Controlled Authorization Network tokens by fission: https://whitepaper....
214
+
215
+ 24. [ucan package - github.com/qri-io/ucan - Go Packages](https://pkg.go.dev/github.com/qri-io/ucan) - Package ucan implements User-Controlled Authorization Network tokens by fission: https://whitepaper....
216
+
217
+ 25. [go-ucan module - github.com/vibrantgenius/go-ucan - Go Packages](https://pkg.go.dev/github.com/vibrantgenius/go-ucan) - github.com/vibrantgenius/go-ucan. go-ucan. module ... go-ucan. UCAN v1.0.0-rc.1 GitHub Tag · Build S...
@@ -0,0 +1,9 @@
1
+ import { CarReader } from '@ipld/car'
2
+ import { CID } from 'multiformats'
3
+
4
+ export interface StorageConnector {
5
+ storeCar(car: Blob, signal?: AbortSignal): Promise<CID>
6
+ }
7
+ export interface RetrievalConnector {
8
+ retrieveCar(cid: CID): Promise<CarReader>
9
+ }
@@ -0,0 +1,31 @@
1
+ import { Logger } from 'besonders-logger'
2
+ import { CID } from 'multiformats'
3
+ import { ensureTsPvAndFinalizeApplog } from '../applog/applog-helpers.ts'
4
+ import { EntityID } from '../applog/datom-types.ts'
5
+ import { Thread } from '../thread.ts'
6
+
7
+ const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars
8
+
9
+ export type PubPullData = {
10
+ cid: CID
11
+ thread: Thread
12
+ }
13
+
14
+ export function integratePub({ targetThread, agentHash, subID, pubData }: {
15
+ targetThread: Thread
16
+ agentHash: EntityID
17
+ pubData: PubPullData
18
+ subID?: EntityID
19
+ }) {
20
+ const newLogs = pubData.thread.applogs.filter(log => !targetThread.hasApplog(log, false))
21
+ DEBUG(`[integratePub] integrating ${newLogs.length} logs`, { targetThread, subID, pubData })
22
+ let toInsert = newLogs
23
+ if (subID) {
24
+ toInsert = toInsert.concat(ensureTsPvAndFinalizeApplog(
25
+ { en: subID, at: 'subscription/cid', vl: pubData.cid.toString(), ag: agentHash },
26
+ targetThread,
27
+ ))
28
+ }
29
+ targetThread.insertRaw(toInsert)
30
+ return newLogs
31
+ }
@@ -0,0 +1,90 @@
1
+ import { CID } from 'multiformats/cid'
2
+ import { cyrb53hash } from './../applog/applog-utils.ts'
3
+ import { AgentHash, AgentID, CidString } from '../applog/datom-types.ts'
4
+ import { Tagged } from '../types.ts'
5
+ import { UCANCapMap } from './ucan.ts'
6
+ type AgentString = Tagged<string, 'AgentString'>
7
+ type DIDString = Tagged<string, 'DID'>
8
+ export type { AgentHash, AgentString, DIDString }
9
+
10
+
11
+ export interface AppAgent {
12
+ ag: AgentHash
13
+ agentString: AgentString
14
+ did: DIDString
15
+ sign? (data: Uint8Array): Promise<Uint8Array>
16
+ ucan?: UCANCapMap
17
+ }
18
+ export interface AppAgentForWorker extends AppAgent {
19
+ signerSecret?: Uint8Array
20
+ }
21
+ export interface SnapRootBlock {
22
+ applogs: CID
23
+ applogsSignature: Uint8Array
24
+ info: CID
25
+ infoSignature: Uint8Array
26
+ prev?: CID
27
+ prevCounter?: number | null // Sequential counter: 0 for first, then +1 for each. null = unknown (legacy/error).
28
+ }
29
+ export interface SnapBlockLogs {
30
+ logs: CID[]
31
+ }
32
+ export interface SnapBlockChunks {
33
+ chunks: CID[]
34
+ }
35
+ export type SnapBlockLogsOrChunks = SnapBlockLogs | SnapBlockChunks
36
+
37
+ // HACK: this is actually note3 types, not wovin
38
+ export interface IShare {
39
+ id?: string // string hash of pub (used as unique id in IDB) `W3Name.create().toString()` starts with k51qzi5uqu5d
40
+ createdAt: string // ISO timestamp of creation
41
+ name: string // nick name for the pub
42
+ isDeleted?: boolean
43
+ purgeBeforePush?: boolean
44
+
45
+ pk: Uint8Array // exported privatekey - needed to create WritableName for publishing //TODO: store as non-extractable / encrypted?
46
+
47
+ autopush: boolean
48
+ lastPush: string | null
49
+ lastCID?: string
50
+ latestLogTs?: string
51
+ pubCounter?: number
52
+
53
+ publishedBy: string // local user appAgent
54
+ selectors?: string[] // to be used as a filter for which applogs to pub
55
+ encryptedFor?: string | null // short agentHash
56
+ encryptedWith?: CryptoKey | null // AES-GCM derived key from ECDH keys (local private and remote public)
57
+
58
+ // HACK WIP #39 - shared encryption
59
+ sharedKey?: CryptoKey | null // AES-GCM derived key from ECDH keys (local private and ipns public)
60
+ sharedAgents?: AgentID[] | null // array of string EntityIDs for the chosen agents (we need public jwkd atoms for each of them)
61
+ sharedKeyMap?: Map<AgentID, string> | null // uses public key from each agent to derive an aes key that is used to encrypt and btoa the sharedKey that is actually used to encrypt and decrypt the applogs
62
+ }
63
+ export interface ISubscription {
64
+ id: string // string hash of pub (used as unique id in IDB) `W3Name.create().toString()` starts with k51qzi5uqu5d
65
+ createdAt: string // ISO timestamp of creation
66
+ name: string // nick name for the pub
67
+ isDeleted: boolean
68
+
69
+ lastPull?: string | null
70
+ lastPullAttempt?: string | null
71
+ autopull: boolean
72
+ realtime?: boolean // enable real-time WebSocket updates via IPNS watcher
73
+ lastCID?: string // ? why not CidString
74
+ lastApplogCID?: string
75
+ publishedBy?: string // remote publisher short agentHash
76
+ encryptedFor?: string | undefined // short agentHash
77
+ encryptedWith?: CryptoKey | undefined // AES-GCM derived key from ECDH keys (local private and remote public)
78
+ }
79
+ export function isShare(obj: any): obj is IShare {
80
+ return obj?.pk !== undefined && obj?.lastPush !== undefined
81
+ }
82
+ export function isSubscription(obj: any): obj is ISubscription {
83
+ return obj?.lastPull !== undefined
84
+ }
85
+
86
+ export type TShareSub = IShare | ISubscription
87
+
88
+ export function agentToShortHash(agentString: string) {
89
+ return cyrb53hash(agentString, 31, 7) as string
90
+ }