@meddleware/seal-ui 0.0.26 → 0.0.28
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 +19 -0
- package/README.md +10 -3
- package/index.html +1 -1
- package/package.json +3 -3
- package/src/components/SealView.vue +75 -1
- package/src/config.ts +47 -16
- package/src/seal.ts +45 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
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.28] - 2026-10-02
|
|
7
|
+
|
|
8
|
+
- `@meddleware/walrus-client` 0.0.22 (relay challenge requests time out).
|
|
9
|
+
- Ships the brand favicon (`/favicon.svg`); the old `/favicon.ico` link pointed at a missing file.
|
|
10
|
+
|
|
11
|
+
## [0.0.27] - 2026-10-02
|
|
12
|
+
|
|
13
|
+
- **Mainnet key servers** (workspace ADR-0002, D24): three keyless Open-mode servers run by
|
|
14
|
+
independent operators — Overclock, NodeInfra, H2O Nodes — at threshold 2, replacing the planned
|
|
15
|
+
Enoki committee. Mainnet sealing still waits for the mainnet `seal_policies` package.
|
|
16
|
+
- **No API key path.** `VITE_SEAL_AGGREGATOR_API_KEY_{NET}` is gone; a key in a `VITE_*` var would ship
|
|
17
|
+
in the bundle. A test proves no server is ever configured with one.
|
|
18
|
+
- **Custody notice.** `VITE_SEAL_KEY_CUSTODY_{NET}` (`independent` by default, or `operator` for the
|
|
19
|
+
self-hosted fallback) — with `operator`, the view says the site's operator could decrypt.
|
|
20
|
+
- **Re-seal** (Decrypt tab): decrypts content with the key servers it was sealed to and seals it again
|
|
21
|
+
for the current ones, producing a new manifest (`@meddleware/seal-client` 0.0.12).
|
|
22
|
+
- The publish workflow passes the mainnet server ids, threshold and custody variables, so switching
|
|
23
|
+
servers is a repository-variable change.
|
|
24
|
+
|
|
6
25
|
## [0.0.26] - 2026-10-02
|
|
7
26
|
|
|
8
27
|
- Targets the version-gated `seal_policies` (testnet `0x61c4aa…`) through `@meddleware/seal-client`
|
package/README.md
CHANGED
|
@@ -29,8 +29,8 @@ access-gate-client) for the active network; they are not configuration.
|
|
|
29
29
|
| `VITE_NETWORK` | Network the standalone build selects: `testnet` (default) or `mainnet`. Embedded, the host's selector rules. |
|
|
30
30
|
| `VITE_SEAL_SERVER_OBJECT_IDS_{NET}` | CSV of key-server object ids (the committee) |
|
|
31
31
|
| `VITE_SEAL_AGGREGATOR_URLS_{NET}` | CSV of aggregator URLs (index-aligned) |
|
|
32
|
-
| `
|
|
33
|
-
| `VITE_SEAL_THRESHOLD_{NET}` | `t` over the configured servers; a committee behind an aggregator counts as one (default testnet
|
|
32
|
+
| `VITE_SEAL_KEY_CUSTODY_{NET}` | `independent` (default) or `operator`. Set `operator` only when the operator's own key server is in use (the self-hosted fallback); the view then warns that the operator could decrypt |
|
|
33
|
+
| `VITE_SEAL_THRESHOLD_{NET}` | `t` over the configured servers; a committee behind an aggregator counts as one (default 2 on testnet and mainnet). An out-of-range value disables sealing on that network with an explanation |
|
|
34
34
|
| `VITE_WALRUS_PUBLISHER_{NET}` | Walrus HTTP publisher. Testnet has a default; **mainnet has none** (Walrus runs no public mainnet publisher), so set an operator-run publisher |
|
|
35
35
|
| `VITE_WALRUS_AGGREGATOR_{NET}` | Walrus HTTP aggregator (Mysten reference endpoints by default) |
|
|
36
36
|
| `VITE_WALRUS_MAX_UPLOAD_BYTES` | Largest ciphertext sent to the publisher (default 10 MiB, the public-publisher limit) |
|
|
@@ -52,7 +52,14 @@ npm run build # vue-tsc + vite → dist/
|
|
|
52
52
|
docker build -t seal-ui .
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
Key servers (workspace ADR-0002): testnet uses Mysten's committee and two Open-mode servers; mainnet
|
|
56
|
+
uses three keyless Open-mode servers run by independent operators (Overclock, NodeInfra, H2O Nodes)
|
|
57
|
+
at threshold 2. No server needs an API key, and none is ever put in the bundle. Mainnet sealing stays
|
|
58
|
+
disabled with an in-app notice until the mainnet `seal_policies` package is published.
|
|
59
|
+
|
|
60
|
+
**Re-seal.** Content can only be decrypted by the key servers it was sealed to. The Decrypt tab's
|
|
61
|
+
re-seal action decrypts it with those servers and seals it again for the current ones, so content can
|
|
62
|
+
move off a provider that withdraws while the others still answer.
|
|
56
63
|
|
|
57
64
|
## License
|
|
58
65
|
|
package/index.html
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
|
-
<link rel="icon" href="/favicon.
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>Sealed Storage — Meddleware</title>
|
|
8
8
|
</head>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meddleware/seal-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.28",
|
|
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>",
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@meddleware/access-gate-client": "^0.0.2",
|
|
48
48
|
"@meddleware/design-tokens": "^0.1.7",
|
|
49
|
-
"@meddleware/seal-client": "^0.0.
|
|
49
|
+
"@meddleware/seal-client": "^0.0.12",
|
|
50
50
|
"@meddleware/ui": "^0.1.28",
|
|
51
|
-
"@meddleware/walrus-client": "
|
|
51
|
+
"@meddleware/walrus-client": "^0.0.22",
|
|
52
52
|
"@mysten/sui": "^2.33.1",
|
|
53
53
|
"@mysten/wallet-standard": "^0.21.0",
|
|
54
54
|
"vue": "^3.5.43"
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import { checkManifestForNetwork } from '../manifest-guard.js'
|
|
20
20
|
import { WalletGuard } from '@meddleware/wallet-adapter'
|
|
21
21
|
import { useWallet, getSuiClient } from '../wallet.js'
|
|
22
|
-
import { registry, getSealController, clearSealSessions } from '../seal.js'
|
|
22
|
+
import { registry, getSealController, clearSealSessions, resealCiphertext } from '../seal.js'
|
|
23
23
|
import { storeBlob, readBlob } from '../walrus.js'
|
|
24
24
|
import { discoverSealedContent } from '../sealed-content.js'
|
|
25
25
|
import { activeConfig, network } from '../config.js'
|
|
@@ -293,6 +293,67 @@ async function performDecrypt(): Promise<void> {
|
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
// ── Re-seal onto the current key servers ─────────────────────────────────────
|
|
297
|
+
// Content can only be decrypted by the key servers it was sealed to. Re-sealing decrypts it with
|
|
298
|
+
// those servers and encrypts it again for the current ones, so it survives a change of providers
|
|
299
|
+
// (workspace ADR-0002). The result is a new blob and manifest; the old ones keep needing the old
|
|
300
|
+
// servers.
|
|
301
|
+
async function performReseal(): Promise<void> {
|
|
302
|
+
errorMsg.value = null
|
|
303
|
+
status.value = null
|
|
304
|
+
const m = decManifest.value
|
|
305
|
+
const provider = decProvider.value
|
|
306
|
+
if (!m || !provider) {
|
|
307
|
+
errorMsg.value = 'Paste or upload a valid manifest.'
|
|
308
|
+
return
|
|
309
|
+
}
|
|
310
|
+
if (!account.value) {
|
|
311
|
+
errorMsg.value = 'Connect your wallet to re-seal.'
|
|
312
|
+
return
|
|
313
|
+
}
|
|
314
|
+
busy.value = true
|
|
315
|
+
try {
|
|
316
|
+
const encryptParams: Record<string, unknown> = { ...(m.params ?? {}) }
|
|
317
|
+
const decryptParams: Record<string, unknown> = { ...encryptParams }
|
|
318
|
+
for (const f of provider.describe().decryptFields) {
|
|
319
|
+
const v = decValues.value[f.name]
|
|
320
|
+
if (v !== undefined && v !== '') decryptParams[f.name] = coerce(f.kind, v)
|
|
321
|
+
if (f.required && (decryptParams[f.name] === undefined || decryptParams[f.name] === '')) {
|
|
322
|
+
throw new Error(`${f.label} is required.`)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
status.value = 'Fetching ciphertext from Walrus…'
|
|
326
|
+
const ciphertext = await readBlob(m.blobId)
|
|
327
|
+
status.value = 'Requesting keys from the original key servers — approve the signature in your wallet…'
|
|
328
|
+
const resealed = await resealCiphertext(m.policyType, decryptParams, encryptParams, m.id, ciphertext, {
|
|
329
|
+
address: account.value.address,
|
|
330
|
+
signPersonalMessage,
|
|
331
|
+
})
|
|
332
|
+
if (!resealed) {
|
|
333
|
+
status.value = 'This content is already sealed to the current key servers; nothing to do.'
|
|
334
|
+
return
|
|
335
|
+
}
|
|
336
|
+
status.value = 'Storing the re-sealed ciphertext on Walrus…'
|
|
337
|
+
const blobId = await storeBlob(resealed.ciphertext, { sendObjectTo: account.value.address })
|
|
338
|
+
manifest.value = {
|
|
339
|
+
policyType: m.policyType,
|
|
340
|
+
id: resealed.id,
|
|
341
|
+
blobId,
|
|
342
|
+
network: network.value,
|
|
343
|
+
params: encryptParams,
|
|
344
|
+
label: m.label,
|
|
345
|
+
}
|
|
346
|
+
publishDigest.value = null
|
|
347
|
+
status.value = 'Re-sealed. Download the new manifest; the old one still needs the old key servers.'
|
|
348
|
+
tab.value = 'encrypt'
|
|
349
|
+
encStep.value = 2
|
|
350
|
+
} catch (e) {
|
|
351
|
+
errorMsg.value = e instanceof Error ? e.message : String(e)
|
|
352
|
+
} finally {
|
|
353
|
+
busy.value = false
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
296
357
|
// ── Publish an on-chain discovery pointer (nft-gate content only) ─────────────
|
|
297
358
|
const publishDigest = ref<string | null>(null)
|
|
298
359
|
|
|
@@ -394,6 +455,10 @@ async function performUnlock(item: SealedContentPointer): Promise<void> {
|
|
|
394
455
|
<UiToolIntro>Client-side encrypted, access-gated storage on Walrus + Sui.</UiToolIntro>
|
|
395
456
|
|
|
396
457
|
<p v-if="activeConfig.problem" class="notice notice--warn">{{ activeConfig.problem }}</p>
|
|
458
|
+
<p v-else-if="activeConfig.custody === 'operator'" class="notice notice--warn">
|
|
459
|
+
On {{ activeConfig.network }}, content is sealed with a key server run by this site's operator,
|
|
460
|
+
who could therefore decrypt it. Do not seal anything you would not share with the operator.
|
|
461
|
+
</p>
|
|
397
462
|
|
|
398
463
|
<!-- The tab list and every panel always render (each tab controls a live panel); the wallet
|
|
399
464
|
prompt replaces only a panel's content until a wallet is connected. Panels stay mounted
|
|
@@ -626,6 +691,15 @@ async function performUnlock(item: SealedContentPointer): Promise<void> {
|
|
|
626
691
|
{{ busy ? 'Working…' : 'Decrypt' }}
|
|
627
692
|
</button>
|
|
628
693
|
</div>
|
|
694
|
+
<p class="muted muted--spaced">
|
|
695
|
+
Moving to new key servers? Re-sealing decrypts this content with the servers it was sealed to
|
|
696
|
+
and seals it again for the current ones, producing a new manifest.
|
|
697
|
+
</p>
|
|
698
|
+
<div class="nav-row">
|
|
699
|
+
<button type="button" class="link" :disabled="busy || disabled || !decProvider" @click="performReseal">
|
|
700
|
+
Re-seal for the current key servers
|
|
701
|
+
</button>
|
|
702
|
+
</div>
|
|
629
703
|
</template>
|
|
630
704
|
</WalletGuard>
|
|
631
705
|
</UiTabPanel>
|
package/src/config.ts
CHANGED
|
@@ -21,10 +21,11 @@ function csv(v: string | undefined): string[] {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* Key-server defaults (workspace ADR-0002). Testnet: Mysten's decentralized committee (via its
|
|
25
|
+
* aggregator) plus Mysten's two Open-mode servers, threshold 2. Mainnet (D24): three keyless
|
|
26
|
+
* Open-mode servers run by independent operators — Overclock, NodeInfra, H2O Nodes — at threshold 2,
|
|
27
|
+
* so no single operator can decrypt and one may be down. No server needs an API key, so none is ever
|
|
28
|
+
* configured in the bundle. A committee behind an aggregator counts as ONE server.
|
|
28
29
|
*/
|
|
29
30
|
const SERVER_DEFAULTS: Record<string, { ids: string[]; aggs: string[]; threshold: number }> = {
|
|
30
31
|
testnet: {
|
|
@@ -37,9 +38,13 @@ const SERVER_DEFAULTS: Record<string, { ids: string[]; aggs: string[]; threshold
|
|
|
37
38
|
threshold: 2,
|
|
38
39
|
},
|
|
39
40
|
mainnet: {
|
|
40
|
-
ids: [
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
ids: [
|
|
42
|
+
'0x145540d931f182fef76467dd8074c9839aea126852d90d18e1556fcbbd1208b6', // Overclock (Open)
|
|
43
|
+
'0x1afb3a57211ceff8f6781757821847e3ddae73f64e78ec8cd9349914ad985475', // NodeInfra (Open)
|
|
44
|
+
'0x4a65b4ff7ba8f4b538895ee35959f982a95f0db7e2a202ec989d261ea927286a', // H2O Nodes (Open)
|
|
45
|
+
],
|
|
46
|
+
aggs: [],
|
|
47
|
+
threshold: 2,
|
|
43
48
|
},
|
|
44
49
|
}
|
|
45
50
|
|
|
@@ -69,6 +74,14 @@ export function thresholdError(threshold: number, servers: readonly { weight?: n
|
|
|
69
74
|
return null
|
|
70
75
|
}
|
|
71
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Who could decrypt content sealed on a network. `independent` (the default): key servers run by
|
|
79
|
+
* parties other than this app's operator, at a threshold no single one meets. `operator`: the
|
|
80
|
+
* operator's own key server is in use (the ADR-0002 fallback), so the operator could decrypt; the
|
|
81
|
+
* view says so.
|
|
82
|
+
*/
|
|
83
|
+
export type KeyCustody = 'independent' | 'operator'
|
|
84
|
+
|
|
72
85
|
/** Everything sealing needs on one network. */
|
|
73
86
|
export interface SealConfig {
|
|
74
87
|
network: string
|
|
@@ -78,6 +91,7 @@ export interface SealConfig {
|
|
|
78
91
|
accessGateOriginalId: string
|
|
79
92
|
servers: KeyServerConfig[]
|
|
80
93
|
threshold: number
|
|
94
|
+
custody: KeyCustody
|
|
81
95
|
walrusPublisher: string
|
|
82
96
|
walrusAggregator: string
|
|
83
97
|
/** Why sealing is unavailable on this network, or null when it is configured. */
|
|
@@ -96,22 +110,24 @@ export function sealConfig(net: string, envSource: EnvSource = env): SealConfig
|
|
|
96
110
|
const envAggs = csv(netEnv('VITE_SEAL_AGGREGATOR_URLS'))
|
|
97
111
|
const ids = envIds.length ? envIds : (defaults?.ids ?? [])
|
|
98
112
|
const aggs = envAggs.length ? envAggs : (defaults?.aggs ?? [])
|
|
99
|
-
|
|
100
|
-
const servers: KeyServerConfig[] = ids.map((objectId, i) => {
|
|
101
|
-
const aggregatorUrl = aggs[i]
|
|
102
|
-
return aggregatorUrl && apiKey
|
|
103
|
-
? { objectId, weight: 1, aggregatorUrl, apiKeyName: 'X-API-Key', apiKey }
|
|
104
|
-
: { objectId, weight: 1, aggregatorUrl }
|
|
105
|
-
})
|
|
113
|
+
// Never an API key: a key in a VITE_* var ships in the public bundle (seal-ui audit I5).
|
|
114
|
+
const servers: KeyServerConfig[] = ids.map((objectId, i) => ({ objectId, weight: 1, aggregatorUrl: aggs[i] }))
|
|
106
115
|
const rawThreshold = netEnv('VITE_SEAL_THRESHOLD')
|
|
107
116
|
const threshold = rawThreshold ? Number(rawThreshold) : (defaults?.threshold ?? 1)
|
|
108
117
|
|
|
118
|
+
const rawCustody = netEnv('VITE_SEAL_KEY_CUSTODY') ?? 'independent'
|
|
119
|
+
const custody: KeyCustody = rawCustody === 'operator' ? 'operator' : 'independent'
|
|
120
|
+
const custodyError =
|
|
121
|
+
rawCustody === 'independent' || rawCustody === 'operator'
|
|
122
|
+
? null
|
|
123
|
+
: `VITE_SEAL_KEY_CUSTODY_${NET} must be "independent" or "operator"; got "${rawCustody}"`
|
|
124
|
+
|
|
109
125
|
const walrus = WALRUS_DEFAULTS[net]
|
|
110
|
-
const problem = !seal
|
|
126
|
+
const problem = custodyError ?? (!seal
|
|
111
127
|
? `Sealed Storage is not available on ${net}: no seal_policies deployment is recorded for it.`
|
|
112
128
|
: servers.length === 0
|
|
113
129
|
? `Sealed Storage is not configured on ${net}: set VITE_SEAL_SERVER_OBJECT_IDS_${NET} (and VITE_SEAL_AGGREGATOR_URLS_${NET} for a committee).`
|
|
114
|
-
: thresholdError(threshold, servers)
|
|
130
|
+
: thresholdError(threshold, servers))
|
|
115
131
|
|
|
116
132
|
return {
|
|
117
133
|
network: net,
|
|
@@ -119,12 +135,27 @@ export function sealConfig(net: string, envSource: EnvSource = env): SealConfig
|
|
|
119
135
|
accessGateOriginalId: accessGate?.originalId ?? '',
|
|
120
136
|
servers,
|
|
121
137
|
threshold,
|
|
138
|
+
custody,
|
|
122
139
|
walrusPublisher: netEnv('VITE_WALRUS_PUBLISHER') || walrus?.publisher || '',
|
|
123
140
|
walrusAggregator: netEnv('VITE_WALRUS_AGGREGATOR') || walrus?.aggregator || '',
|
|
124
141
|
problem,
|
|
125
142
|
}
|
|
126
143
|
}
|
|
127
144
|
|
|
145
|
+
/**
|
|
146
|
+
* The aggregator URL for a committee server on `net`, from the current configuration or the
|
|
147
|
+
* defaults (independent servers publish their URL on-chain and need none). Used to reach the servers
|
|
148
|
+
* an older ciphertext was sealed to.
|
|
149
|
+
*/
|
|
150
|
+
export function aggregatorUrlFor(net: string, objectId: string, envSource: EnvSource = env): string | undefined {
|
|
151
|
+
const norm = (id: string) => `0x${id.toLowerCase().replace(/^0x/, '').padStart(64, '0')}`
|
|
152
|
+
const fromConfig = sealConfig(net, envSource).servers.find((s) => norm(s.objectId) === norm(objectId))
|
|
153
|
+
if (fromConfig?.aggregatorUrl) return fromConfig.aggregatorUrl
|
|
154
|
+
const d = SERVER_DEFAULTS[net]
|
|
155
|
+
const i = d ? d.ids.findIndex((id) => norm(id) === norm(objectId)) : -1
|
|
156
|
+
return i >= 0 ? d?.aggs[i] : undefined
|
|
157
|
+
}
|
|
158
|
+
|
|
128
159
|
/** The configuration for the active network. */
|
|
129
160
|
export const activeConfig = computed(() => sealConfig(network.value))
|
|
130
161
|
|
package/src/seal.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { computed } from 'vue'
|
|
|
7
7
|
import { createDefaultRegistry, type PolicyRegistry } from '@meddleware/seal-client'
|
|
8
8
|
import type { SealController } from '@meddleware/seal-client/controller'
|
|
9
9
|
import { getSuiClient } from './wallet.js'
|
|
10
|
-
import { activeConfig, type SealConfig } from './config.js'
|
|
10
|
+
import { activeConfig, aggregatorUrlFor, type SealConfig } from './config.js'
|
|
11
11
|
|
|
12
12
|
const registries = new Map<string, PolicyRegistry>()
|
|
13
13
|
const controllers = new Map<string, SealController>()
|
|
@@ -52,6 +52,50 @@ export async function getSealController(): Promise<SealController> {
|
|
|
52
52
|
return c
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/** A ciphertext re-sealed onto the active network's key servers. */
|
|
56
|
+
export interface Resealed {
|
|
57
|
+
id: string
|
|
58
|
+
ciphertext: Uint8Array
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Re-seal `ciphertext` (identity `id`, sealed under policy `policyType`) onto the active network's
|
|
63
|
+
* current key servers: decrypt with a controller for the servers its header records, then encrypt
|
|
64
|
+
* the plaintext under `encryptParams` with the current controller. Returns `null` when it is already
|
|
65
|
+
* sealed to the current servers. The plaintext never leaves this function.
|
|
66
|
+
*
|
|
67
|
+
* @throws {Error} if sealing is unavailable, the ciphertext belongs to another policy package, or the
|
|
68
|
+
* original servers cannot release the key.
|
|
69
|
+
*/
|
|
70
|
+
export async function resealCiphertext(
|
|
71
|
+
policyType: string,
|
|
72
|
+
decryptParams: Record<string, unknown>,
|
|
73
|
+
encryptParams: Record<string, unknown>,
|
|
74
|
+
id: string,
|
|
75
|
+
ciphertext: Uint8Array,
|
|
76
|
+
opts: { address: string; signPersonalMessage: (message: Uint8Array) => Promise<{ signature: string }> },
|
|
77
|
+
): Promise<Resealed | null> {
|
|
78
|
+
const cfg = activeConfig.value
|
|
79
|
+
if (cfg.problem || !cfg.seal) throw new Error(cfg.problem ?? `Sealed Storage is not available on ${cfg.network}.`)
|
|
80
|
+
const { SealController, describeCiphertext, sealedUnderServers } = await import('@meddleware/seal-client/controller')
|
|
81
|
+
const info = describeCiphertext(ciphertext)
|
|
82
|
+
if (sealedUnderServers(info, cfg.servers, cfg.threshold)) return null
|
|
83
|
+
const original = new SealController(
|
|
84
|
+
{
|
|
85
|
+
suiClient: getSuiClient(),
|
|
86
|
+
originalId: cfg.seal.originalId,
|
|
87
|
+
publishedAt: cfg.seal.publishedAt,
|
|
88
|
+
policyConfigId: cfg.seal.policyConfigId,
|
|
89
|
+
threshold: info.threshold,
|
|
90
|
+
serverConfigs: info.servers.map((s) => ({ ...s, aggregatorUrl: aggregatorUrlFor(cfg.network, s.objectId) })),
|
|
91
|
+
},
|
|
92
|
+
registryFor(cfg),
|
|
93
|
+
)
|
|
94
|
+
const plaintext = await original.decrypt(policyType, decryptParams, id, ciphertext, opts)
|
|
95
|
+
original.clearSession()
|
|
96
|
+
return (await getSealController()).encrypt(policyType, encryptParams, plaintext)
|
|
97
|
+
}
|
|
98
|
+
|
|
55
99
|
/**
|
|
56
100
|
* Drop every cached SessionKey (wallet disconnected or switched account). A no-op before a
|
|
57
101
|
* controller has been loaded, so calling it never pulls `@mysten/seal` into the bundle.
|