@mysten-incubation/devstack 0.4.0 → 0.6.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 (61) hide show
  1. package/README.md +9 -10
  2. package/dashboard-ui/assets/{grpc-CpkDu4SA.js → grpc-s7Ztk9wv.js} +1 -1
  3. package/dashboard-ui/assets/{index-jLPRmjst.js → index-DrOd0m4F.js} +2 -2
  4. package/dashboard-ui/index.html +1 -1
  5. package/dist/contracts/routable.d.mts +1 -1
  6. package/dist/index.d.mts +3 -3
  7. package/dist/orchestrators/boot.mjs +2 -1
  8. package/dist/orchestrators/boot.mjs.map +1 -1
  9. package/dist/orchestrators/router/file-provider.mjs +11 -8
  10. package/dist/orchestrators/router/file-provider.mjs.map +1 -1
  11. package/dist/orchestrators/router/service.mjs +2 -2
  12. package/dist/orchestrators/router/service.mjs.map +1 -1
  13. package/dist/plugins/deepbook/index.d.mts +36 -6
  14. package/dist/plugins/deepbook/index.mjs +52 -16
  15. package/dist/plugins/deepbook/index.mjs.map +1 -1
  16. package/dist/plugins/seal/codegen.d.mts +5 -0
  17. package/dist/plugins/seal/codegen.mjs +53 -10
  18. package/dist/plugins/seal/codegen.mjs.map +1 -1
  19. package/dist/plugins/seal/index.d.mts +64 -19
  20. package/dist/plugins/seal/index.mjs +26 -36
  21. package/dist/plugins/seal/index.mjs.map +1 -1
  22. package/dist/plugins/seal/mode/fork-known.d.mts +0 -2
  23. package/dist/plugins/seal/mode/fork-known.mjs +11 -22
  24. package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
  25. package/dist/plugins/seal/mode/live.d.mts +36 -13
  26. package/dist/plugins/seal/mode/live.mjs +104 -43
  27. package/dist/plugins/seal/mode/live.mjs.map +1 -1
  28. package/dist/plugins/seal/mode/local-keygen.mjs +2 -1
  29. package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
  30. package/dist/plugins/seal/registry-publish.d.mts +15 -3
  31. package/dist/plugins/seal/registry-publish.mjs.map +1 -1
  32. package/dist/plugins/sui/index.d.mts +10 -10
  33. package/dist/plugins/walrus/client-services.d.mts +1 -0
  34. package/dist/plugins/walrus/client-services.mjs +115 -0
  35. package/dist/plugins/walrus/client-services.mjs.map +1 -0
  36. package/dist/plugins/walrus/deploy.mjs +11 -5
  37. package/dist/plugins/walrus/deploy.mjs.map +1 -1
  38. package/dist/plugins/walrus/errors.d.mts +1 -1
  39. package/dist/plugins/walrus/errors.mjs.map +1 -1
  40. package/dist/plugins/walrus/index.d.mts +138 -9
  41. package/dist/plugins/walrus/index.mjs +72 -20
  42. package/dist/plugins/walrus/index.mjs.map +1 -1
  43. package/dist/plugins/walrus/mode/known-deploy.d.mts +5 -1
  44. package/dist/plugins/walrus/mode/known-deploy.mjs +14 -11
  45. package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
  46. package/dist/plugins/walrus/mode/local-cluster.d.mts +14 -1
  47. package/dist/plugins/walrus/mode/local-cluster.mjs +54 -12
  48. package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
  49. package/dist/plugins/walrus/routable.mjs +12 -15
  50. package/dist/plugins/walrus/routable.mjs.map +1 -1
  51. package/dist/plugins/walrus/snapshot.mjs +3 -2
  52. package/dist/plugins/walrus/snapshot.mjs.map +1 -1
  53. package/dist/plugins/walrus/storage-nodes.mjs +31 -33
  54. package/dist/plugins/walrus/storage-nodes.mjs.map +1 -1
  55. package/dist/substrate/runtime/config-validation.mjs +1 -1
  56. package/images/walrus/Dockerfile +17 -4
  57. package/images/walrus/deploy-walrus.sh +7 -4
  58. package/images/walrus/run-walrus-client-service.sh +103 -0
  59. package/package.json +2 -2
  60. package/dist/plugins/seal/service.mjs +0 -17
  61. package/dist/plugins/seal/service.mjs.map +0 -1
@@ -21,7 +21,6 @@ import { DEFAULT_SEAL_VERSION } from "./bootstrap-assets/source-fetch.mjs";
21
21
  import { bootLocalKeygen, resolveLocalKeygenOptions } from "./mode/local-keygen.mjs";
22
22
  import { makeSealResource } from "./registry-publish.mjs";
23
23
  import { makeKnownSnapshotable, makeLocalKeygenSnapshotable } from "./snapshot.mjs";
24
- import { bootSealService } from "./service.mjs";
25
24
  import { Effect, FileSystem, Path } from "effect";
26
25
  //#region src/plugins/seal/index.ts
27
26
  /** Constants + shared knobs for the buildXyz helpers below. */
@@ -116,6 +115,7 @@ const buildLocalKeygenPlugin = (opts) => {
116
115
  objectId: resolvedValue.objectId,
117
116
  keyServerUrl: resolvedValue.keyServerUrl,
118
117
  serverConfigs: resolvedValue.serverConfigs,
118
+ verifyKeyServers: resolvedValue.verifyKeyServers,
119
119
  mode: "local-keygen"
120
120
  }));
121
121
  ctx.endpoint(makeSealRoutable({
@@ -130,18 +130,16 @@ const buildLocalKeygenPlugin = (opts) => {
130
130
  const buildLivePlugin = (opts) => {
131
131
  const name = opts.name ?? DEFAULT_NAME;
132
132
  const sealResource = makeSealResource(name);
133
- const validated = validateLiveInputs({
133
+ const resolved = validateLiveInputs({
134
134
  name,
135
135
  ...opts
136
136
  });
137
137
  const bindings = {
138
138
  name,
139
- objectId: validated.objectId,
140
- keyServerUrl: validated.keyServerUrl,
141
- serverConfigs: [{
142
- objectId: validated.objectId,
143
- weight: 1
144
- }],
139
+ objectId: resolved.objectId,
140
+ keyServerUrl: resolved.keyServerUrl,
141
+ serverConfigs: resolved.serverConfigs,
142
+ verifyKeyServers: resolved.verifyKeyServers,
145
143
  mode: "live"
146
144
  };
147
145
  const snap = makeKnownSnapshotable({ name });
@@ -155,21 +153,19 @@ const buildLivePlugin = (opts) => {
155
153
  known: {
156
154
  objectId: bindings.objectId,
157
155
  keyServerUrl: bindings.keyServerUrl,
158
- serverConfigs: bindings.serverConfigs
156
+ serverConfigs: bindings.serverConfigs,
157
+ verifyKeyServers: bindings.verifyKeyServers
159
158
  }
160
159
  })],
161
160
  start: () => Effect.gen(function* () {
162
161
  const ctx = yield* PluginContext;
163
- const mode = {
164
- mode: "live",
165
- name,
166
- resolved: validated
167
- };
168
- const resolved = yield* bootSealService(yield* CacheService, mode);
169
162
  ctx.snapshotExtra(snap);
170
163
  ctx.codegen(makeSealCodegenable(bindings));
171
164
  return {
172
- ...resolved.keyServer,
165
+ serverConfigs: resolved.serverConfigs,
166
+ keyServerUrl: resolved.keyServerUrl,
167
+ objectId: resolved.objectId,
168
+ verifyKeyServers: resolved.verifyKeyServers,
173
169
  mode: "live",
174
170
  manager: null
175
171
  };
@@ -206,35 +202,29 @@ const buildForkKnownPlugin = (opts) => {
206
202
  known: {
207
203
  objectId: validated.objectId,
208
204
  keyServerUrl: validated.keyServerUrl,
209
- serverConfigs: [{
210
- objectId: validated.objectId,
211
- weight: 1
212
- }]
205
+ serverConfigs: validated.serverConfigs,
206
+ verifyKeyServers: validated.verifyKeyServers
213
207
  }
214
208
  })],
215
209
  start: () => Effect.gen(function* () {
216
210
  const ctx = yield* PluginContext;
217
- const mode = {
218
- mode: "fork-known",
219
- name,
220
- upstream: opts.upstream,
221
- resolved: validated
222
- };
223
- const resolved = yield* bootSealService(yield* CacheService, mode);
224
- const resolvedValue = {
225
- ...resolved.keyServer,
226
- mode: "fork-known",
227
- manager: null
228
- };
229
211
  ctx.snapshotExtra(snap);
230
212
  ctx.codegen(makeSealCodegenable({
231
213
  name,
232
- objectId: resolved.keyServer.objectId,
233
- keyServerUrl: resolved.keyServer.keyServerUrl,
234
- serverConfigs: resolved.keyServer.serverConfigs,
214
+ objectId: validated.objectId,
215
+ keyServerUrl: validated.keyServerUrl,
216
+ serverConfigs: validated.serverConfigs,
217
+ verifyKeyServers: validated.verifyKeyServers,
235
218
  mode: "fork-known"
236
219
  }));
237
- return resolvedValue;
220
+ return {
221
+ serverConfigs: validated.serverConfigs,
222
+ keyServerUrl: validated.keyServerUrl,
223
+ objectId: validated.objectId,
224
+ verifyKeyServers: validated.verifyKeyServers,
225
+ mode: "fork-known",
226
+ manager: null
227
+ };
238
228
  })
239
229
  });
240
230
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/seal/index.ts"],"sourcesContent":["// Seal plugin — barrel + factories.\n//\n// Architecture (07-seal.md): Seal is a local or known service plugin.\n// Three operative modes:\n//\n// - `local-keygen` — localnet, owns the master key. Heavy boot.\n// - `live` — testnet / mainnet, read-only handle to a\n// known deployment.\n// - `fork-known` — `*-fork` networks; routes to the wrapped\n// upstream's known deployment.\n//\n// Plus one type-level refusal:\n//\n// - `fork-localkeygen-refused` — local-keygen ON `*-fork` is a\n// synchronous throw at factory time (distilled-doc invariant #8).\n//\n// Public surface:\n//\n// - `seal(opts)` — explicit mode selection.\n// - `sealFor(network).<mode>` — mode-narrowed factory namespace.\n// Mode-narrowing makes `sealFor(forkNetwork).localKeygen(...)`\n// a COMPILE-time refusal (architecture Tension 11 + type-prototype\n// finding #4).\n//\n// During `start`, the plugin emits (via the typed `ctx.*` verbs):\n//\n// 1. `ctx.snapshotExtra` — local-keygen contributes secret material\n// subtree; known modes contribute the\n// empty shape.\n// 2. `ctx.codegen` — `seal-key-server` bindings (server\n// configs + URL + objectId).\n// 3. `ctx.endpoint` — `seal-key-server` endpoint, local-keygen\n// only (known modes route to a remote URL\n// outside Traefik's purview).\n\nimport { Effect, FileSystem, Path } from 'effect';\n\nimport { definePlugin, type ResourceRef } from '../../api/define-plugin.ts';\nimport { defineModeNamespace } from '../../api/mode-narrowed-factory.ts';\nimport { ContainerRuntimeService } from '../../runtime/docker/service.ts';\nimport { IdentityContext, StackPathsService } from '../../substrate/runtime/paths.ts';\nimport { PluginContext } from '../../substrate/plugin-ctx.ts';\nimport { CacheService } from '../../substrate/runtime/cache/index.ts';\nimport { chainProbeFor } from '../../substrate/runtime/strategy-registry/index.ts';\nimport type { AccountResourceId, AccountValue } from '../account/index.ts';\nimport { suiResource } from '../sui/index.ts';\n\nimport type { SealObjectProbeKey } from './deploy.ts';\nimport { sealPluginKey } from './plugin-key.ts';\nimport { makeSealCodegenable, makeSealStaticCodegen, type SealBindings } from './codegen.ts';\nimport { sealError, type SealError } from './errors.ts';\nimport { validateForkKnownInputs, type ForkUpstream } from './mode/fork-known.ts';\nimport type { KnownNetwork } from './mode/live.ts';\nimport { validateLiveInputs } from './mode/live.ts';\nimport {\n\tbuildSealNetworkName,\n\tDEFAULT_KEY_SERVER_PORT,\n\tderiveSealSubnetPrefix,\n} from './key-server.ts';\nimport { withSubnetAddressing } from '../../substrate/runtime/subnet-broker.ts';\nimport {\n\tbootLocalKeygen,\n\tresolveLocalKeygenOptions,\n\ttype LocalKeygenDeps,\n} from './mode/local-keygen.ts';\nimport {\n\tmakeSealResource,\n\ttype SealLocalKeygenResolved,\n\ttype SealKnownResolved,\n\ttype SealResolved,\n} from './registry-publish.ts';\nimport { buildSealKeyServerPublicRoute, makeSealRoutable } from './routable.ts';\nimport { makeKnownSnapshotable, makeLocalKeygenSnapshotable } from './snapshot.ts';\nimport { bootSealService, type SealMode } from './service.ts';\nimport { DEFAULT_SEAL_VERSION } from './bootstrap-assets/source-fetch.ts';\n\n// ---------------------------------------------------------------------------\n// Resource exports — distilled-doc §\"TypeScript exports consumed elsewhere\"\n// ---------------------------------------------------------------------------\n\nexport {\n\tmakeSealResource,\n\tsealResourceId,\n\ttype SealKeyServer,\n\ttype SealKeyServerEntry,\n\ttype SealResolved,\n\ttype SealLocalKeygenResolved,\n\ttype SealKnownResolved,\n\ttype SealResourceId,\n} from './registry-publish.ts';\nexport type { SealKeyManager } from './key-manager.ts';\nexport { type SealError, type SealAnyError, type SealConfigError } from './errors.ts';\nexport type { SealBindings } from './codegen.ts';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/** Common options shared across all three modes. */\nexport interface SealCommonOptions {\n\treadonly name?: string;\n}\n\n/** A user-supplied signer account ref. The user passes the result of\n * `account('publisher')` — NOT a magic-string token. Generic over the\n * literal account name so the seal dependency edge preserves the\n * per-account resource id. */\nexport type SealSignerMember<Name extends string = string> = ResourceRef<\n\tAccountResourceId<Name>,\n\tAccountValue\n>;\n\n/** Local-keygen mode options. The `signer` field is REQUIRED — the\n * Move publish + on-chain register both need it. The mode-narrowed\n * factory's TypeScript shape enforces this (no `signer?` here). */\nexport interface SealLocalKeygenOptions<\n\tSigner extends SealSignerMember = SealSignerMember,\n> extends SealCommonOptions {\n\t/** Signer for the seal Move publish + on-chain key-server register.\n\t * Pass the result of `account('publisher')` — the same plugin/resource\n\t * ref used elsewhere in the stack. The ref is threaded through\n\t * `dependsOn` so the publish tx waits for the account's acquire\n\t * (keypair mint + funding) to complete. */\n\treadonly signer: Signer;\n\treadonly version?: string;\n\treadonly movePackagePath?: string;\n\treadonly readyTimeoutMs?: number;\n\treadonly keyServerName?: string;\n\treadonly image?: { readonly pull: string } | { readonly build: { readonly context: string } };\n}\n\n/** Live-mode options (testnet / mainnet). */\nexport interface SealLiveOptions extends SealCommonOptions {\n\treadonly network?: KnownNetwork;\n\treadonly objectId?: string;\n\treadonly keyServerUrl?: string;\n}\n\n/** Fork-known-mode options. */\nexport interface SealForkKnownOptions extends SealCommonOptions {\n\treadonly upstream: ForkUpstream;\n\treadonly objectId?: string;\n\treadonly keyServerUrl?: string;\n}\n\nexport type SealOptions<Signer extends SealSignerMember = SealSignerMember> =\n\t| ({ readonly mode: 'local-keygen' } & SealLocalKeygenOptions<Signer>)\n\t| ({ readonly mode: 'live' } & SealLiveOptions)\n\t| ({ readonly mode: 'fork-known' } & SealForkKnownOptions);\n\n// ---------------------------------------------------------------------------\n// Plugin construction\n// ---------------------------------------------------------------------------\n\n/** Constants + shared knobs for the buildXyz helpers below. */\nconst DEFAULT_NAME = 'seal';\n\n// ---------------------------------------------------------------------------\n// The three seal `start` bodies emit their contributions inline via the typed\n// `ctx` verbs after resolving the value. The decl shapes + emit ORDER are\n// load-bearing.\n//\n// ⚠ ID-STABILITY: the snapshotable decl captures the seal vault / master-key\n// secret material subtree; its shape (subtree paths, container label tuple,\n// secretMaterial / missingTolerance flags) MUST stay byte-identical.\n// ---------------------------------------------------------------------------\n\n/** Build the local-keygen-mode plugin. The service contributes\n * Snapshotable (secret) + Codegenable + Routable.\n *\n * Architecture mirror (walrus): `ContainerRuntimeService` +\n * `IdentityContext` wired via the supervisor's plugin runtime context,\n * artifact publisher publisher + chain probe yielded inside `acquire`. */\nconst buildLocalKeygenPlugin = <const Signer extends SealSignerMember>(\n\topts: SealLocalKeygenOptions<Signer>,\n) => {\n\t// Synchronous factory-time defaults. Localnet-signer-required is\n\t// enforced by the typed `signer:` field on\n\t// SealLocalKeygenOptions.\n\tconst resolved = resolveLocalKeygenOptions(opts, DEFAULT_SEAL_VERSION);\n\n\tconst sealResource = makeSealResource(resolved.name);\n\n\treturn definePlugin({\n\t\tid: sealResource.id,\n\t\tdependsOn: { sui: suiResource, signer: opts.signer },\n\t\trole: 'service',\n\t\tsection: 'service',\n\t\tpluginKey: sealPluginKey(resolved.name),\n\t\t// Stack-free codegen: the key-server object id / URL / committee are\n\t\t// LOADED CONFIG DATA -- the committed `seal.ts` stub emits\n\t\t// `requireValue(dep, 'seal:<name>', '<key>')`, never a baked object id.\n\t\tstaticCodegen: () => [makeSealStaticCodegen({ name: resolved.name, mode: 'local-keygen' })],\n\t\t// `deps` auto-infers the resolved `{ sui, signer }` dependency\n\t\t// object from seal's `dependsOn: { sui: suiResource, signer:\n\t\t// opts.signer }`. `ctx` is the typed plugin-authoring surface the\n\t\t// contribution emission below drives; it arrives via the\n\t\t// `PluginContext` service.\n\t\tstart: (deps) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst { sui, signer: signerAccount } = deps;\n\t\t\t\t// Substrate-context primitives:\n\t\t\t\t// - `ContainerRuntimeService` + `IdentityContext` arrive\n\t\t\t\t// via the supervisor's plugin runtime context.\n\t\t\t\t// - `ArtifactPublisher` is the substrate-level\n\t\t\t\t// publisher (cache → verify → produce → register cycle).\n\t\t\t\t// - Chain probe is looked up via the\n\t\t\t\t// StrategyRegistry under `chain-probe:<chainId>`; the\n\t\t\t\t// Sui plugin registered it during its own acquire.\n\t\t\t\t// The probe is yielded eagerly so the dep edge fails\n\t\t\t\t// fast if sui's chain-probe registration is missing,\n\t\t\t\t// matching walrus's pattern.\n\t\t\t\t// - `StackPathsService` resolves the per-stack on-disk\n\t\t\t\t// root so the key-server config + master-key.env\n\t\t\t\t// bind-mount sources are real paths (not the\n\t\t\t\t// `<runtime>/...` template).\n\t\t\t\tconst runtime = yield* ContainerRuntimeService;\n\t\t\t\tconst identity = yield* IdentityContext;\n\t\t\t\tconst publisher = yield* CacheService;\n\t\t\t\tconst stackPaths = yield* StackPathsService;\n\t\t\t\tconst fs = yield* FileSystem.FileSystem;\n\t\t\t\tconst path = yield* Path.Path;\n\t\t\t\tconst probe = yield* chainProbeFor<SealObjectProbeKey>(sui.chainId);\n\n\t\t\t\t// Resolve the seal service dir from the per-stack paths\n\t\t\t\t// bundle. The dir must exist before the key-server's\n\t\t\t\t// bind-mounts (config yaml + master-key env-file).\n\t\t\t\tconst servicePath = path.join(stackPaths.stackRoot, 'seal', resolved.name);\n\t\t\t\tyield* fs.makeDirectory(servicePath, { recursive: true }).pipe(\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\tsealError('config-render', {\n\t\t\t\t\t\t\t\tname: resolved.name,\n\t\t\t\t\t\t\t\tmessage: `seal.config-render: failed to create service directory at ${servicePath} — downstream config + master-key writes would all fail; surfacing the underlying filesystem error.`,\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\t// Cross-container DNS: seal's key-server dials sui RPC via\n\t\t\t\t// `host.docker.internal`. The sui plugin binds a brokered\n\t\t\t\t// host port; the SDK publish/register path runs from the host\n\t\t\t\t// process and does not need the container network.\n\t\t\t\t//\n\t\t\t\t// Architectural decision (B5): seal owns its OWN docker\n\t\t\t\t// network for the key-server's network attachment;\n\t\t\t\t// sui-side hops go through the host gateway. Mirrors\n\t\t\t\t// walrus's pattern (see plugins/walrus/index.ts §B5).\n\t\t\t\tconst sealNetworkName = buildSealNetworkName(identity.app, identity.stack, resolved.name);\n\t\t\t\tconst suiRpcUrlInNetwork = sui.hostGateway.rpcUrl;\n\t\t\t\tconst routed = buildSealKeyServerPublicRoute({\n\t\t\t\t\tapp: identity.app,\n\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\tport: DEFAULT_KEY_SERVER_PORT,\n\t\t\t\t});\n\n\t\t\t\t// Idempotent network create — the long-running key-server\n\t\t\t\t// attaches to this name. The seal boot pipeline doesn't own\n\t\t\t\t// the create (unlike walrus's local-cluster which calls\n\t\t\t\t// `ensureNetwork` from its mode body); we call it here\n\t\t\t\t// because the substrate's `acquire` is the only seam that\n\t\t\t\t// holds both the runtime + the identity needed for the\n\t\t\t\t// per-stack label tuple.\n\t\t\t\tconst sealSubnetPrefix = deriveSealSubnetPrefix({\n\t\t\t\t\tapp: identity.app,\n\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\tsealName: resolved.name,\n\t\t\t\t});\n\t\t\t\tyield* runtime.ensureNetwork(\n\t\t\t\t\twithSubnetAddressing(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname: sealNetworkName,\n\t\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsealSubnetPrefix,\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\tconst localKeygenDeps: LocalKeygenDeps = {\n\t\t\t\t\truntime,\n\t\t\t\t\tpublisher,\n\t\t\t\t\tsigner: signerAccount,\n\t\t\t\t\tsdk: sui.sdk,\n\t\t\t\t\t...(sui.buildImage !== null ? { buildImage: sui.buildImage } : {}),\n\t\t\t\t\tchainProbe: probe,\n\t\t\t\t\tchainId: sui.chainId,\n\t\t\t\t\tservicePath,\n\t\t\t\t\tcontainerName: `devstack-${identity.app}-${identity.stack}-seal-${resolved.name}-key-server`,\n\t\t\t\t\tlabels: {\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\tplugin: 'seal',\n\t\t\t\t\t\trole: 'key-server',\n\t\t\t\t\t},\n\t\t\t\t\tsuiNetworkName: sealNetworkName,\n\t\t\t\t\tsuiRpcUrlInNetwork,\n\t\t\t\t\troutedHostname: routed.hostname,\n\t\t\t\t\troutedUrl: routed.url,\n\t\t\t\t};\n\n\t\t\t\tconst boot = (yield* bootLocalKeygen(\n\t\t\t\t\tlocalKeygenDeps,\n\t\t\t\t\tresolved,\n\t\t\t\t)) satisfies SealLocalKeygenResolved;\n\n\t\t\t\tconst resolvedValue: SealResolved = {\n\t\t\t\t\t...boot.keyServer,\n\t\t\t\t\tmode: 'local-keygen',\n\t\t\t\t\tmanager: boot.keyManager,\n\t\t\t\t};\n\t\t\t\t// Emit the resolved contributions inline: snapshot (seal vault /\n\t\t\t\t// master-key secret-material subtree) → codegen → routable (key\n\t\t\t\t// server). `resolvedValue` is the just-resolved `SealResolved`;\n\t\t\t\t// `identity` (from `IdentityContext`) stamps the snapshot +\n\t\t\t\t// routable container name. The codegen bindings carry the REAL\n\t\t\t\t// objectId / keyServerUrl / serverConfigs.\n\t\t\t\tctx.snapshotExtra(\n\t\t\t\t\tmakeLocalKeygenSnapshotable({\n\t\t\t\t\t\tname: resolved.name,\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tctx.codegen(\n\t\t\t\t\tmakeSealCodegenable({\n\t\t\t\t\t\tname: resolved.name,\n\t\t\t\t\t\tobjectId: resolvedValue.objectId,\n\t\t\t\t\t\tkeyServerUrl: resolvedValue.keyServerUrl,\n\t\t\t\t\t\tserverConfigs: resolvedValue.serverConfigs,\n\t\t\t\t\t\tmode: 'local-keygen',\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tctx.endpoint(\n\t\t\t\t\tmakeSealRoutable({\n\t\t\t\t\t\tname: resolved.name,\n\t\t\t\t\t\tcontainerName: `devstack-${identity.app}-${identity.stack}-seal-${resolved.name}-key-server`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn resolvedValue;\n\t\t\t}),\n\t});\n};\n\n/** Build the live-mode plugin. No Routable (URL is remote). */\nconst buildLivePlugin = (opts: SealLiveOptions) => {\n\tconst name = opts.name ?? DEFAULT_NAME;\n\tconst sealResource = makeSealResource(name);\n\t// Validate inputs at factory time so misconfigurations fail\n\t// before any plugin row starts work.\n\tconst validated = validateLiveInputs({ name, ...opts });\n\tconst bindings: SealBindings = {\n\t\tname,\n\t\tobjectId: validated.objectId,\n\t\tkeyServerUrl: validated.keyServerUrl,\n\t\tserverConfigs: [{ objectId: validated.objectId, weight: 1 }],\n\t\tmode: 'live',\n\t};\n\tconst snap = makeKnownSnapshotable({ name });\n\n\treturn definePlugin({\n\t\tid: sealResource.id,\n\t\trole: 'task',\n\t\tsection: 'service',\n\t\t// Live mode resolves its `{objectId, keyServerUrl, serverConfigs}` tuple\n\t\t// at factory time (DECLARED config) — bake them as literals in the\n\t\t// committed `seal.ts` (mirrors `knownPackage`).\n\t\tstaticCodegen: () => [\n\t\t\tmakeSealStaticCodegen({\n\t\t\t\tname,\n\t\t\t\tmode: 'live',\n\t\t\t\tknown: {\n\t\t\t\t\tobjectId: bindings.objectId,\n\t\t\t\t\tkeyServerUrl: bindings.keyServerUrl,\n\t\t\t\t\tserverConfigs: bindings.serverConfigs,\n\t\t\t\t},\n\t\t\t}),\n\t\t],\n\t\t// Live mode has no `dependsOn`, so `start` is zero-arg. `ctx`\n\t\t// drives the contribution emission below; it arrives via the\n\t\t// `PluginContext` service.\n\t\tstart: () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst mode: SealMode = { mode: 'live', name, resolved: validated };\n\t\t\t\tconst publisher = yield* CacheService;\n\t\t\t\tconst resolved = (yield* bootSealService(publisher, mode)) as SealKnownResolved;\n\t\t\t\t// Emit inline: snapshot → codegen. `snap` + `bindings` are\n\t\t\t\t// resolved at FACTORY time for live mode (validated\n\t\t\t\t// `{objectId, keyServerUrl}`), so the emit reuses the same\n\t\t\t\t// precomputed values.\n\t\t\t\tctx.snapshotExtra(snap);\n\t\t\t\tctx.codegen(makeSealCodegenable(bindings));\n\t\t\t\treturn {\n\t\t\t\t\t...resolved.keyServer,\n\t\t\t\t\tmode: 'live',\n\t\t\t\t\tmanager: null,\n\t\t\t\t} satisfies SealResolved;\n\t\t\t}),\n\t});\n};\n\n/** Build the fork-known-mode plugin. Structurally symmetric with\n * `buildLivePlugin` — both resolve their `{objectId, keyServerUrl}`\n * tuple at factory time via `validateLiveInputs` (the fork-known\n * side maps `upstream → KnownNetwork` first via\n * `validateForkKnownInputs`) and thread it through SealMode's\n * `resolved:` envelope.\n *\n * Capabilities are kept on the dynamic shape (callback form)\n * rather than precomputed — leaves room for a future on-acquire\n * override path (e.g. the substrate dynamically rewrites the\n * bindings) without restructuring the factory. Not load-bearing\n * today; static capabilities would also work. */\nconst buildForkKnownPlugin = (opts: SealForkKnownOptions) => {\n\tconst name = opts.name ?? DEFAULT_NAME;\n\tconst sealResource = makeSealResource(name);\n\tconst snap = makeKnownSnapshotable({ name });\n\t// Validate inputs at factory time so misconfigurations fail\n\t// before any plugin row starts work — symmetric with\n\t// `buildLivePlugin`'s factory-boundary `validateLiveInputs` call.\n\t// `validateForkKnownInputs` maps the upstream alias to a\n\t// `KnownNetwork` and runs the same validation pipeline as live mode.\n\tconst validated = validateForkKnownInputs({ name, ...opts });\n\n\treturn definePlugin({\n\t\tid: sealResource.id,\n\t\trole: 'task',\n\t\tsection: 'service',\n\t\t// Fork-known mode resolves its `{objectId, keyServerUrl}` tuple at\n\t\t// factory time (DECLARED config, via `validateForkKnownInputs`) — bake\n\t\t// them as literals in the committed `seal.ts` (mirrors `knownPackage`).\n\t\t// The committee `serverConfigs` is the single declared key-server (the\n\t\t// same shape `acquireLive` derives), so it is declared config too.\n\t\tstaticCodegen: () => [\n\t\t\tmakeSealStaticCodegen({\n\t\t\t\tname,\n\t\t\t\tmode: 'fork-known',\n\t\t\t\tknown: {\n\t\t\t\t\tobjectId: validated.objectId,\n\t\t\t\t\tkeyServerUrl: validated.keyServerUrl,\n\t\t\t\t\tserverConfigs: [{ objectId: validated.objectId, weight: 1 }],\n\t\t\t\t},\n\t\t\t}),\n\t\t],\n\t\t// Fork-known mode has no `dependsOn`, so `start` is zero-arg.\n\t\t// `ctx` drives the contribution emission below; it arrives via the\n\t\t// `PluginContext` service.\n\t\tstart: () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\t// Symmetric with the live branch's `{ mode, name, resolved }`\n\t\t\t\t// envelope. `upstream` rides through for downstream\n\t\t\t\t// span attribution; `resolved` carries the validated\n\t\t\t\t// `{objectId, keyServerUrl}` tuple.\n\t\t\t\tconst mode: SealMode = {\n\t\t\t\t\tmode: 'fork-known',\n\t\t\t\t\tname,\n\t\t\t\t\tupstream: opts.upstream,\n\t\t\t\t\tresolved: validated,\n\t\t\t\t};\n\t\t\t\tconst publisher = yield* CacheService;\n\t\t\t\tconst resolved = (yield* bootSealService(publisher, mode)) as SealKnownResolved;\n\t\t\t\tconst resolvedValue: SealResolved = {\n\t\t\t\t\t...resolved.keyServer,\n\t\t\t\t\tmode: 'fork-known',\n\t\t\t\t\tmanager: null,\n\t\t\t\t};\n\t\t\t\t// Emit inline: snapshot → codegen. `bindings` is built from the\n\t\t\t\t// post-acquire `resolved.keyServer` (`{objectId, keyServerUrl,\n\t\t\t\t// serverConfigs}`); `snap` is factory-scoped.\n\t\t\t\tctx.snapshotExtra(snap);\n\t\t\t\tctx.codegen(\n\t\t\t\t\tmakeSealCodegenable({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tobjectId: resolved.keyServer.objectId,\n\t\t\t\t\t\tkeyServerUrl: resolved.keyServer.keyServerUrl,\n\t\t\t\t\t\tserverConfigs: resolved.keyServer.serverConfigs,\n\t\t\t\t\t\tmode: 'fork-known',\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn resolvedValue;\n\t\t\t}),\n\t});\n};\n\n// ---------------------------------------------------------------------------\n// User-facing factories\n// ---------------------------------------------------------------------------\n\n/** Explicit Seal factory. `local-keygen` requires a signer; live and\n * fork-known modes must be selected directly or through `sealFor`. */\nexport const seal = <const Signer extends SealSignerMember = SealSignerMember>(\n\topts: SealOptions<Signer>,\n) => {\n\tswitch (opts.mode) {\n\t\tcase 'local-keygen':\n\t\t\treturn buildLocalKeygenPlugin(opts);\n\t\tcase 'live':\n\t\t\treturn buildLivePlugin(opts);\n\t\tcase 'fork-known':\n\t\t\treturn buildForkKnownPlugin(opts);\n\t}\n};\n\n/** Mode-narrowed factory namespace.\n *\n * Usage:\n * const local = { mode: 'local' } as const;\n * sealFor(local).localKeygen({signer}) // OK\n * sealFor(local).forkKnown(...) // type error: not in 'local' branch\n *\n * const fork = { mode: 'fork' } as const;\n * sealFor(fork).forkKnown({upstream}) // OK\n * sealFor(fork).localKeygen({signer}) // type error: not in 'fork' branch\n * // (distilled-doc invariant #8)\n *\n * Distilled-doc invariant #8 (fork-localkeygen-refused): the\n * fork-mode branch has NO `localKeygen` entry — that's the\n * type-level refusal. */\nexport const sealFor = defineModeNamespace({\n\tlocal: {\n\t\tlocalKeygen: <const Signer extends SealSignerMember>(opts: SealLocalKeygenOptions<Signer>) =>\n\t\t\tbuildLocalKeygenPlugin(opts),\n\t},\n\tlive: {\n\t\ttestnet: (opts: Omit<SealLiveOptions, 'network'> = {}) =>\n\t\t\tbuildLivePlugin({ network: 'testnet', ...opts }),\n\t\tmainnet: (opts: Omit<SealLiveOptions, 'network'> = {}) =>\n\t\t\tbuildLivePlugin({ network: 'mainnet', ...opts }),\n\t\tcustom: (\n\t\t\topts: Required<Pick<SealLiveOptions, 'objectId' | 'keyServerUrl'>> & SealLiveOptions,\n\t\t) => buildLivePlugin(opts),\n\t},\n\tfork: {\n\t\tforkKnown: (opts: SealForkKnownOptions) => buildForkKnownPlugin(opts),\n\t},\n});\n\n// ---------------------------------------------------------------------------\n// Type-only error re-export\n// ---------------------------------------------------------------------------\n\nexport type { SealError as SealAcquireError };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/seal/index.ts"],"sourcesContent":["// Seal plugin — barrel + factories.\n//\n// Architecture (07-seal.md): Seal is a local or known service plugin.\n// Three operative modes:\n//\n// - `local-keygen` — localnet, owns the master key. Heavy boot.\n// - `live` — testnet / mainnet, read-only handle to a\n// known deployment.\n// - `fork-known` — `*-fork` networks; routes to the wrapped\n// upstream's known deployment.\n//\n// Plus one type-level refusal:\n//\n// - `fork-localkeygen-refused` — local-keygen ON `*-fork` is a\n// synchronous throw at factory time (distilled-doc invariant #8).\n//\n// Public surface:\n//\n// - `seal(opts)` — explicit mode selection.\n// - `sealFor(network).<mode>` — mode-narrowed factory namespace.\n// Mode-narrowing makes `sealFor(forkNetwork).localKeygen(...)`\n// a COMPILE-time refusal (architecture Tension 11 + type-prototype\n// finding #4).\n//\n// During `start`, the plugin emits (via the typed `ctx.*` verbs):\n//\n// 1. `ctx.snapshotExtra` — local-keygen contributes secret material\n// subtree; known modes contribute the\n// empty shape.\n// 2. `ctx.codegen` — `seal-key-server` bindings (server\n// configs + URL + objectId).\n// 3. `ctx.endpoint` — `seal-key-server` endpoint, local-keygen\n// only (known modes route to a remote URL\n// outside Traefik's purview).\n\nimport { Effect, FileSystem, Path } from 'effect';\n\nimport { definePlugin, type ResourceRef } from '../../api/define-plugin.ts';\nimport { defineModeNamespace } from '../../api/mode-narrowed-factory.ts';\nimport { ContainerRuntimeService } from '../../runtime/docker/service.ts';\nimport { IdentityContext, StackPathsService } from '../../substrate/runtime/paths.ts';\nimport { PluginContext } from '../../substrate/plugin-ctx.ts';\nimport { CacheService } from '../../substrate/runtime/cache/index.ts';\nimport { chainProbeFor } from '../../substrate/runtime/strategy-registry/index.ts';\nimport type { AccountResourceId, AccountValue } from '../account/index.ts';\nimport { suiResource } from '../sui/index.ts';\n\nimport type { SealObjectProbeKey } from './deploy.ts';\nimport { sealPluginKey } from './plugin-key.ts';\nimport { makeSealCodegenable, makeSealStaticCodegen, type SealBindings } from './codegen.ts';\nimport { sealError, type SealError } from './errors.ts';\nimport { validateForkKnownInputs, type ForkUpstream } from './mode/fork-known.ts';\nimport type { KnownNetwork, ResolvedLiveInputs, SealServerKind } from './mode/live.ts';\nimport { validateLiveInputs } from './mode/live.ts';\nimport {\n\tbuildSealNetworkName,\n\tDEFAULT_KEY_SERVER_PORT,\n\tderiveSealSubnetPrefix,\n} from './key-server.ts';\nimport { withSubnetAddressing } from '../../substrate/runtime/subnet-broker.ts';\nimport {\n\tbootLocalKeygen,\n\tresolveLocalKeygenOptions,\n\ttype LocalKeygenDeps,\n} from './mode/local-keygen.ts';\nimport {\n\tmakeSealResource,\n\ttype SealKeyServerEntry,\n\ttype SealLocalKeygenResolved,\n\ttype SealResolved,\n} from './registry-publish.ts';\nimport { buildSealKeyServerPublicRoute, makeSealRoutable } from './routable.ts';\nimport { makeKnownSnapshotable, makeLocalKeygenSnapshotable } from './snapshot.ts';\nimport { DEFAULT_SEAL_VERSION } from './bootstrap-assets/source-fetch.ts';\n\n// ---------------------------------------------------------------------------\n// Resource exports — distilled-doc §\"TypeScript exports consumed elsewhere\"\n// ---------------------------------------------------------------------------\n\nexport {\n\tmakeSealResource,\n\tsealResourceId,\n\ttype SealKeyServer,\n\ttype SealKeyServerEntry,\n\ttype SealResolved,\n\ttype SealLocalKeygenResolved,\n\ttype SealKnownResolved,\n\ttype SealResourceId,\n} from './registry-publish.ts';\nexport type { SealKeyManager } from './key-manager.ts';\nexport { type SealError, type SealAnyError, type SealConfigError } from './errors.ts';\nexport type { SealBindings } from './codegen.ts';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/** Common options shared across all three modes. */\nexport interface SealCommonOptions {\n\treadonly name?: string;\n}\n\n/** A user-supplied signer account ref. The user passes the result of\n * `account('publisher')` — NOT a magic-string token. Generic over the\n * literal account name so the seal dependency edge preserves the\n * per-account resource id. */\nexport type SealSignerMember<Name extends string = string> = ResourceRef<\n\tAccountResourceId<Name>,\n\tAccountValue\n>;\n\n/** Local-keygen mode options. The `signer` field is REQUIRED — the\n * Move publish + on-chain register both need it. The mode-narrowed\n * factory's TypeScript shape enforces this (no `signer?` here). */\nexport interface SealLocalKeygenOptions<\n\tSigner extends SealSignerMember = SealSignerMember,\n> extends SealCommonOptions {\n\t/** Signer for the seal Move publish + on-chain key-server register.\n\t * Pass the result of `account('publisher')` — the same plugin/resource\n\t * ref used elsewhere in the stack. The ref is threaded through\n\t * `dependsOn` so the publish tx waits for the account's acquire\n\t * (keypair mint + funding) to complete. */\n\treadonly signer: Signer;\n\treadonly version?: string;\n\treadonly movePackagePath?: string;\n\treadonly readyTimeoutMs?: number;\n\treadonly keyServerName?: string;\n\treadonly image?: { readonly pull: string } | { readonly build: { readonly context: string } };\n}\n\n/** Live-mode options (testnet / mainnet).\n *\n * Zero-config `testnet()` resolves BOTH independent servers;\n * `mainnet()` resolves the committee (which requires credentials — declare\n * the non-secret `apiKeyName`). `{ server: 'committee' }` opts a testnet\n * stack into the committee aggregator. `serverConfigs` is the raw verbatim\n * override. `verifyKeyServers` defaults to the SDK default (true) on live /\n * known — omit it; only local-keygen forces it false. This default also\n * applies to a verbatim `serverConfigs` override: for a private / unregistered\n * key server (whose on-chain registration can't be verified), pass\n * `verifyKeyServers: false` explicitly.\n *\n * NOTE: devstack never carries the secret committee `apiKey` value (committed\n * config + the browser-injected `deployment.json` are world-readable). Pass\n * `apiKeyName` (the header name) and inject the apiKey at runtime when you\n * construct `SealClient`. */\nexport interface SealLiveOptions extends SealCommonOptions {\n\treadonly network?: KnownNetwork;\n\treadonly server?: SealServerKind;\n\treadonly apiKeyName?: string;\n\treadonly verifyKeyServers?: boolean;\n\treadonly serverConfigs?: ReadonlyArray<SealKeyServerEntry>;\n}\n\n/** Fork-known-mode options. The `*-fork` network routes to the wrapped\n * upstream's known deployment; `server` / `apiKeyName` mirror the live\n * selectors (the secret apiKey is injected by the app at runtime, never\n * carried by devstack). */\nexport interface SealForkKnownOptions extends SealCommonOptions {\n\treadonly upstream: ForkUpstream;\n\treadonly server?: SealServerKind;\n\treadonly apiKeyName?: string;\n\treadonly verifyKeyServers?: boolean;\n}\n\nexport type SealOptions<Signer extends SealSignerMember = SealSignerMember> =\n\t| ({ readonly mode: 'local-keygen' } & SealLocalKeygenOptions<Signer>)\n\t| ({ readonly mode: 'live' } & SealLiveOptions)\n\t| ({ readonly mode: 'fork-known' } & SealForkKnownOptions);\n\n// ---------------------------------------------------------------------------\n// Plugin construction\n// ---------------------------------------------------------------------------\n\n/** Constants + shared knobs for the buildXyz helpers below. */\nconst DEFAULT_NAME = 'seal';\n\n// ---------------------------------------------------------------------------\n// The three seal `start` bodies emit their contributions inline via the typed\n// `ctx` verbs after resolving the value. The decl shapes + emit ORDER are\n// load-bearing.\n//\n// ⚠ ID-STABILITY: the snapshotable decl captures the seal vault / master-key\n// secret material subtree; its shape (subtree paths, container label tuple,\n// secretMaterial / missingTolerance flags) MUST stay byte-identical.\n// ---------------------------------------------------------------------------\n\n/** Build the local-keygen-mode plugin. The service contributes\n * Snapshotable (secret) + Codegenable + Routable.\n *\n * Architecture mirror (walrus): `ContainerRuntimeService` +\n * `IdentityContext` wired via the supervisor's plugin runtime context,\n * artifact publisher publisher + chain probe yielded inside `acquire`. */\nconst buildLocalKeygenPlugin = <const Signer extends SealSignerMember>(\n\topts: SealLocalKeygenOptions<Signer>,\n) => {\n\t// Synchronous factory-time defaults. Localnet-signer-required is\n\t// enforced by the typed `signer:` field on\n\t// SealLocalKeygenOptions.\n\tconst resolved = resolveLocalKeygenOptions(opts, DEFAULT_SEAL_VERSION);\n\n\tconst sealResource = makeSealResource(resolved.name);\n\n\treturn definePlugin({\n\t\tid: sealResource.id,\n\t\tdependsOn: { sui: suiResource, signer: opts.signer },\n\t\trole: 'service',\n\t\tsection: 'service',\n\t\tpluginKey: sealPluginKey(resolved.name),\n\t\t// Stack-free codegen: the key-server object id / URL / committee are\n\t\t// LOADED CONFIG DATA -- the committed `seal.ts` stub emits\n\t\t// `requireValue(dep, 'seal:<name>', '<key>')`, never a baked object id.\n\t\tstaticCodegen: () => [makeSealStaticCodegen({ name: resolved.name, mode: 'local-keygen' })],\n\t\t// `deps` auto-infers the resolved `{ sui, signer }` dependency\n\t\t// object from seal's `dependsOn: { sui: suiResource, signer:\n\t\t// opts.signer }`. `ctx` is the typed plugin-authoring surface the\n\t\t// contribution emission below drives; it arrives via the\n\t\t// `PluginContext` service.\n\t\tstart: (deps) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst { sui, signer: signerAccount } = deps;\n\t\t\t\t// Substrate-context primitives:\n\t\t\t\t// - `ContainerRuntimeService` + `IdentityContext` arrive\n\t\t\t\t// via the supervisor's plugin runtime context.\n\t\t\t\t// - `ArtifactPublisher` is the substrate-level\n\t\t\t\t// publisher (cache → verify → produce → register cycle).\n\t\t\t\t// - Chain probe is looked up via the\n\t\t\t\t// StrategyRegistry under `chain-probe:<chainId>`; the\n\t\t\t\t// Sui plugin registered it during its own acquire.\n\t\t\t\t// The probe is yielded eagerly so the dep edge fails\n\t\t\t\t// fast if sui's chain-probe registration is missing,\n\t\t\t\t// matching walrus's pattern.\n\t\t\t\t// - `StackPathsService` resolves the per-stack on-disk\n\t\t\t\t// root so the key-server config + master-key.env\n\t\t\t\t// bind-mount sources are real paths (not the\n\t\t\t\t// `<runtime>/...` template).\n\t\t\t\tconst runtime = yield* ContainerRuntimeService;\n\t\t\t\tconst identity = yield* IdentityContext;\n\t\t\t\tconst publisher = yield* CacheService;\n\t\t\t\tconst stackPaths = yield* StackPathsService;\n\t\t\t\tconst fs = yield* FileSystem.FileSystem;\n\t\t\t\tconst path = yield* Path.Path;\n\t\t\t\tconst probe = yield* chainProbeFor<SealObjectProbeKey>(sui.chainId);\n\n\t\t\t\t// Resolve the seal service dir from the per-stack paths\n\t\t\t\t// bundle. The dir must exist before the key-server's\n\t\t\t\t// bind-mounts (config yaml + master-key env-file).\n\t\t\t\tconst servicePath = path.join(stackPaths.stackRoot, 'seal', resolved.name);\n\t\t\t\tyield* fs.makeDirectory(servicePath, { recursive: true }).pipe(\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\tsealError('config-render', {\n\t\t\t\t\t\t\t\tname: resolved.name,\n\t\t\t\t\t\t\t\tmessage: `seal.config-render: failed to create service directory at ${servicePath} — downstream config + master-key writes would all fail; surfacing the underlying filesystem error.`,\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\t// Cross-container DNS: seal's key-server dials sui RPC via\n\t\t\t\t// `host.docker.internal`. The sui plugin binds a brokered\n\t\t\t\t// host port; the SDK publish/register path runs from the host\n\t\t\t\t// process and does not need the container network.\n\t\t\t\t//\n\t\t\t\t// Architectural decision (B5): seal owns its OWN docker\n\t\t\t\t// network for the key-server's network attachment;\n\t\t\t\t// sui-side hops go through the host gateway. Mirrors\n\t\t\t\t// walrus's pattern (see plugins/walrus/index.ts §B5).\n\t\t\t\tconst sealNetworkName = buildSealNetworkName(identity.app, identity.stack, resolved.name);\n\t\t\t\tconst suiRpcUrlInNetwork = sui.hostGateway.rpcUrl;\n\t\t\t\tconst routed = buildSealKeyServerPublicRoute({\n\t\t\t\t\tapp: identity.app,\n\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\tport: DEFAULT_KEY_SERVER_PORT,\n\t\t\t\t});\n\n\t\t\t\t// Idempotent network create — the long-running key-server\n\t\t\t\t// attaches to this name. The seal boot pipeline doesn't own\n\t\t\t\t// the create (unlike walrus's local-cluster which calls\n\t\t\t\t// `ensureNetwork` from its mode body); we call it here\n\t\t\t\t// because the substrate's `acquire` is the only seam that\n\t\t\t\t// holds both the runtime + the identity needed for the\n\t\t\t\t// per-stack label tuple.\n\t\t\t\tconst sealSubnetPrefix = deriveSealSubnetPrefix({\n\t\t\t\t\tapp: identity.app,\n\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\tsealName: resolved.name,\n\t\t\t\t});\n\t\t\t\tyield* runtime.ensureNetwork(\n\t\t\t\t\twithSubnetAddressing(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname: sealNetworkName,\n\t\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsealSubnetPrefix,\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\tconst localKeygenDeps: LocalKeygenDeps = {\n\t\t\t\t\truntime,\n\t\t\t\t\tpublisher,\n\t\t\t\t\tsigner: signerAccount,\n\t\t\t\t\tsdk: sui.sdk,\n\t\t\t\t\t...(sui.buildImage !== null ? { buildImage: sui.buildImage } : {}),\n\t\t\t\t\tchainProbe: probe,\n\t\t\t\t\tchainId: sui.chainId,\n\t\t\t\t\tservicePath,\n\t\t\t\t\tcontainerName: `devstack-${identity.app}-${identity.stack}-seal-${resolved.name}-key-server`,\n\t\t\t\t\tlabels: {\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\tplugin: 'seal',\n\t\t\t\t\t\trole: 'key-server',\n\t\t\t\t\t},\n\t\t\t\t\tsuiNetworkName: sealNetworkName,\n\t\t\t\t\tsuiRpcUrlInNetwork,\n\t\t\t\t\troutedHostname: routed.hostname,\n\t\t\t\t\troutedUrl: routed.url,\n\t\t\t\t};\n\n\t\t\t\tconst boot = (yield* bootLocalKeygen(\n\t\t\t\t\tlocalKeygenDeps,\n\t\t\t\t\tresolved,\n\t\t\t\t)) satisfies SealLocalKeygenResolved;\n\n\t\t\t\tconst resolvedValue: SealResolved = {\n\t\t\t\t\t...boot.keyServer,\n\t\t\t\t\tmode: 'local-keygen',\n\t\t\t\t\tmanager: boot.keyManager,\n\t\t\t\t};\n\t\t\t\t// Emit the resolved contributions inline: snapshot (seal vault /\n\t\t\t\t// master-key secret-material subtree) → codegen → routable (key\n\t\t\t\t// server). `resolvedValue` is the just-resolved `SealResolved`;\n\t\t\t\t// `identity` (from `IdentityContext`) stamps the snapshot +\n\t\t\t\t// routable container name. The codegen bindings carry the REAL\n\t\t\t\t// objectId / keyServerUrl / serverConfigs.\n\t\t\t\tctx.snapshotExtra(\n\t\t\t\t\tmakeLocalKeygenSnapshotable({\n\t\t\t\t\t\tname: resolved.name,\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tctx.codegen(\n\t\t\t\t\tmakeSealCodegenable({\n\t\t\t\t\t\tname: resolved.name,\n\t\t\t\t\t\tobjectId: resolvedValue.objectId,\n\t\t\t\t\t\tkeyServerUrl: resolvedValue.keyServerUrl,\n\t\t\t\t\t\tserverConfigs: resolvedValue.serverConfigs,\n\t\t\t\t\t\tverifyKeyServers: resolvedValue.verifyKeyServers,\n\t\t\t\t\t\tmode: 'local-keygen',\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tctx.endpoint(\n\t\t\t\t\tmakeSealRoutable({\n\t\t\t\t\t\tname: resolved.name,\n\t\t\t\t\t\tcontainerName: `devstack-${identity.app}-${identity.stack}-seal-${resolved.name}-key-server`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn resolvedValue;\n\t\t\t}),\n\t});\n};\n\n/** Build the live-mode plugin. No Routable (URL is remote). */\nconst buildLivePlugin = (opts: SealLiveOptions) => {\n\tconst name = opts.name ?? DEFAULT_NAME;\n\tconst sealResource = makeSealResource(name);\n\t// Validate + resolve inputs at factory time so misconfigurations fail\n\t// before any plugin row starts work. `resolved.serverConfigs` is the\n\t// full SDK-ready array (independent fan-out OR a single committee entry\n\t// with aggregatorUrl + the non-secret apiKeyName; never a secret apiKey).\n\tconst resolved: ResolvedLiveInputs = validateLiveInputs({ name, ...opts });\n\tconst bindings: SealBindings = {\n\t\tname,\n\t\tobjectId: resolved.objectId,\n\t\tkeyServerUrl: resolved.keyServerUrl,\n\t\tserverConfigs: resolved.serverConfigs,\n\t\tverifyKeyServers: resolved.verifyKeyServers,\n\t\tmode: 'live',\n\t};\n\tconst snap = makeKnownSnapshotable({ name });\n\n\treturn definePlugin({\n\t\tid: sealResource.id,\n\t\trole: 'task',\n\t\tsection: 'service',\n\t\t// Live mode resolves its `{objectId, keyServerUrl, serverConfigs}` tuple\n\t\t// at factory time (DECLARED config) — bake them as literals in the\n\t\t// committed `seal.ts` (mirrors `knownPackage`). The committed\n\t\t// `serverConfigs` carries ALL entries + aggregatorUrl + apiKeyName; the\n\t\t// secret apiKey is NEVER emitted (the app injects it at runtime).\n\t\tstaticCodegen: () => [\n\t\t\tmakeSealStaticCodegen({\n\t\t\t\tname,\n\t\t\t\tmode: 'live',\n\t\t\t\tknown: {\n\t\t\t\t\tobjectId: bindings.objectId,\n\t\t\t\t\tkeyServerUrl: bindings.keyServerUrl,\n\t\t\t\t\tserverConfigs: bindings.serverConfigs,\n\t\t\t\t\tverifyKeyServers: bindings.verifyKeyServers,\n\t\t\t\t},\n\t\t\t}),\n\t\t],\n\t\t// Live mode has no `dependsOn`, so `start` is zero-arg. `ctx`\n\t\t// drives the contribution emission below; it arrives via the\n\t\t// `PluginContext` service.\n\t\tstart: () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\t// Live mode resolves its `{objectId, keyServerUrl, serverConfigs}`\n\t\t\t\t// tuple at FACTORY time (`resolved`), so `start` only projects the\n\t\t\t\t// already-resolved value — there is no runtime boot. `snap` +\n\t\t\t\t// `bindings` are likewise factory-scoped.\n\t\t\t\tctx.snapshotExtra(snap);\n\t\t\t\tctx.codegen(makeSealCodegenable(bindings));\n\t\t\t\treturn {\n\t\t\t\t\tserverConfigs: resolved.serverConfigs,\n\t\t\t\t\tkeyServerUrl: resolved.keyServerUrl,\n\t\t\t\t\tobjectId: resolved.objectId,\n\t\t\t\t\tverifyKeyServers: resolved.verifyKeyServers,\n\t\t\t\t\tmode: 'live',\n\t\t\t\t\tmanager: null,\n\t\t\t\t} satisfies SealResolved;\n\t\t\t}),\n\t});\n};\n\n/** Build the fork-known-mode plugin. Structurally symmetric with\n * `buildLivePlugin` — both resolve their `{objectId, keyServerUrl}`\n * tuple at factory time via `validateLiveInputs` (the fork-known\n * side maps `upstream → KnownNetwork` first via\n * `validateForkKnownInputs`) and thread it through SealMode's\n * `resolved:` envelope.\n *\n * Capabilities are kept on the dynamic shape (callback form)\n * rather than precomputed — leaves room for a future on-acquire\n * override path (e.g. the substrate dynamically rewrites the\n * bindings) without restructuring the factory. Not load-bearing\n * today; static capabilities would also work. */\nconst buildForkKnownPlugin = (opts: SealForkKnownOptions) => {\n\tconst name = opts.name ?? DEFAULT_NAME;\n\tconst sealResource = makeSealResource(name);\n\tconst snap = makeKnownSnapshotable({ name });\n\t// Validate inputs at factory time so misconfigurations fail\n\t// before any plugin row starts work — symmetric with\n\t// `buildLivePlugin`'s factory-boundary `validateLiveInputs` call.\n\t// `validateForkKnownInputs` maps the upstream alias to a\n\t// `KnownNetwork` and runs the same validation pipeline as live mode.\n\tconst validated = validateForkKnownInputs({ name, ...opts });\n\n\treturn definePlugin({\n\t\tid: sealResource.id,\n\t\trole: 'task',\n\t\tsection: 'service',\n\t\t// Fork-known mode resolves its `{objectId, keyServerUrl, serverConfigs}`\n\t\t// bundle at factory time (DECLARED config, via `validateForkKnownInputs`)\n\t\t// — bake them as literals in the committed `seal.ts` (mirrors\n\t\t// `knownPackage`). The resolved `serverConfigs` is the full SDK-ready\n\t\t// array (independent fan-out or the committee entry + aggregatorUrl), so\n\t\t// it is declared config too; the secret committee apiKey is NEVER\n\t\t// emitted (the app injects it at runtime).\n\t\tstaticCodegen: () => [\n\t\t\tmakeSealStaticCodegen({\n\t\t\t\tname,\n\t\t\t\tmode: 'fork-known',\n\t\t\t\tknown: {\n\t\t\t\t\tobjectId: validated.objectId,\n\t\t\t\t\tkeyServerUrl: validated.keyServerUrl,\n\t\t\t\t\tserverConfigs: validated.serverConfigs,\n\t\t\t\t\tverifyKeyServers: validated.verifyKeyServers,\n\t\t\t\t},\n\t\t\t}),\n\t\t],\n\t\t// Fork-known mode has no `dependsOn`, so `start` is zero-arg.\n\t\t// `ctx` drives the contribution emission below; it arrives via the\n\t\t// `PluginContext` service.\n\t\tstart: () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\t// Fork-known resolves its `{objectId, keyServerUrl, serverConfigs}`\n\t\t\t\t// bundle at FACTORY time (`validated`, via `validateForkKnownInputs`),\n\t\t\t\t// so `start` only projects the already-resolved value — no runtime\n\t\t\t\t// boot. `snap` is factory-scoped.\n\t\t\t\tctx.snapshotExtra(snap);\n\t\t\t\tctx.codegen(\n\t\t\t\t\tmakeSealCodegenable({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tobjectId: validated.objectId,\n\t\t\t\t\t\tkeyServerUrl: validated.keyServerUrl,\n\t\t\t\t\t\tserverConfigs: validated.serverConfigs,\n\t\t\t\t\t\tverifyKeyServers: validated.verifyKeyServers,\n\t\t\t\t\t\tmode: 'fork-known',\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn {\n\t\t\t\t\tserverConfigs: validated.serverConfigs,\n\t\t\t\t\tkeyServerUrl: validated.keyServerUrl,\n\t\t\t\t\tobjectId: validated.objectId,\n\t\t\t\t\tverifyKeyServers: validated.verifyKeyServers,\n\t\t\t\t\tmode: 'fork-known',\n\t\t\t\t\tmanager: null,\n\t\t\t\t} satisfies SealResolved;\n\t\t\t}),\n\t});\n};\n\n// ---------------------------------------------------------------------------\n// User-facing factories\n// ---------------------------------------------------------------------------\n\n/** Explicit Seal factory. `local-keygen` requires a signer; live and\n * fork-known modes must be selected directly or through `sealFor`. */\nexport const seal = <const Signer extends SealSignerMember = SealSignerMember>(\n\topts: SealOptions<Signer>,\n) => {\n\tswitch (opts.mode) {\n\t\tcase 'local-keygen':\n\t\t\treturn buildLocalKeygenPlugin(opts);\n\t\tcase 'live':\n\t\t\treturn buildLivePlugin(opts);\n\t\tcase 'fork-known':\n\t\t\treturn buildForkKnownPlugin(opts);\n\t}\n};\n\n/** Mode-narrowed factory namespace.\n *\n * Usage:\n * const local = { mode: 'local' } as const;\n * sealFor(local).localKeygen({signer}) // OK\n * sealFor(local).forkKnown(...) // type error: not in 'local' branch\n *\n * const fork = { mode: 'fork' } as const;\n * sealFor(fork).forkKnown({upstream}) // OK\n * sealFor(fork).localKeygen({signer}) // type error: not in 'fork' branch\n * // (distilled-doc invariant #8)\n *\n * Distilled-doc invariant #8 (fork-localkeygen-refused): the\n * fork-mode branch has NO `localKeygen` entry — that's the\n * type-level refusal. */\nexport const sealFor = defineModeNamespace({\n\tlocal: {\n\t\tlocalKeygen: <const Signer extends SealSignerMember>(opts: SealLocalKeygenOptions<Signer>) =>\n\t\t\tbuildLocalKeygenPlugin(opts),\n\t},\n\tlive: {\n\t\ttestnet: (opts: Omit<SealLiveOptions, 'network'> = {}) =>\n\t\t\tbuildLivePlugin({ network: 'testnet', ...opts }),\n\t\tmainnet: (opts: Omit<SealLiveOptions, 'network'> = {}) =>\n\t\t\tbuildLivePlugin({ network: 'mainnet', ...opts }),\n\t\t// Raw verbatim override — the caller supplies the full SDK-ready\n\t\t// `serverConfigs` array (e.g. a private deployment or a hand-tuned\n\t\t// committee). No known table is consulted.\n\t\tcustom: (opts: Required<Pick<SealLiveOptions, 'serverConfigs'>> & SealLiveOptions) =>\n\t\t\tbuildLivePlugin(opts),\n\t},\n\tfork: {\n\t\tforkKnown: (opts: SealForkKnownOptions) => buildForkKnownPlugin(opts),\n\t},\n});\n\n// ---------------------------------------------------------------------------\n// Type-only error re-export\n// ---------------------------------------------------------------------------\n\nexport type { SealError as SealAcquireError };\n"],"mappings":"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"}
@@ -1,5 +1,3 @@
1
- import { Effect } from "effect";
2
-
3
1
  //#region src/plugins/seal/mode/fork-known.d.ts
4
2
  /** Map a fork upstream to its known-deployment lookup target. */
5
3
  type ForkUpstream = 'mainnet' | 'testnet' | 'devnet';
@@ -1,21 +1,23 @@
1
- import { acquireLive, validateLiveInputs } from "./live.mjs";
2
- import "effect";
1
+ import { validateLiveInputs } from "./live.mjs";
3
2
  //#region src/plugins/seal/mode/fork-known.ts
4
3
  /** Resolve the wrapped upstream's known deployment key. Distilled-
5
4
  * doc invariant #13. */
6
5
  const resolveDeploymentNetwork = (upstream) => upstream;
7
6
  /** Resolve the fork-known overrides + upstream-derived defaults into
8
- * a validated tuple. Pure synchronous projection — mirrors the
9
- * factory-boundary `validateLiveInputs` call in `index.ts` for the
10
- * live branch. Throws on missing fields after fallback (matches the
11
- * live branch's behaviour see `validateLiveInputs`). */
7
+ * a resolved bundle. Pure synchronous projection — delegates to
8
+ * `validateLiveInputs` (the live branch's resolver) after mapping the
9
+ * upstream alias to a `KnownNetwork`, so the resolved `serverConfigs`
10
+ * (independent fan-out or committee) is threaded through unchanged.
11
+ * Throws `SealConfigError` on missing fields (matches the live
12
+ * branch's behaviour). */
12
13
  const validateForkKnownInputs = (inputs) => {
13
14
  try {
14
15
  return validateLiveInputs({
15
16
  name: inputs.name,
16
17
  network: resolveDeploymentNetwork(inputs.upstream),
17
- objectId: inputs.objectId,
18
- keyServerUrl: inputs.keyServerUrl
18
+ ...inputs.server !== void 0 ? { server: inputs.server } : {},
19
+ ...inputs.apiKeyName !== void 0 ? { apiKeyName: inputs.apiKeyName } : {},
20
+ ...inputs.verifyKeyServers !== void 0 ? { verifyKeyServers: inputs.verifyKeyServers } : {}
19
21
  });
20
22
  } catch (err) {
21
23
  if (isSealConfigError(err)) throw {
@@ -26,20 +28,7 @@ const validateForkKnownInputs = (inputs) => {
26
28
  }
27
29
  };
28
30
  const isSealConfigError = (value) => typeof value === "object" && value !== null && value._tag === "SealConfigError" && typeof value.message === "string";
29
- /** Acquire body for the fork-known mode. Inputs already pre-validated
30
- * by the barrel via `validateForkKnownInputs`; delegates the
31
- * read-side projection to live mode. The `upstream` field rides
32
- * through for downstream span attribution.
33
- *
34
- * Error channel kept typed as `SealError` for forward-compatibility
35
- * with any future fallible work this body needs to do (currently
36
- * `acquireLive` is `Effect.sync` so the channel is effectively
37
- * `never`). */
38
- const acquireForkKnown = (inputs) => acquireLive({
39
- name: inputs.name,
40
- resolved: inputs.resolved
41
- });
42
31
  //#endregion
43
- export { acquireForkKnown, validateForkKnownInputs };
32
+ export { validateForkKnownInputs };
44
33
 
45
34
  //# sourceMappingURL=fork-known.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"fork-known.mjs","names":[],"sources":["../../../../src/plugins/seal/mode/fork-known.ts"],"sourcesContent":["// Seal fork-known mode — *-fork networks route to the wrapped\n// upstream's known key-server deployment.\n//\n// Distilled-doc §\"Startup — `sealKnownKeyServer` (testnet / mainnet\n// / fork)\": structurally IDENTICAL to live mode. The only difference\n// is the network resolver step at the barrel — `*-fork` networks\n// route to the wrapped upstream (mainnet-fork → mainnet,\n// testnet-fork → testnet, devnet-fork → devnet) BEFORE the\n// known-deployment lookup fires.\n//\n// Distilled-doc invariant #13: `Seal()` on `*-fork` MUST route to\n// `sealKnownKeyServer` with the wrapped upstream's deployment. The\n// barrel (`index.ts`) enforces this via `resolveDeploymentNetwork`;\n// this file's mode body simply consumes the resolved network the\n// barrel passed.\n//\n// The mode body re-uses `acquireLive` since the boot pipeline is\n// identical. We expose a distinct factory only so the mode-narrowed\n// factory namespace (`sealFor(network).forkKnown(opts)`) can\n// route by mode without leaking the live-mode internals.\n\nimport { Effect } from 'effect';\n\nimport { acquireLive, validateLiveInputs, type KnownNetwork } from './live.ts';\nimport type { SealError } from '../errors.ts';\nimport type { SealKnownResolved } from '../registry-publish.ts';\n\n// ---------------------------------------------------------------------------\n// Fork → upstream network mapping\n// ---------------------------------------------------------------------------\n\n/** Map a fork upstream to its known-deployment lookup target. */\nexport type ForkUpstream = 'mainnet' | 'testnet' | 'devnet';\n\n/** Resolve the wrapped upstream's known deployment key. Distilled-\n * doc invariant #13. */\nexport const resolveDeploymentNetwork = (upstream: ForkUpstream): KnownNetwork => upstream;\n\n// ---------------------------------------------------------------------------\n// Inputs\n// ---------------------------------------------------------------------------\n\n/** Pre-validated fork-known inputs. Symmetric with the live mode's\n * `{ name, resolved }` shapethe barrel resolves\n * `upstream → KnownNetwork → validated tuple` synchronously at\n * factory time so both branches reach `acquireLive` with the same\n * structural envelope. */\nexport interface ForkKnownInputs {\n\treadonly name: string;\n\treadonly upstream: ForkUpstream;\n\treadonly resolved: { readonly objectId: string; readonly keyServerUrl: string };\n}\n\n/** Resolve the fork-known overrides + upstream-derived defaults into\n * a validated tuple. Pure synchronous projection mirrors the\n * factory-boundary `validateLiveInputs` call in `index.ts` for the\n * live branch. Throws on missing fields after fallback (matches the\n * live branch's behaviour — see `validateLiveInputs`). */\nexport const validateForkKnownInputs = (inputs: {\n\treadonly name: string;\n\treadonly upstream: ForkUpstream;\n\treadonly objectId?: string;\n\treadonly keyServerUrl?: string;\n}): { readonly objectId: string; readonly keyServerUrl: string } => {\n\ttry {\n\t\treturn validateLiveInputs({\n\t\t\tname: inputs.name,\n\t\t\tnetwork: resolveDeploymentNetwork(inputs.upstream),\n\t\t\tobjectId: inputs.objectId,\n\t\t\tkeyServerUrl: inputs.keyServerUrl,\n\t\t});\n\t} catch (err) {\n\t\t// Re-throw the original tagged error so downstream\n\t\t// `Effect.catchTag('SealConfigError', …)` still matches.\n\t\t// Wrapping in `new Error(…)` would strip the `_tag` and turn\n\t\t// the typed refusal into an untyped runtime crash. Prepend the\n\t\t// upstream context to the message in-place instead.\n\t\tif (isSealConfigError(err)) {\n\t\t\tthrow {\n\t\t\t\t...err,\n\t\t\t\tmessage: `seal.fork-known (upstream=${inputs.upstream}): ${err.message}`,\n\t\t\t};\n\t\t}\n\t\tthrow err;\n\t}\n};\n\nconst isSealConfigError = (\n\tvalue: unknown,\n): value is { readonly _tag: 'SealConfigError'; readonly message: string; [k: string]: unknown } =>\n\ttypeof value === 'object' &&\n\tvalue !== null &&\n\t(value as { _tag?: unknown })._tag === 'SealConfigError' &&\n\ttypeof (value as { message?: unknown }).message === 'string';\n\n// ---------------------------------------------------------------------------\n// Mode acquire\n// ---------------------------------------------------------------------------\n\n/** Acquire body for the fork-known mode. Inputs already pre-validated\n * by the barrel via `validateForkKnownInputs`; delegates the\n * read-side projection to live mode. The `upstream` field rides\n * through for downstream span attribution.\n *\n * Error channel kept typed as `SealError` for forward-compatibility\n * with any future fallible work this body needs to do (currently\n * `acquireLive` is `Effect.sync` so the channel is effectively\n * `never`). */\nexport const acquireForkKnown = (\n\tinputs: ForkKnownInputs,\n): Effect.Effect<SealKnownResolved, SealError> =>\n\tacquireLive({ name: inputs.name, resolved: inputs.resolved });\n"],"mappings":";;;;;AAoCA,MAAa,4BAA4B,aAAyC;;;;;;AAsBlF,MAAa,2BAA2B,WAK4B;CACnE,IAAI;EACH,OAAO,mBAAmB;GACzB,MAAM,OAAO;GACb,SAAS,yBAAyB,OAAO,QAAQ;GACjD,UAAU,OAAO;GACjB,cAAc,OAAO;EACtB,CAAC;CACF,SAAS,KAAK;EAMb,IAAI,kBAAkB,GAAG,GACxB,MAAM;GACL,GAAG;GACH,SAAS,6BAA6B,OAAO,SAAS,KAAK,IAAI;EAChE;EAED,MAAM;CACP;AACD;AAEA,MAAM,qBACL,UAEA,OAAO,UAAU,YACjB,UAAU,QACT,MAA6B,SAAS,qBACvC,OAAQ,MAAgC,YAAY;;;;;;;;;;AAerD,MAAa,oBACZ,WAEA,YAAY;CAAE,MAAM,OAAO;CAAM,UAAU,OAAO;AAAS,CAAC"}
1
+ {"version":3,"file":"fork-known.mjs","names":[],"sources":["../../../../src/plugins/seal/mode/fork-known.ts"],"sourcesContent":["// Seal fork-known mode — *-fork networks route to the wrapped\n// upstream's known key-server deployment.\n//\n// Distilled-doc §\"Startup — `sealKnownKeyServer` (testnet / mainnet\n// / fork)\": structurally IDENTICAL to live mode. The only difference\n// is the network resolver step at the barrel — `*-fork` networks\n// route to the wrapped upstream (mainnet-fork → mainnet,\n// testnet-fork → testnet, devnet-fork → devnet) BEFORE the\n// known-deployment lookup fires.\n//\n// Distilled-doc invariant #13: `Seal()` on `*-fork` MUST route to\n// `sealKnownKeyServer` with the wrapped upstream's deployment. The\n// barrel (`index.ts`) enforces this via `resolveDeploymentNetwork`;\n// this file's mode body simply consumes the resolved network the\n// barrel passed.\n//\n// The mode body re-uses live mode's resolver (`validateLiveInputs`)\n// since the resolution pipeline is identical. We expose a distinct\n// factory only so the mode-narrowed factory namespace\n// (`sealFor(network).forkKnown(opts)`) can route by mode without\n// leaking the live-mode internals.\n\nimport {\n\tvalidateLiveInputs,\n\ttype KnownNetwork,\n\ttype ResolvedLiveInputs,\n\ttype SealServerKind,\n} from './live.ts';\n\n// ---------------------------------------------------------------------------\n// Fork → upstream network mapping\n// ---------------------------------------------------------------------------\n\n/** Map a fork upstream to its known-deployment lookup target. */\nexport type ForkUpstream = 'mainnet' | 'testnet' | 'devnet';\n\n/** Resolve the wrapped upstream's known deployment key. Distilled-\n * doc invariant #13. */\nexport const resolveDeploymentNetwork = (upstream: ForkUpstream): KnownNetwork => upstream;\n\n// ---------------------------------------------------------------------------\n// Inputs\n// ---------------------------------------------------------------------------\n\n/** Resolve the fork-known overrides + upstream-derived defaults into\n * a resolved bundle. Pure synchronous projection delegates to\n * `validateLiveInputs` (the live branch's resolver) after mapping the\n * upstream alias to a `KnownNetwork`, so the resolved `serverConfigs`\n * (independent fan-out or committee) is threaded through unchanged.\n * Throws `SealConfigError` on missing fields (matches the live\n * branch's behaviour). */\nexport const validateForkKnownInputs = (inputs: {\n\treadonly name: string;\n\treadonly upstream: ForkUpstream;\n\treadonly server?: SealServerKind;\n\treadonly apiKeyName?: string;\n\treadonly verifyKeyServers?: boolean;\n}): ResolvedLiveInputs => {\n\ttry {\n\t\treturn validateLiveInputs({\n\t\t\tname: inputs.name,\n\t\t\tnetwork: resolveDeploymentNetwork(inputs.upstream),\n\t\t\t...(inputs.server !== undefined ? { server: inputs.server } : {}),\n\t\t\t...(inputs.apiKeyName !== undefined ? { apiKeyName: inputs.apiKeyName } : {}),\n\t\t\t...(inputs.verifyKeyServers !== undefined\n\t\t\t\t? { verifyKeyServers: inputs.verifyKeyServers }\n\t\t\t\t: {}),\n\t\t});\n\t} catch (err) {\n\t\t// Re-throw the original tagged error so downstream\n\t\t// `Effect.catchTag('SealConfigError', …)` still matches.\n\t\t// Wrapping in `new Error(…)` would strip the `_tag` and turn\n\t\t// the typed refusal into an untyped runtime crash. Prepend the\n\t\t// upstream context to the message in-place instead.\n\t\tif (isSealConfigError(err)) {\n\t\t\tthrow {\n\t\t\t\t...err,\n\t\t\t\tmessage: `seal.fork-known (upstream=${inputs.upstream}): ${err.message}`,\n\t\t\t};\n\t\t}\n\t\tthrow err;\n\t}\n};\n\nconst isSealConfigError = (\n\tvalue: unknown,\n): value is { readonly _tag: 'SealConfigError'; readonly message: string; [k: string]: unknown } =>\n\ttypeof value === 'object' &&\n\tvalue !== null &&\n\t(value as { _tag?: unknown })._tag === 'SealConfigError' &&\n\ttypeof (value as { message?: unknown }).message === 'string';\n"],"mappings":";;;;AAsCA,MAAa,4BAA4B,aAAyC;;;;;;;;AAalF,MAAa,2BAA2B,WAMd;CACzB,IAAI;EACH,OAAO,mBAAmB;GACzB,MAAM,OAAO;GACb,SAAS,yBAAyB,OAAO,QAAQ;GACjD,GAAI,OAAO,WAAW,KAAA,IAAY,EAAE,QAAQ,OAAO,OAAO,IAAI,CAAC;GAC/D,GAAI,OAAO,eAAe,KAAA,IAAY,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;GAC3E,GAAI,OAAO,qBAAqB,KAAA,IAC7B,EAAE,kBAAkB,OAAO,iBAAiB,IAC5C,CAAC;EACL,CAAC;CACF,SAAS,KAAK;EAMb,IAAI,kBAAkB,GAAG,GACxB,MAAM;GACL,GAAG;GACH,SAAS,6BAA6B,OAAO,SAAS,KAAK,IAAI;EAChE;EAED,MAAM;CACP;AACD;AAEA,MAAM,qBACL,UAEA,OAAO,UAAU,YACjB,UAAU,QACT,MAA6B,SAAS,qBACvC,OAAQ,MAAgC,YAAY"}
@@ -1,20 +1,43 @@
1
- import { Effect } from "effect";
2
-
3
1
  //#region src/plugins/seal/mode/live.d.ts
4
- /** Closed set of known deployments. `mainnet` + `devnet` intentionally
5
- * null (no public default key server). `testnet` carries the public
6
- * key-server URL but `keyServerObjectId` is `null` until a real id is
7
- * sourced `validateLiveInputs` forces the caller to supply
8
- * `objectId` explicitly in that case. */
9
- declare const KNOWN_DEPLOYMENTS: {
10
- readonly testnet: {
11
- readonly keyServerObjectId: string | null;
12
- readonly keyServerUrl: string;
2
+ /** Which class of known key-server backing to resolve. `independent`
3
+ * fans out to all of the network's standalone Open-mode servers
4
+ * (weight 1 each); `committee` resolves the single threshold-committee
5
+ * object reached through an aggregator. Plugin-INTERNAL not exported
6
+ * from the barrel. */
7
+ type SealServerKind = 'independent' | 'committee';
8
+ /** A single independent (Open-mode) key server. */
9
+ interface IndependentServerSpec {
10
+ readonly objectId: string;
11
+ readonly url: string;
12
+ }
13
+ /** The committee (threshold) deployment for a network. */
14
+ interface CommitteeSpec {
15
+ readonly objectId: string;
16
+ readonly aggregatorUrl: string;
17
+ /** Threshold `m-of-n`, carried for diagnostics / display only. */
18
+ readonly threshold: {
19
+ readonly m: number;
20
+ readonly n: number;
13
21
  };
14
- readonly mainnet: null;
22
+ /** When `true`, the committee aggregator demands an API key and the
23
+ * factory throws `SealConfigError` if the caller omits `apiKeyName`. */
24
+ readonly requiresApiKey: boolean;
25
+ }
26
+ /** Per-network known deployment. `independent` is `null` when the
27
+ * network ships no public standalone server (mainnet); `committee` is
28
+ * `null` when none ships (devnet). */
29
+ interface KnownDeployment {
30
+ readonly independent: ReadonlyArray<IndependentServerSpec> | null;
31
+ readonly committee: CommitteeSpec | null;
32
+ /** The default server kind for zero-config use of this network. */
33
+ readonly defaultServer: SealServerKind;
34
+ }
35
+ declare const KNOWN_DEPLOYMENTS: {
36
+ readonly testnet: KnownDeployment;
37
+ readonly mainnet: KnownDeployment;
15
38
  readonly devnet: null;
16
39
  };
17
40
  type KnownNetwork = keyof typeof KNOWN_DEPLOYMENTS;
18
41
  //#endregion
19
- export { KnownNetwork };
42
+ export { KnownNetwork, SealServerKind };
20
43
  //# sourceMappingURL=live.d.mts.map
@@ -1,57 +1,118 @@
1
- import { expectNonEmptyString } from "../../../substrate/runtime/config-validation.mjs";
2
1
  import { sealConfigError } from "../errors.mjs";
3
- import { Effect } from "effect";
4
2
  //#region src/plugins/seal/mode/live.ts
5
- /** Closed set of known deployments. `mainnet` + `devnet` intentionally
6
- * null (no public default key server). `testnet` carries the public
7
- * key-server URL but `keyServerObjectId` is `null` until a real id is
8
- * sourced — `validateLiveInputs` forces the caller to supply
9
- * `objectId` explicitly in that case. */
10
3
  const KNOWN_DEPLOYMENTS = {
11
4
  testnet: {
12
- keyServerObjectId: null,
13
- keyServerUrl: "https://seal-keyserver.testnet.mystenlabs.com"
5
+ independent: [{
6
+ objectId: "0x73d05d62c18d9374e3ea529e8e0ed6161da1a141a94d3f76ae3fe4e99356db75",
7
+ url: "https://seal-key-server-testnet-1.mystenlabs.com"
8
+ }, {
9
+ objectId: "0xf5d14a81a982144ae441cd7d64b09027f116a468bd36e7eca494f750591623c8",
10
+ url: "https://seal-key-server-testnet-2.mystenlabs.com"
11
+ }],
12
+ committee: {
13
+ objectId: "0xb012378c9f3799fb5b1a7083da74a4069e3c3f1c93de0b27212a5799ce1e1e98",
14
+ aggregatorUrl: "https://seal-aggregator-testnet.mystenlabs.com",
15
+ threshold: {
16
+ m: 3,
17
+ n: 5
18
+ },
19
+ requiresApiKey: false
20
+ },
21
+ defaultServer: "independent"
22
+ },
23
+ mainnet: {
24
+ independent: null,
25
+ committee: {
26
+ objectId: "0x686098f1439237fff9f36b99c7329683c22979d2005c2465cb891acb012a7595",
27
+ aggregatorUrl: "https://seal-aggregator-mainnet.mystenlabs.com",
28
+ threshold: {
29
+ m: 5,
30
+ n: 8
31
+ },
32
+ requiresApiKey: true
33
+ },
34
+ defaultServer: "committee"
14
35
  },
15
- mainnet: null,
16
36
  devnet: null
17
37
  };
18
- /** Validate the inputs at the factory boundary. Pure synchronous
19
- * function. The plugin `start` body is reserved for Effect-flavored
20
- * work; this validation throws synchronously (distilled-doc
21
- * §Failure modes). */
38
+ /** Validate + resolve the inputs at the factory boundary. Pure
39
+ * synchronous function the plugin `start` body is reserved for
40
+ * Effect-flavored work; this resolution throws `SealConfigError`
41
+ * synchronously (distilled-doc §Failure modes).
42
+ *
43
+ * Resolution rules (locked decisions #5/#6):
44
+ * - `serverConfigs` override wins verbatim.
45
+ * - else pick the server kind: explicit `server`, else the network's
46
+ * `defaultServer`.
47
+ * - `independent` → map every standalone server to `{objectId, weight:1}`.
48
+ * - `committee` → a single entry `{objectId, weight:1, aggregatorUrl,
49
+ * …apiKeyName}` (NO secret apiKey value); throw if the committee
50
+ * `requiresApiKey` and no `apiKeyName` is declared.
51
+ * - mainnet has no independent → `mainnet({server:'independent'})` throws. */
22
52
  const validateLiveInputs = (inputs) => {
23
- const fromNetwork = inputs.network && KNOWN_DEPLOYMENTS[inputs.network] ? KNOWN_DEPLOYMENTS[inputs.network] : null;
24
- const objectId = inputs.objectId ?? fromNetwork?.keyServerObjectId;
25
- const keyServerUrl = inputs.keyServerUrl ?? fromNetwork?.keyServerUrl;
26
- const message = `seal.live: missing required fields. Pass network ('testnet') or set objectId + keyServerUrl explicitly (got network=${String(inputs.network)}, objectId=${String(inputs.objectId)}, keyServerUrl=${String(inputs.keyServerUrl)}).`;
53
+ const context = `seal.live: network=${String(inputs.network)}, server=${String(inputs.server)}`;
54
+ const verifyKeyServers = inputs.verifyKeyServers ?? true;
55
+ if (inputs.serverConfigs !== void 0) {
56
+ const configs = inputs.serverConfigs;
57
+ if (configs.length === 0) throw sealConfigError({
58
+ field: "serverConfigs",
59
+ message: `${context}: serverConfigs override must carry at least one entry.`
60
+ });
61
+ if (configs.some((c) => c.apiKey !== void 0)) throw sealConfigError({
62
+ field: "serverConfigs",
63
+ message: `${context}: serverConfigs override embeds an apiKey, but devstack never carries the secret apiKey value (the committed config + deployment.json are world-readable). Drop apiKey here (keep apiKeyName) and inject the apiKey at runtime when you build SealClient.`
64
+ });
65
+ const first = configs[0];
66
+ return {
67
+ serverConfigs: configs,
68
+ objectId: first.objectId,
69
+ keyServerUrl: first.aggregatorUrl ?? "",
70
+ verifyKeyServers
71
+ };
72
+ }
73
+ const deployment = inputs.network ? KNOWN_DEPLOYMENTS[inputs.network] : null;
74
+ if (deployment === null || deployment === void 0) throw sealConfigError({
75
+ field: "network",
76
+ message: `${context}: no known seal deployment. Pass a supported network ('testnet'|'mainnet') with an optional { server } selector, or supply serverConfigs explicitly.`
77
+ });
78
+ if ((inputs.server ?? deployment.defaultServer) === "independent") {
79
+ const servers = deployment.independent;
80
+ if (servers === null) throw sealConfigError({
81
+ field: "server",
82
+ message: `${context}: no independent (standalone) key server ships for this network — use { server: 'committee' } (declare { apiKeyName } where required; inject the apiKey at runtime).`
83
+ });
84
+ return {
85
+ serverConfigs: servers.map((s) => ({
86
+ objectId: s.objectId,
87
+ weight: 1
88
+ })),
89
+ objectId: servers[0].objectId,
90
+ keyServerUrl: servers[0].url,
91
+ verifyKeyServers
92
+ };
93
+ }
94
+ const committee = deployment.committee;
95
+ if (committee === null) throw sealConfigError({
96
+ field: "server",
97
+ message: `${context}: no committee key server ships for this network.`
98
+ });
99
+ if (committee.requiresApiKey && (inputs.apiKeyName === void 0 || inputs.apiKeyName.length === 0)) throw sealConfigError({
100
+ field: "apiKeyName",
101
+ message: `${context}: the ${committee.threshold.m}-of-${committee.threshold.n} committee requires credentials. Pass { apiKeyName } (the non-secret header name); inject the secret apiKey value at runtime when you build SealClient — devstack does not carry it.`
102
+ });
27
103
  return {
28
- objectId: expectNonEmptyString(objectId, {
29
- field: "objectId",
30
- message,
31
- mkError: sealConfigError
32
- }),
33
- keyServerUrl: expectNonEmptyString(keyServerUrl, {
34
- field: "keyServerUrl",
35
- message,
36
- mkError: sealConfigError
37
- })
38
- };
39
- };
40
- /** Acquire body for the live mode. Validation already ran at the
41
- * factory boundary (see `index.ts:buildLivePlugin`); this projects
42
- * the validated bundle into the resolved shape. Returns the
43
- * read-side handle ONLY (no manager tag — distilled-doc invariant #15). */
44
- const acquireLive = (inputs) => Effect.sync(() => {
45
- return { keyServer: {
46
104
  serverConfigs: [{
47
- objectId: inputs.resolved.objectId,
48
- weight: 1
105
+ objectId: committee.objectId,
106
+ weight: 1,
107
+ aggregatorUrl: committee.aggregatorUrl,
108
+ ...inputs.apiKeyName !== void 0 ? { apiKeyName: inputs.apiKeyName } : {}
49
109
  }],
50
- keyServerUrl: inputs.resolved.keyServerUrl,
51
- objectId: inputs.resolved.objectId
52
- } };
53
- });
110
+ objectId: committee.objectId,
111
+ keyServerUrl: committee.aggregatorUrl,
112
+ verifyKeyServers
113
+ };
114
+ };
54
115
  //#endregion
55
- export { acquireLive, validateLiveInputs };
116
+ export { validateLiveInputs };
56
117
 
57
118
  //# sourceMappingURL=live.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"live.mjs","names":[],"sources":["../../../../src/plugins/seal/mode/live.ts"],"sourcesContent":["// Seal live mode — testnet / mainnet known key server.\n//\n// Distilled-doc §\"Startup — `sealKnownKeyServer`\": far simpler than\n// the local-keygen path.\n//\n// 1. Look up the known deployment (or accept explicit overrides).\n// 2. Publish the resolved key-server endpoint as the plugin's\n// resource (the codegen + manifest emitters consume it).\n// 3. Return the read-side handle.\n//\n// NO chain interactions, NO docker, NO on-disk persistence, NO\n// keygen. Distilled-doc invariant #15 — the manager tag is NOT\n// produced (we don't own the master key for a remote deployment).\n//\n// Known-deployment lookup: distilled-doc §\"Adjacent Seal references\"\n// → `engine/known-deployments.ts:153-157` carries the\n// `mysten-testnet-1` Open-mode independent server. `mainnet`\n// intentionally empty (Mysten doesn't ship a public default key\n// server on mainnet — production is via Enoki). `devnet`\n// intentionally empty.\n\nimport { Effect } from 'effect';\n\nimport { expectNonEmptyString } from '../../../substrate/runtime/config-validation.ts';\nimport { sealConfigError } from '../errors.ts';\nimport type { SealKeyServerEntry, SealKnownResolved } from '../registry-publish.ts';\n\n// ---------------------------------------------------------------------------\n// Known-deployment table — distilled-doc reference\n// ---------------------------------------------------------------------------\n\n/** Closed set of known deployments. `mainnet` + `devnet` intentionally\n * null (no public default key server). `testnet` carries the public\n * key-server URL but `keyServerObjectId` is `null` until a real id is\n * sourced — `validateLiveInputs` forces the caller to supply\n * `objectId` explicitly in that case. */\nexport const KNOWN_DEPLOYMENTS: {\n\treadonly testnet: {\n\t\treadonly keyServerObjectId: string | null;\n\t\treadonly keyServerUrl: string;\n\t};\n\treadonly mainnet: null;\n\treadonly devnet: null;\n} = {\n\ttestnet: {\n\t\tkeyServerObjectId: null,\n\t\tkeyServerUrl: 'https://seal-keyserver.testnet.mystenlabs.com',\n\t},\n\tmainnet: null,\n\tdevnet: null,\n};\n\nexport type KnownNetwork = keyof typeof KNOWN_DEPLOYMENTS;\n\n// ---------------------------------------------------------------------------\n// Inputs\n// ---------------------------------------------------------------------------\n\n/** Live-mode inputs. Either `network` resolves to a known deployment\n * OR the user supplies explicit `objectId` + `keyServerUrl`\n * overrides. Distilled-doc §\"Configuration\" — the factory throws\n * synchronously if neither path produces a usable tuple. */\nexport interface LiveModeInputs {\n\treadonly name: string;\n\treadonly network?: KnownNetwork;\n\treadonly objectId?: string;\n\treadonly keyServerUrl?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Resolved\n// ---------------------------------------------------------------------------\n\n/** Validate the inputs at the factory boundary. Pure synchronous\n * function. The plugin `start` body is reserved for Effect-flavored\n * work; this validation throws synchronously (distilled-doc\n * §Failure modes). */\nexport const validateLiveInputs = (\n\tinputs: LiveModeInputs,\n): {\n\treadonly objectId: string;\n\treadonly keyServerUrl: string;\n} => {\n\tconst fromNetwork =\n\t\tinputs.network && KNOWN_DEPLOYMENTS[inputs.network] ? KNOWN_DEPLOYMENTS[inputs.network] : null;\n\tconst objectId = inputs.objectId ?? fromNetwork?.keyServerObjectId;\n\tconst keyServerUrl = inputs.keyServerUrl ?? fromNetwork?.keyServerUrl;\n\tconst message = `seal.live: missing required fields. Pass network ('testnet') or set objectId + keyServerUrl explicitly (got network=${String(\n\t\tinputs.network,\n\t)}, objectId=${String(inputs.objectId)}, keyServerUrl=${String(inputs.keyServerUrl)}).`;\n\treturn {\n\t\tobjectId: expectNonEmptyString(objectId, {\n\t\t\tfield: 'objectId',\n\t\t\tmessage,\n\t\t\tmkError: sealConfigError,\n\t\t}),\n\t\tkeyServerUrl: expectNonEmptyString(keyServerUrl, {\n\t\t\tfield: 'keyServerUrl',\n\t\t\tmessage,\n\t\t\tmkError: sealConfigError,\n\t\t}),\n\t};\n};\n\n// ---------------------------------------------------------------------------\n// Mode acquire\n// ---------------------------------------------------------------------------\n\n/** Acquire body for the live mode. Validation already ran at the\n * factory boundary (see `index.ts:buildLivePlugin`); this projects\n * the validated bundle into the resolved shape. Returns the\n * read-side handle ONLY (no manager tag — distilled-doc invariant #15). */\nexport const acquireLive = (inputs: {\n\treadonly name: string;\n\treadonly resolved: { readonly objectId: string; readonly keyServerUrl: string };\n}): Effect.Effect<SealKnownResolved> =>\n\tEffect.sync(() => {\n\t\tconst serverConfigs: ReadonlyArray<SealKeyServerEntry> = [\n\t\t\t{ objectId: inputs.resolved.objectId, weight: 1 },\n\t\t];\n\t\treturn {\n\t\t\tkeyServer: {\n\t\t\t\tserverConfigs,\n\t\t\t\tkeyServerUrl: inputs.resolved.keyServerUrl,\n\t\t\t\tobjectId: inputs.resolved.objectId,\n\t\t\t},\n\t\t} satisfies SealKnownResolved;\n\t});\n"],"mappings":";;;;;;;;;AAoCA,MAAa,oBAOT;CACH,SAAS;EACR,mBAAmB;EACnB,cAAc;CACf;CACA,SAAS;CACT,QAAQ;AACT;;;;;AA2BA,MAAa,sBACZ,WAII;CACJ,MAAM,cACL,OAAO,WAAW,kBAAkB,OAAO,WAAW,kBAAkB,OAAO,WAAW;CAC3F,MAAM,WAAW,OAAO,YAAY,aAAa;CACjD,MAAM,eAAe,OAAO,gBAAgB,aAAa;CACzD,MAAM,UAAU,uHAAuH,OACtI,OAAO,OACR,EAAE,aAAa,OAAO,OAAO,QAAQ,EAAE,iBAAiB,OAAO,OAAO,YAAY,EAAE;CACpF,OAAO;EACN,UAAU,qBAAqB,UAAU;GACxC,OAAO;GACP;GACA,SAAS;EACV,CAAC;EACD,cAAc,qBAAqB,cAAc;GAChD,OAAO;GACP;GACA,SAAS;EACV,CAAC;CACF;AACD;;;;;AAUA,MAAa,eAAe,WAI3B,OAAO,WAAW;CAIjB,OAAO,EACN,WAAW;EACV,eAAA,CAJD;GAAE,UAAU,OAAO,SAAS;GAAU,QAAQ;EAAE,CAInC;EACZ,cAAc,OAAO,SAAS;EAC9B,UAAU,OAAO,SAAS;CAC3B,EACD;AACD,CAAC"}
1
+ {"version":3,"file":"live.mjs","names":[],"sources":["../../../../src/plugins/seal/mode/live.ts"],"sourcesContent":["// Seal live mode — testnet / mainnet known key server.\n//\n// Distilled-doc §\"Startup — `sealKnownKeyServer`\": far simpler than\n// the local-keygen path.\n//\n// 1. Resolve the requested deployment (independent servers vs the\n// committee aggregator) into a `serverConfigs` array.\n// 2. Publish the resolved key-server endpoint as the plugin's\n// resource (the codegen + manifest emitters consume it).\n// 3. Return the read-side handle.\n//\n// NO chain interactions, NO docker, NO on-disk persistence, NO\n// keygen. Distilled-doc invariant #15 — the manager tag is NOT\n// produced (we don't own the master key for a remote deployment).\n//\n// Known-deployment table (real Mysten ids):\n// - testnet INDEPENDENT: two Open-mode servers, weight 1 each. This\n// is the zero-config default (`sealFor(net).testnet()`).\n// - testnet COMMITTEE: a single 3-of-5 committee object reached\n// through the testnet aggregator. Opt in via `{ server: 'committee' }`;\n// no API key required.\n// - mainnet COMMITTEE: a single 5-of-8 committee object reached\n// through the mainnet aggregator. This is the mainnet DEFAULT and\n// requires credentials — declare the non-secret `apiKeyName`; the\n// secret apiKey value is injected by the app at runtime, never by\n// devstack. Mainnet ships NO independent default — so\n// `mainnet({ server: 'independent' })` throws.\n\nimport { sealConfigError } from '../errors.ts';\nimport type { SealKeyServerEntry } from '../registry-publish.ts';\n\n// ---------------------------------------------------------------------------\n// Server-kind selector\n// ---------------------------------------------------------------------------\n\n/** Which class of known key-server backing to resolve. `independent`\n * fans out to all of the network's standalone Open-mode servers\n * (weight 1 each); `committee` resolves the single threshold-committee\n * object reached through an aggregator. Plugin-INTERNAL — not exported\n * from the barrel. */\nexport type SealServerKind = 'independent' | 'committee';\n\n// ---------------------------------------------------------------------------\n// Known-deployment table — real Mysten object ids\n// ---------------------------------------------------------------------------\n\n/** A single independent (Open-mode) key server. */\ninterface IndependentServerSpec {\n\treadonly objectId: string;\n\treadonly url: string;\n}\n\n/** The committee (threshold) deployment for a network. */\ninterface CommitteeSpec {\n\treadonly objectId: string;\n\treadonly aggregatorUrl: string;\n\t/** Threshold `m-of-n`, carried for diagnostics / display only. */\n\treadonly threshold: { readonly m: number; readonly n: number };\n\t/** When `true`, the committee aggregator demands an API key and the\n\t * factory throws `SealConfigError` if the caller omits `apiKeyName`. */\n\treadonly requiresApiKey: boolean;\n}\n\n/** Per-network known deployment. `independent` is `null` when the\n * network ships no public standalone server (mainnet); `committee` is\n * `null` when none ships (devnet). */\ninterface KnownDeployment {\n\treadonly independent: ReadonlyArray<IndependentServerSpec> | null;\n\treadonly committee: CommitteeSpec | null;\n\t/** The default server kind for zero-config use of this network. */\n\treadonly defaultServer: SealServerKind;\n}\n\nexport const KNOWN_DEPLOYMENTS: {\n\treadonly testnet: KnownDeployment;\n\treadonly mainnet: KnownDeployment;\n\treadonly devnet: null;\n} = {\n\ttestnet: {\n\t\tindependent: [\n\t\t\t{\n\t\t\t\tobjectId: '0x73d05d62c18d9374e3ea529e8e0ed6161da1a141a94d3f76ae3fe4e99356db75',\n\t\t\t\turl: 'https://seal-key-server-testnet-1.mystenlabs.com',\n\t\t\t},\n\t\t\t{\n\t\t\t\tobjectId: '0xf5d14a81a982144ae441cd7d64b09027f116a468bd36e7eca494f750591623c8',\n\t\t\t\turl: 'https://seal-key-server-testnet-2.mystenlabs.com',\n\t\t\t},\n\t\t],\n\t\tcommittee: {\n\t\t\tobjectId: '0xb012378c9f3799fb5b1a7083da74a4069e3c3f1c93de0b27212a5799ce1e1e98',\n\t\t\taggregatorUrl: 'https://seal-aggregator-testnet.mystenlabs.com',\n\t\t\tthreshold: { m: 3, n: 5 },\n\t\t\trequiresApiKey: false,\n\t\t},\n\t\t// Zero-config testnet = BOTH independent servers (locked decision #6).\n\t\tdefaultServer: 'independent',\n\t},\n\tmainnet: {\n\t\t// Mainnet ships no public independent default — committee only.\n\t\tindependent: null,\n\t\tcommittee: {\n\t\t\tobjectId: '0x686098f1439237fff9f36b99c7329683c22979d2005c2465cb891acb012a7595',\n\t\t\taggregatorUrl: 'https://seal-aggregator-mainnet.mystenlabs.com',\n\t\t\tthreshold: { m: 5, n: 8 },\n\t\t\trequiresApiKey: true,\n\t\t},\n\t\tdefaultServer: 'committee',\n\t},\n\tdevnet: null,\n};\n\nexport type KnownNetwork = keyof typeof KNOWN_DEPLOYMENTS;\n\n// ---------------------------------------------------------------------------\n// Inputs\n// ---------------------------------------------------------------------------\n\n/** Live-mode inputs. Either `network` resolves to a known deployment\n * (optionally narrowed by `server`), OR the user supplies explicit\n * `serverConfigs` verbatim. Distilled-doc §\"Configuration\" — the\n * factory throws synchronously if neither path produces a usable\n * `serverConfigs` array. */\nexport interface LiveModeInputs {\n\treadonly name: string;\n\treadonly network?: KnownNetwork;\n\t/** Which known server kind to resolve. Defaults to the network's\n\t * `defaultServer` (testnet→independent, mainnet→committee). */\n\treadonly server?: SealServerKind;\n\t/** The NON-secret header name for a credentialed committee (mainnet).\n\t * devstack emits this so the generated config tells the app which header\n\t * to set. The secret apiKey VALUE is NEVER taken by devstack — the app\n\t * injects it at runtime when constructing `SealClient` (a committed\n\t * config + the world-readable `deployment.json` must not carry secrets). */\n\treadonly apiKeyName?: string;\n\t/** Whether the SDK should verify the key servers against their on-chain\n\t * registration. Defaults to `true` for live / fork-known (real Mysten\n\t * servers). */\n\treadonly verifyKeyServers?: boolean;\n\t/** Raw override — bypasses the known table entirely. */\n\treadonly serverConfigs?: ReadonlyArray<SealKeyServerEntry>;\n}\n\n// ---------------------------------------------------------------------------\n// Resolution\n// ---------------------------------------------------------------------------\n\n/** The validated/resolved live-mode bundle. `serverConfigs` is the\n * SDK-ready array; the legacy single `objectId` / `keyServerUrl`\n * fields are derived from the first entry / chosen url for the\n * read-side handle + codegen literals that still expect them. */\nexport interface ResolvedLiveInputs {\n\treadonly serverConfigs: ReadonlyArray<SealKeyServerEntry>;\n\treadonly objectId: string;\n\treadonly keyServerUrl: string;\n\t/** SDK key-server verification flag. `true` by default for live /\n\t * fork-known (real Mysten servers); the caller may override. */\n\treadonly verifyKeyServers: boolean;\n}\n\n/** Validate + resolve the inputs at the factory boundary. Pure\n * synchronous function — the plugin `start` body is reserved for\n * Effect-flavored work; this resolution throws `SealConfigError`\n * synchronously (distilled-doc §Failure modes).\n *\n * Resolution rules (locked decisions #5/#6):\n * - `serverConfigs` override wins verbatim.\n * - else pick the server kind: explicit `server`, else the network's\n * `defaultServer`.\n * - `independent` → map every standalone server to `{objectId, weight:1}`.\n * - `committee` → a single entry `{objectId, weight:1, aggregatorUrl,\n * …apiKeyName}` (NO secret apiKey value); throw if the committee\n * `requiresApiKey` and no `apiKeyName` is declared.\n * - mainnet has no independent → `mainnet({server:'independent'})` throws. */\nexport const validateLiveInputs = (inputs: LiveModeInputs): ResolvedLiveInputs => {\n\tconst context = `seal.live: network=${String(inputs.network)}, server=${String(inputs.server)}`;\n\n\t// SDK default for live / fork-known is to verify the key servers against\n\t// their on-chain registration (real Mysten servers). The caller may opt out.\n\tconst verifyKeyServers = inputs.verifyKeyServers ?? true;\n\n\t// 1. Verbatim override.\n\tif (inputs.serverConfigs !== undefined) {\n\t\tconst configs = inputs.serverConfigs;\n\t\tif (configs.length === 0) {\n\t\t\tthrow sealConfigError({\n\t\t\t\tfield: 'serverConfigs',\n\t\t\t\tmessage: `${context}: serverConfigs override must carry at least one entry.`,\n\t\t\t});\n\t\t}\n\t\t// Secrets never ride a committed/world-readable config. devstack does\n\t\t// NOT carry the committee `apiKey` value (the committed `seal.ts` and the\n\t\t// browser-injected `deployment.json` are both world-readable). Reject an\n\t\t// override that embeds one — pass `apiKeyName` here and inject the secret\n\t\t// apiKey VALUE into `serverConfigs` at runtime when constructing SealClient.\n\t\tif (configs.some((c) => c.apiKey !== undefined)) {\n\t\t\tthrow sealConfigError({\n\t\t\t\tfield: 'serverConfigs',\n\t\t\t\tmessage: `${context}: serverConfigs override embeds an apiKey, but devstack never carries the secret apiKey value (the committed config + deployment.json are world-readable). Drop apiKey here (keep apiKeyName) and inject the apiKey at runtime when you build SealClient.`,\n\t\t\t});\n\t\t}\n\t\tconst first = configs[0]!;\n\t\treturn {\n\t\t\tserverConfigs: configs,\n\t\t\tobjectId: first.objectId,\n\t\t\t// `keyServerUrl` is best-effort / legacy for raw overrides: a\n\t\t\t// KeyServerConfig has no `url` field (the URL lives on the on-chain\n\t\t\t// object), so we derive it from `aggregatorUrl` when present and\n\t\t\t// fall back to empty. Consumers should read `serverConfigs`, not this.\n\t\t\tkeyServerUrl: first.aggregatorUrl ?? '',\n\t\t\tverifyKeyServers,\n\t\t};\n\t}\n\n\t// 2. Known-table resolution.\n\tconst deployment = inputs.network ? KNOWN_DEPLOYMENTS[inputs.network] : null;\n\tif (deployment === null || deployment === undefined) {\n\t\tthrow sealConfigError({\n\t\t\tfield: 'network',\n\t\t\tmessage: `${context}: no known seal deployment. Pass a supported network ('testnet'|'mainnet') with an optional { server } selector, or supply serverConfigs explicitly.`,\n\t\t});\n\t}\n\n\tconst kind: SealServerKind = inputs.server ?? deployment.defaultServer;\n\n\tif (kind === 'independent') {\n\t\tconst servers = deployment.independent;\n\t\tif (servers === null) {\n\t\t\tthrow sealConfigError({\n\t\t\t\tfield: 'server',\n\t\t\t\tmessage: `${context}: no independent (standalone) key server ships for this network — use { server: 'committee' } (declare { apiKeyName } where required; inject the apiKey at runtime).`,\n\t\t\t});\n\t\t}\n\t\tconst serverConfigs: ReadonlyArray<SealKeyServerEntry> = servers.map((s) => ({\n\t\t\tobjectId: s.objectId,\n\t\t\tweight: 1,\n\t\t}));\n\t\treturn {\n\t\t\tserverConfigs,\n\t\t\tobjectId: servers[0]!.objectId,\n\t\t\tkeyServerUrl: servers[0]!.url,\n\t\t\tverifyKeyServers,\n\t\t};\n\t}\n\n\t// kind === 'committee'\n\tconst committee = deployment.committee;\n\tif (committee === null) {\n\t\tthrow sealConfigError({\n\t\t\tfield: 'server',\n\t\t\tmessage: `${context}: no committee key server ships for this network.`,\n\t\t});\n\t}\n\t// A committee that requires credentials needs the NON-secret `apiKeyName`\n\t// declared so the generated config tells the app which header to set. The\n\t// secret apiKey VALUE is NEVER taken or emitted by devstack — the app\n\t// injects it at runtime when constructing SealClient (committed config +\n\t// deployment.json are world-readable).\n\tif (\n\t\tcommittee.requiresApiKey &&\n\t\t(inputs.apiKeyName === undefined || inputs.apiKeyName.length === 0)\n\t) {\n\t\tthrow sealConfigError({\n\t\t\tfield: 'apiKeyName',\n\t\t\tmessage: `${context}: the ${committee.threshold.m}-of-${committee.threshold.n} committee requires credentials. Pass { apiKeyName } (the non-secret header name); inject the secret apiKey value at runtime when you build SealClient — devstack does not carry it.`,\n\t\t});\n\t}\n\tconst serverConfigs: ReadonlyArray<SealKeyServerEntry> = [\n\t\t{\n\t\t\tobjectId: committee.objectId,\n\t\t\tweight: 1,\n\t\t\taggregatorUrl: committee.aggregatorUrl,\n\t\t\t...(inputs.apiKeyName !== undefined ? { apiKeyName: inputs.apiKeyName } : {}),\n\t\t},\n\t];\n\treturn {\n\t\tserverConfigs,\n\t\tobjectId: committee.objectId,\n\t\tkeyServerUrl: committee.aggregatorUrl,\n\t\tverifyKeyServers,\n\t};\n};\n"],"mappings":";;AAyEA,MAAa,oBAIT;CACH,SAAS;EACR,aAAa,CACZ;GACC,UAAU;GACV,KAAK;EACN,GACA;GACC,UAAU;GACV,KAAK;EACN,CACD;EACA,WAAW;GACV,UAAU;GACV,eAAe;GACf,WAAW;IAAE,GAAG;IAAG,GAAG;GAAE;GACxB,gBAAgB;EACjB;EAEA,eAAe;CAChB;CACA,SAAS;EAER,aAAa;EACb,WAAW;GACV,UAAU;GACV,eAAe;GACf,WAAW;IAAE,GAAG;IAAG,GAAG;GAAE;GACxB,gBAAgB;EACjB;EACA,eAAe;CAChB;CACA,QAAQ;AACT;;;;;;;;;;;;;;;AAgEA,MAAa,sBAAsB,WAA+C;CACjF,MAAM,UAAU,sBAAsB,OAAO,OAAO,OAAO,EAAE,WAAW,OAAO,OAAO,MAAM;CAI5F,MAAM,mBAAmB,OAAO,oBAAoB;CAGpD,IAAI,OAAO,kBAAkB,KAAA,GAAW;EACvC,MAAM,UAAU,OAAO;EACvB,IAAI,QAAQ,WAAW,GACtB,MAAM,gBAAgB;GACrB,OAAO;GACP,SAAS,GAAG,QAAQ;EACrB,CAAC;EAOF,IAAI,QAAQ,MAAM,MAAM,EAAE,WAAW,KAAA,CAAS,GAC7C,MAAM,gBAAgB;GACrB,OAAO;GACP,SAAS,GAAG,QAAQ;EACrB,CAAC;EAEF,MAAM,QAAQ,QAAQ;EACtB,OAAO;GACN,eAAe;GACf,UAAU,MAAM;GAKhB,cAAc,MAAM,iBAAiB;GACrC;EACD;CACD;CAGA,MAAM,aAAa,OAAO,UAAU,kBAAkB,OAAO,WAAW;CACxE,IAAI,eAAe,QAAQ,eAAe,KAAA,GACzC,MAAM,gBAAgB;EACrB,OAAO;EACP,SAAS,GAAG,QAAQ;CACrB,CAAC;CAKF,KAF6B,OAAO,UAAU,WAAW,mBAE5C,eAAe;EAC3B,MAAM,UAAU,WAAW;EAC3B,IAAI,YAAY,MACf,MAAM,gBAAgB;GACrB,OAAO;GACP,SAAS,GAAG,QAAQ;EACrB,CAAC;EAMF,OAAO;GACN,eALwD,QAAQ,KAAK,OAAO;IAC5E,UAAU,EAAE;IACZ,QAAQ;GACT,EAEa;GACZ,UAAU,QAAQ,EAAE,CAAE;GACtB,cAAc,QAAQ,EAAE,CAAE;GAC1B;EACD;CACD;CAGA,MAAM,YAAY,WAAW;CAC7B,IAAI,cAAc,MACjB,MAAM,gBAAgB;EACrB,OAAO;EACP,SAAS,GAAG,QAAQ;CACrB,CAAC;CAOF,IACC,UAAU,mBACT,OAAO,eAAe,KAAA,KAAa,OAAO,WAAW,WAAW,IAEjE,MAAM,gBAAgB;EACrB,OAAO;EACP,SAAS,GAAG,QAAQ,QAAQ,UAAU,UAAU,EAAE,MAAM,UAAU,UAAU,EAAE;CAC/E,CAAC;CAUF,OAAO;EACN,eAAA,CARA;GACC,UAAU,UAAU;GACpB,QAAQ;GACR,eAAe,UAAU;GACzB,GAAI,OAAO,eAAe,KAAA,IAAY,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;EAC5E,CAGY;EACZ,UAAU,UAAU;EACpB,cAAc,UAAU;EACxB;CACD;AACD"}