@meddleware/seal-ui 0.0.30 → 0.0.32
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.
- package/CHANGELOG.md +12 -0
- package/package.json +8 -7
- package/src/components/SealView.vue +8 -1
- package/src/seal.ts +2 -0
- package/src/sealed-content.ts +19 -4
- package/src/walrus.ts +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
All notable changes to `@meddleware/seal-ui` are documented here.
|
|
4
4
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows [Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [0.0.32] - 2026-10-08
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- Operator-only discovery by default (opt-in for other publishers), refuse non-linked gates when sealing, publisher result handling; seal-client 0.0.16, access-gate-client 0.0.6, walrus-client 0.0.26
|
|
11
|
+
|
|
12
|
+
## [0.0.31] - 2026-10-03
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- seal-client 0.0.15, walrus-client 0.0.25, access-gate-client 0.0.4, ui 0.1.30, design-tokens 0.1.8.
|
|
17
|
+
|
|
6
18
|
## [0.0.30] - 2026-10-02
|
|
7
19
|
|
|
8
20
|
- **What you should know before sealing** (always shown): the key-server threshold, that the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meddleware/seal-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.32",
|
|
4
4
|
"homepage": "https://docs.meddleware.co.uk/blockchain/sui/sealed-storage/",
|
|
5
5
|
"description": "Standalone Vue 3 SPA for Sealed Storage — client-side Seal encryption + access-gated, decentralised Walrus storage on Sui.",
|
|
6
6
|
"author": "MeddleWare <dev@meddleware.co.uk>",
|
|
@@ -44,12 +44,12 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@meddleware/access-gate-client": "^0.0.
|
|
48
|
-
"@meddleware/design-tokens": "^0.1.
|
|
49
|
-
"@meddleware/seal-client": "^0.0.
|
|
50
|
-
"@meddleware/ui": "^0.1.
|
|
51
|
-
"@meddleware/walrus-client": "^0.0.
|
|
52
|
-
"@mysten/sui": "^2.33.
|
|
47
|
+
"@meddleware/access-gate-client": "^0.0.6",
|
|
48
|
+
"@meddleware/design-tokens": "^0.1.8",
|
|
49
|
+
"@meddleware/seal-client": "^0.0.16",
|
|
50
|
+
"@meddleware/ui": "^0.1.30",
|
|
51
|
+
"@meddleware/walrus-client": "^0.0.26",
|
|
52
|
+
"@mysten/sui": "^2.33.2",
|
|
53
53
|
"@mysten/wallet-standard": "^0.21.0",
|
|
54
54
|
"vue": "^3.5.43"
|
|
55
55
|
},
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"@meddleware/wallet-adapter": ">=0.0.12 <0.2.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
+
"@meddleware/eslint-config": "^0.0.1",
|
|
60
61
|
"@meddleware/wallet-adapter": "^0.0.13",
|
|
61
62
|
"@types/node": "~24.12.2",
|
|
62
63
|
"@vitejs/plugin-vue": "^6.0.9",
|
|
@@ -394,6 +394,8 @@ const unlockGateId = ref('')
|
|
|
394
394
|
const discovered = ref<SealedContentPointer[]>([])
|
|
395
395
|
const unlockNftId = ref('')
|
|
396
396
|
const unlockSoulbound = ref(false)
|
|
397
|
+
// Seal gives confidentiality, not authenticity: by default only the gate operator's pointers are listed.
|
|
398
|
+
const includeOthers = ref(false)
|
|
397
399
|
|
|
398
400
|
async function performDiscover(): Promise<void> {
|
|
399
401
|
errorMsg.value = null
|
|
@@ -405,7 +407,7 @@ async function performDiscover(): Promise<void> {
|
|
|
405
407
|
busy.value = true
|
|
406
408
|
try {
|
|
407
409
|
status.value = 'Searching for sealed content…'
|
|
408
|
-
discovered.value = await discoverSealedContent(unlockGateId.value)
|
|
410
|
+
discovered.value = await discoverSealedContent(unlockGateId.value, { includeOthers: includeOthers.value })
|
|
409
411
|
status.value = discovered.value.length
|
|
410
412
|
? `Found ${discovered.value.length} item(s).`
|
|
411
413
|
: 'No sealed content published for this gate.'
|
|
@@ -621,6 +623,10 @@ async function performUnlock(item: SealedContentPointer): Promise<void> {
|
|
|
621
623
|
<button v-if="!unlockGateManual" type="button" class="link link--spaced" @click="unlockGateManual = true; unlockGateId = ''">Enter ID manually</button>
|
|
622
624
|
<button v-else type="button" class="link link--spaced" @click="unlockGateManual = false; unlockGateId = ''">← Back to picker</button>
|
|
623
625
|
</template>
|
|
626
|
+
<label class="field checkbox">
|
|
627
|
+
<input type="checkbox" v-model="includeOthers" />
|
|
628
|
+
<span>Also list content published by other addresses (unverified: anyone can publish to a gate)</span>
|
|
629
|
+
</label>
|
|
624
630
|
<button type="button" class="primary" :disabled="busy || !unlockGateId" @click="performDiscover">
|
|
625
631
|
{{ busy ? 'Working…' : 'Find sealed content' }}
|
|
626
632
|
</button>
|
|
@@ -642,6 +648,7 @@ async function performUnlock(item: SealedContentPointer): Promise<void> {
|
|
|
642
648
|
|
|
643
649
|
<article v-for="item in discovered" :key="item.contentId" class="card discovered">
|
|
644
650
|
<h3 class="discovered__title">{{ item.label || '(untitled)' }}</h3>
|
|
651
|
+
<p v-if="includeOthers" class="muted muted--sm">Published by {{ item.publisher }}: decrypting proves it was sealed to this gate, not who wrote it.</p>
|
|
645
652
|
<p class="muted discovered__blob">blob {{ item.blobId }}</p>
|
|
646
653
|
<button type="button" class="primary" :disabled="busy || !account || !unlockNftId" @click="performUnlock(item)">
|
|
647
654
|
Unlock & download
|
package/src/seal.ts
CHANGED
|
@@ -44,6 +44,8 @@ export async function getSealController(): Promise<SealController> {
|
|
|
44
44
|
policyConfigId: cfg.seal.policyConfigId,
|
|
45
45
|
threshold: cfg.threshold,
|
|
46
46
|
serverConfigs: cfg.servers,
|
|
47
|
+
// Sealing to a gate of another access_gate package can never be unlocked: refuse it.
|
|
48
|
+
accessGateOriginalId: cfg.accessGateOriginalId || undefined,
|
|
47
49
|
},
|
|
48
50
|
registryFor(cfg),
|
|
49
51
|
)
|
package/src/sealed-content.ts
CHANGED
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
// Discover on-chain SealedContent pointers for a gate with seal-client's `listSealedContent`
|
|
2
2
|
// (exact event type at the deployment's original id; BCS-decoded). Reads the read-indexer when
|
|
3
3
|
// VITE_INDEXER_URL is set — display data only, falling back to the full node.
|
|
4
|
-
import { listSealedContent, type SealedContentPointer } from '@meddleware/seal-client'
|
|
4
|
+
import { gateOperators, listSealedContent, type SealedContentPointer } from '@meddleware/seal-client'
|
|
5
5
|
import { getSuiClient } from './wallet.js'
|
|
6
6
|
import { activeConfig, INDEXER_URL } from './config.js'
|
|
7
7
|
|
|
8
8
|
/** Most pointers listed for one gate. */
|
|
9
9
|
export const MAX_DISCOVERED = 200
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Published pointers for `gateId` on the active network, newest first.
|
|
13
|
+
*
|
|
14
|
+
* Seal gives confidentiality, not authenticity: anyone can seal content to a gate and publish a
|
|
15
|
+
* pointer for it, and a pass holder decrypts it successfully. So by default only the gate
|
|
16
|
+
* operator's pointers are listed (`gateOperators`); `includeOthers` lists every publisher's, for the
|
|
17
|
+
* view to label as unverified.
|
|
18
|
+
*/
|
|
19
|
+
export async function discoverSealedContent(
|
|
20
|
+
gateId: string,
|
|
21
|
+
opts: { includeOthers?: boolean } = {},
|
|
22
|
+
): Promise<SealedContentPointer[]> {
|
|
13
23
|
const cfg = activeConfig.value
|
|
14
24
|
if (!cfg.seal) throw new Error(cfg.problem ?? `Sealed Storage is not available on ${cfg.network}.`)
|
|
15
|
-
const
|
|
25
|
+
const client = getSuiClient()
|
|
26
|
+
const publishers = opts.includeOthers
|
|
27
|
+
? undefined
|
|
28
|
+
: await gateOperators(client as never, gateId, cfg.accessGateOriginalId)
|
|
29
|
+
const page = await listSealedContent(client, {
|
|
16
30
|
originalId: cfg.seal.originalId,
|
|
17
31
|
gateId,
|
|
18
32
|
limit: MAX_DISCOVERED,
|
|
19
33
|
indexer: INDEXER_URL ? { url: INDEXER_URL, network: cfg.network } : undefined,
|
|
34
|
+
...(publishers ? { publishers } : {}),
|
|
20
35
|
})
|
|
21
36
|
return page.pointers
|
|
22
37
|
}
|
package/src/walrus.ts
CHANGED
|
@@ -16,12 +16,18 @@ export async function storeBlob(bytes: Uint8Array, opts: { sendObjectTo: string
|
|
|
16
16
|
`set VITE_WALRUS_PUBLISHER_${cfg.network.toUpperCase()} to an operator-run publisher.`,
|
|
17
17
|
)
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
const stored = await storeBlobViaPublisher(bytes, {
|
|
20
20
|
publisher: cfg.walrusPublisher,
|
|
21
21
|
epochs: WALRUS_EPOCHS,
|
|
22
22
|
sendObjectTo: opts.sendObjectTo,
|
|
23
23
|
maxBytes: WALRUS_MAX_UPLOAD_BYTES,
|
|
24
24
|
})
|
|
25
|
+
// Ciphertext carries a random Seal identity, so it cannot already be stored; if a publisher says it
|
|
26
|
+
// is, the user would own no Blob object to extend, so refuse rather than hand back a dead pointer.
|
|
27
|
+
if (stored.kind !== 'newlyCreated') {
|
|
28
|
+
throw new Error('The publisher reported this blob as already stored, so no Blob object was created for you. Try again.')
|
|
29
|
+
}
|
|
30
|
+
return stored.blobId
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
/** Read the ciphertext back, with the aggregator's strict consistency check. */
|