@fased/fased 0.1.68 → 0.1.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +20 -22
- package/config/sat-runtime.env +6 -2
- package/config/signer-protocol-v2.json +6 -0
- package/dist/agents/tools/wallet-action-tool.js +2 -0
- package/dist/agents/tools/wallet-tool.js +5 -3
- package/dist/brand.js +1 -1
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/cli/program/register.wallet.js +88 -5
- package/dist/commands/wallet.js +842 -57
- package/dist/config/sat-runtime-ids.js +6 -1
- package/dist/control-ui/assets/{app-DyPTS0oM.js → app-BLFLoFPk.js} +8 -8
- package/dist/control-ui/assets/{federation-DgA9e-Pn.js → federation-SGMAnM_8.js} +2 -2
- package/dist/control-ui/assets/{index-Cu13yYHr.js → index-S88kp5ZQ.js} +3 -3
- package/dist/control-ui/assets/{mining-Dg-3ll0F.js → mining-ClI8RW4u.js} +2 -2
- package/dist/control-ui/assets/{wallet-ByKBcYhm.js → wallet-CRIhDQbW.js} +201 -116
- package/dist/control-ui/index.html +1 -1
- package/dist/control-ui/version.json +1 -1
- package/dist/gateway/server-http.js +46 -9
- package/dist/mining/mainnet-sync.js +67 -5
- package/dist/operator/operator-readiness.js +1 -1
- package/dist/plugin-sdk/{audio-preflight-_FVQjR1r.js → audio-preflight-CS7Q_7-R.js} +2 -2
- package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
- package/dist/plugin-sdk/{chunk-items-LH4uKSnh.js → chunk-items-BLBZNORg.js} +1 -1
- package/dist/plugin-sdk/command-status.js +2 -2
- package/dist/plugin-sdk/{config-schema-DjU_uD-6.js → config-schema-BiBOUW89.js} +255 -47
- package/dist/plugin-sdk/device-pair.d.ts +1 -1
- package/dist/plugin-sdk/discord.d.ts +4 -4
- package/dist/plugin-sdk/index.d.ts +16 -16
- package/dist/plugin-sdk/index.js +2 -2
- package/dist/plugin-sdk/{monitor-DUMMbG4h.js → monitor-WXdn4mR-.js} +4 -4
- package/dist/plugin-sdk/{monitor-DXIM16Hj.js → monitor-xzVAPbg2.js} +6 -6
- package/dist/plugin-sdk/{openresponses-http-C8lKwta1.js → openresponses-http-DnI1LFgq.js} +2 -2
- package/dist/plugin-sdk/{pairing-message-mKO92L-M.d.ts → pairing-message-CiTcdD6m.d.ts} +1 -1
- package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
- package/dist/plugin-sdk/{pw-ai-DJ_gTc4M.js → pw-ai-DZYLDC-S.js} +1 -1
- package/dist/plugin-sdk/{runtime-dependency-nup8niQL.js → runtime-dependency-DczkvPxn.js} +9 -9
- package/dist/plugin-sdk/sat-runtime.d.ts +89 -10
- package/dist/plugin-sdk/sat-runtime.js +2 -2
- package/dist/plugin-sdk/{slack-B-nOOLM0.js → slack-CkIRVDUK.js} +4 -4
- package/dist/plugin-sdk/slack.d.ts +4 -4
- package/dist/plugin-sdk/{sticker-cache-BLiId8Em.js → sticker-cache-Dpap5ce_.js} +2 -2
- package/dist/plugin-sdk/{telegram-actions-1X9A32Dn.js → telegram-actions-Cewdga8H.js} +3 -3
- package/dist/plugin-sdk/telegram.d.ts +4 -4
- package/dist/plugin-sdk/{tmp-fased-dir-BkpWlGUe.d.ts → tmp-fased-dir-BSEHc8MQ.d.ts} +2 -2
- package/dist/plugin-sdk/{types-DtCOO_hW.d.ts → types-BakzIzGN.d.ts} +1 -1
- package/dist/plugin-sdk/{types-B-zL9gSv.d.ts → types-CzF0YQqK.d.ts} +2 -2
- package/dist/plugin-sdk/{web-BvjNyYBz.js → web-DGWi7i3B.js} +2 -2
- package/dist/plugin-sdk/whatsapp.d.ts +9 -9
- package/dist/plugin-sdk/{zod-schema.providers-core-BCDlgtls.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +63 -63
- package/dist/wallet/local-socket-signer-lifecycle.js +79 -3
- package/dist/wallet/local-socket-signer-protocol.js +70 -10
- package/dist/wallet/mining-wallet-retirement.js +256 -0
- package/dist/wallet/providers/local-socket-signer-adapter.js +2 -0
- package/dist/wallet/signer-protocol-v2.generated.js +6 -0
- package/dist/wallet/wallet-agent-selection.js +6 -10
- package/dist/wallet/wallet-provider-registry.js +70 -29
- package/dist/wallet/wallet-send-approvals.js +6 -2
- package/dist/wallet/wallet-status.js +56 -1
- package/dist/wizard/onboarding.js +32 -30
- package/dist/wizard/onboarding.wallet.js +10 -1
- package/extensions/acpx/package.json +1 -1
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/fased-federation/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +6 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +2 -2
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +6 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +6 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/sat-mining/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/synology-chat/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +6 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +6 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +6 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +6 -0
- package/extensions/zalouser/package.json +1 -1
- package/install.sh +276 -103
- package/package.json +4 -3
- package/scripts/install-fased-signerd.sh +4 -8
- package/scripts/install-runtime-profile.sh +27 -0
- package/scripts/start-managed.sh +13 -1
- package/scripts/fased-signer-wallet-import-hosting.sh +0 -78
package/dist/commands/wallet.js
CHANGED
|
@@ -3,15 +3,16 @@ import { loadConfig, writeConfigFile } from "../config/io.js";
|
|
|
3
3
|
import "../config/config.js";
|
|
4
4
|
import { hasLegacyEmbeddedKeystoreConfig, hasLegacyEmbeddedKeystoreMaterialHint, throwLegacyEmbeddedKeystoreMigrationRequired } from "../wallet/legacy-embedded-keystore.js";
|
|
5
5
|
import { ensureWalletStateDir, isLocalSignerExternallyManaged, resolveLocalSignerControlSocketPath, resolveLocalSignerMasterKeyPath, resolveLocalSignerSidecarPaths, resolveLocalSignerSocketPath, resolveLocalSignerStateDbPath } from "../wallet/wallet-runtime-config.js";
|
|
6
|
-
import { normalizeWalletUserRole, readWalletProviderRegistry, resolveWalletUserRole, setDefaultWallet, setNamedWalletRole, setWalletProviderEnabled, upsertNamedWallet } from "../wallet/wallet-provider-registry.js";
|
|
6
|
+
import { normalizeWalletUserRole, readWalletProviderRegistry, replaceRetiredMiningWallet, resolveWalletUserRole, setDefaultWallet, setNamedWalletRole, setWalletProviderEnabled, upsertNamedWallet, writeWalletProviderRegistry } from "../wallet/wallet-provider-registry.js";
|
|
7
7
|
import { buildWalletProviderCapabilityMatrix } from "../wallet/wallet-provider-capabilities.js";
|
|
8
8
|
import { redactWalletDiagnosticText } from "../wallet/wallet-redaction.js";
|
|
9
9
|
import { callLocalSocketSigner, probeLocalSocketSignerHealth, requireLocalSocketSignerPath } from "../wallet/providers/local-socket-signer-adapter.js";
|
|
10
10
|
import { readWalletProviderSecretStatus, saveWalletProviderSecret } from "../wallet/wallet-secrets-store.js";
|
|
11
11
|
import { createWalletProviderAdapter, resolveWalletProviderId } from "../wallet/wallet-provider-resolver.js";
|
|
12
|
-
import {
|
|
13
|
-
import { createLockedSignerOwnedWallet } from "../wallet/local-socket-signer-lifecycle.js";
|
|
12
|
+
import { activateSignerOwnedRoleBaseline, createRoleReadySignerOwnedWallet, readSignerOwnedWallet, readSignerOwnedWalletReadiness } from "../wallet/local-socket-signer-lifecycle.js";
|
|
14
13
|
import { normalizeNativeSignerWalletId } from "../wallet/native-signer-wallet-id.js";
|
|
14
|
+
import { readWalletStatusSnapshot, resolveWalletConfigForRuntime } from "../wallet/wallet-status.js";
|
|
15
|
+
import { buildMiningRetirementEvidence, verifyMiningRecoveryPackage, writeMiningRetirementReceipt } from "../wallet/mining-wallet-retirement.js";
|
|
15
16
|
import { configureSignerOwnedWalletNetwork } from "../wallet/signer-network-admin.js";
|
|
16
17
|
import { listWalletInboundEvents, pollWalletInboundEvents, reconcileWalletInboundEvents } from "../wallet/wallet-inbound-events.js";
|
|
17
18
|
import { buildWalletCanaryReport, runWalletProviderCanaryReport } from "../wallet/wallet-canary.js";
|
|
@@ -411,7 +412,14 @@ async function createSignerOwnedWalletForSetup(params) {
|
|
|
411
412
|
}
|
|
412
413
|
let result;
|
|
413
414
|
try {
|
|
414
|
-
result =
|
|
415
|
+
result = hosted && params.env.FASED_GATEWAY_SERVICE !== "1" ? invokeNativeSignerWalletCreate({
|
|
416
|
+
signerBinPath: "/opt/fased/signer/fased-signerd",
|
|
417
|
+
operatorSocketPath: "/run/fased-signerd/operator.sock",
|
|
418
|
+
walletId: expectedSignerWalletId,
|
|
419
|
+
role: params.role,
|
|
420
|
+
allowExisting: Boolean(params.options.force),
|
|
421
|
+
env: mergedEnv
|
|
422
|
+
}) : await createRoleReadySignerOwnedWallet({
|
|
415
423
|
socketPath,
|
|
416
424
|
walletId,
|
|
417
425
|
role: params.role,
|
|
@@ -419,7 +427,7 @@ async function createSignerOwnedWalletForSetup(params) {
|
|
|
419
427
|
});
|
|
420
428
|
} catch (error) {
|
|
421
429
|
if (hosted) {
|
|
422
|
-
throw new Error(`
|
|
430
|
+
throw new Error(`hosted signer wallet creation failed: ${error instanceof Error ? error.message : String(error)}. Confirm the operator socket and exact installed signer release are healthy, then retry the same wallet ID.`, { cause: error });
|
|
423
431
|
}
|
|
424
432
|
throw error;
|
|
425
433
|
}
|
|
@@ -431,7 +439,16 @@ async function createSignerOwnedWalletForSetup(params) {
|
|
|
431
439
|
if (signerIdCollision) {
|
|
432
440
|
throw new Error(`native signer wallet ID ${signerWalletId} is already registered as ${signerIdCollision.id}; choose a distinct wallet ID`);
|
|
433
441
|
}
|
|
434
|
-
const
|
|
442
|
+
const operatorLifecycle = hosted && params.env.FASED_GATEWAY_SERVICE !== "1";
|
|
443
|
+
const network = operatorLifecycle ? invokeNativeSignerNetworkSetPrimary({
|
|
444
|
+
signerBinPath: "/opt/fased/signer/fased-signerd",
|
|
445
|
+
socketFlag: "--operator-socket",
|
|
446
|
+
socketPath: "/run/fased-signerd/operator.sock",
|
|
447
|
+
walletId: signerWalletId,
|
|
448
|
+
primaryRpcUrl: params.rpcUrl,
|
|
449
|
+
expectedVersion: 0,
|
|
450
|
+
env: mergedEnv
|
|
451
|
+
}) : await configureSignerOwnedWalletNetwork({
|
|
435
452
|
walletId: signerWalletId,
|
|
436
453
|
primaryRpcUrl: params.rpcUrl,
|
|
437
454
|
env: mergedEnv,
|
|
@@ -439,6 +456,19 @@ async function createSignerOwnedWalletForSetup(params) {
|
|
|
439
456
|
});
|
|
440
457
|
cfg = setConfigEnvVar(cfg, rpcEnvKeyFor(params.chain, walletId), params.rpcUrl);
|
|
441
458
|
await writeConfigFile(cfg);
|
|
459
|
+
const readiness = operatorLifecycle ? invokeNativeSignerWalletReadiness({
|
|
460
|
+
signerBinPath: "/opt/fased/signer/fased-signerd",
|
|
461
|
+
socketFlag: "--operator-socket",
|
|
462
|
+
socketPath: "/run/fased-signerd/operator.sock",
|
|
463
|
+
walletId: signerWalletId,
|
|
464
|
+
env: mergedEnv
|
|
465
|
+
}) : await readSignerOwnedWalletReadiness({
|
|
466
|
+
socketPath,
|
|
467
|
+
walletId: signerWalletId
|
|
468
|
+
});
|
|
469
|
+
if (!readiness.ready || readiness.walletId !== signerWalletId || readiness.publicKey !== result.wallet.publicKey || readiness.role !== params.role || readiness.baselineVersion !== 1 || readiness.policyHash !== result.policy.hash || readiness.policyVersion !== result.policy.version || readiness.networkHash !== network.hash || readiness.networkVersion !== network.version) {
|
|
470
|
+
throw new Error(`Setup incomplete: signer readiness does not match the created ${params.role} wallet lifecycle`);
|
|
471
|
+
}
|
|
442
472
|
const wallet = upsertNamedWallet({
|
|
443
473
|
walletId,
|
|
444
474
|
name: params.options.walletName || "Wallet",
|
|
@@ -451,19 +481,16 @@ async function createSignerOwnedWalletForSetup(params) {
|
|
|
451
481
|
signerWalletId,
|
|
452
482
|
policyHash: result.policy.hash,
|
|
453
483
|
policyVersion: result.policy.version,
|
|
454
|
-
|
|
484
|
+
baselineVersion: readiness.baselineVersion,
|
|
485
|
+
policyState: "ready",
|
|
455
486
|
networkHash: network.hash,
|
|
456
487
|
networkVersion: network.version,
|
|
457
|
-
networkReady: network.ready
|
|
488
|
+
networkReady: network.ready,
|
|
489
|
+
operationLane: readiness.operationLane,
|
|
490
|
+
roleReady: readiness.ready
|
|
458
491
|
},
|
|
459
492
|
env: params.env
|
|
460
493
|
});
|
|
461
|
-
if (params.role === "agent") {
|
|
462
|
-
setDefaultWallet({
|
|
463
|
-
walletId: wallet.id,
|
|
464
|
-
env: params.env
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
494
|
if (params.role === "mining") {
|
|
468
495
|
const currentEntry = cfg.plugins?.entries?.["sat-mining"];
|
|
469
496
|
const currentSatConfig = currentEntry?.config && typeof currentEntry.config === "object" && !Array.isArray(currentEntry.config) ? currentEntry.config : {};
|
|
@@ -496,15 +523,18 @@ async function createSignerOwnedWalletForSetup(params) {
|
|
|
496
523
|
address: result.wallet.publicKey,
|
|
497
524
|
policyHash: result.policy.hash,
|
|
498
525
|
policyVersion: result.policy.version,
|
|
499
|
-
|
|
526
|
+
baselineVersion: readiness.baselineVersion,
|
|
527
|
+
policyState: "ready",
|
|
500
528
|
networkVersion: network.version,
|
|
501
|
-
networkReady: network.ready
|
|
529
|
+
networkReady: network.ready,
|
|
530
|
+
operationLane: readiness.operationLane,
|
|
531
|
+
roleReady: readiness.ready
|
|
502
532
|
}, null, 2));
|
|
503
533
|
} else {
|
|
504
534
|
params.runtime.log(`Signer wallet ID: ${signerWalletId}`);
|
|
505
535
|
params.runtime.log(`${params.chain.toUpperCase()} address: ${result.wallet.publicKey}`);
|
|
506
536
|
if (!params.options.noSignerHints) {
|
|
507
|
-
params.runtime.log(
|
|
537
|
+
params.runtime.log(`Role baseline active: ${params.role} v${readiness.baselineVersion} (${readiness.operationLane}).`);
|
|
508
538
|
}
|
|
509
539
|
}
|
|
510
540
|
return result;
|
|
@@ -551,26 +581,23 @@ function parseNativeSignerImportResult(params) {
|
|
|
551
581
|
throw new Error("native signer import returned an invalid result");
|
|
552
582
|
}
|
|
553
583
|
const result = value;
|
|
554
|
-
if (result.wallet?.walletId !== params.walletId || typeof result.wallet.publicKey !== "string" || !result.wallet.publicKey.trim() || !Number.isSafeInteger(result.wallet.version) || result.policy?.walletId !== params.walletId || result.policy.role !== params.role || result.policy.version !== 1 || result.policy.
|
|
555
|
-
throw new Error("native signer import did not return
|
|
584
|
+
if (result.wallet?.walletId !== params.walletId || typeof result.wallet.publicKey !== "string" || !result.wallet.publicKey.trim() || !Number.isSafeInteger(result.wallet.version) || result.policy?.walletId !== params.walletId || result.policy.role !== params.role || result.policy.version !== 1 || result.policy.baselineVersion !== 1 || !result.policy.operations?.length || !result.policy.programs?.length || !result.policy.assets?.length || typeof result.policy.hash !== "string" || !/^sha256:[0-9a-f]{64}$/.test(result.policy.hash)) {
|
|
585
|
+
throw new Error("native signer import did not return an active signer-owned role baseline");
|
|
556
586
|
}
|
|
557
587
|
return result;
|
|
558
588
|
}
|
|
559
589
|
function invokeNativeSignerWalletImport(params) {
|
|
560
|
-
if (params.hosted) {
|
|
561
|
-
throw new Error("Hosting private-key import is a signer-owner operation. Run /usr/local/sbin/fased-signer-wallet-import from the provider root console, then rerun wallet setup with Create and the same wallet ID to register the existing signer wallet.");
|
|
562
|
-
}
|
|
563
590
|
const input = requireOwnerOnlySignerImportFile(params.importFile);
|
|
564
591
|
const command = params.signerBinPath;
|
|
565
592
|
const args = [
|
|
566
593
|
"admin",
|
|
567
594
|
"wallet",
|
|
568
595
|
"import",
|
|
569
|
-
"--control-socket",
|
|
596
|
+
params.hosted ? "--operator-socket" : "--control-socket",
|
|
570
597
|
params.controlSocketPath,
|
|
571
598
|
"--wallet-id",
|
|
572
599
|
params.walletId,
|
|
573
|
-
"--
|
|
600
|
+
"--baseline-role",
|
|
574
601
|
params.role
|
|
575
602
|
];
|
|
576
603
|
try {
|
|
@@ -605,21 +632,294 @@ function invokeNativeSignerWalletImport(params) {
|
|
|
605
632
|
fs.closeSync(input.fd);
|
|
606
633
|
}
|
|
607
634
|
}
|
|
608
|
-
function
|
|
609
|
-
|
|
610
|
-
|
|
635
|
+
function nativeSignerLifecycleEnv(env) {
|
|
636
|
+
return {
|
|
637
|
+
HOME: env.HOME,
|
|
638
|
+
LANG: env.LANG || "C.UTF-8",
|
|
639
|
+
PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
function invokeNativeSignerWalletCreate(params) {
|
|
643
|
+
const child = spawnSync(params.signerBinPath, [
|
|
644
|
+
"admin",
|
|
645
|
+
"wallet",
|
|
646
|
+
"create",
|
|
647
|
+
"--operator-socket",
|
|
648
|
+
params.operatorSocketPath,
|
|
649
|
+
"--wallet-id",
|
|
650
|
+
params.walletId,
|
|
651
|
+
"--baseline-role",
|
|
652
|
+
params.role,
|
|
653
|
+
...params.allowExisting ? ["--allow-existing"] : []
|
|
654
|
+
], {
|
|
655
|
+
env: nativeSignerLifecycleEnv(params.env),
|
|
656
|
+
stdio: [
|
|
657
|
+
"ignore",
|
|
658
|
+
"pipe",
|
|
659
|
+
"pipe"
|
|
660
|
+
],
|
|
661
|
+
encoding: "utf8",
|
|
662
|
+
maxBuffer: 256 * 1024,
|
|
663
|
+
timeout: 3e4
|
|
664
|
+
});
|
|
665
|
+
if (child.error) {
|
|
666
|
+
throw child.error;
|
|
667
|
+
}
|
|
668
|
+
if (child.status !== 0) {
|
|
669
|
+
throw new Error(redactWalletDiagnosticText(String(child.stderr || "native signer create failed").trim()));
|
|
670
|
+
}
|
|
671
|
+
return parseNativeSignerImportResult({
|
|
672
|
+
stdout: String(child.stdout ?? ""),
|
|
673
|
+
walletId: params.walletId,
|
|
674
|
+
role: params.role
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
function invokeNativeSignerNetworkSetPrimary(params) {
|
|
678
|
+
const input = JSON.stringify({ primaryRpcUrl: params.primaryRpcUrl });
|
|
679
|
+
const child = spawnSync(params.signerBinPath, [
|
|
680
|
+
"admin",
|
|
681
|
+
"network",
|
|
682
|
+
"set-primary",
|
|
683
|
+
params.socketFlag,
|
|
684
|
+
params.socketPath,
|
|
685
|
+
"--wallet-id",
|
|
686
|
+
params.walletId,
|
|
687
|
+
"--expected-version",
|
|
688
|
+
String(params.expectedVersion)
|
|
689
|
+
], {
|
|
690
|
+
env: nativeSignerLifecycleEnv(params.env),
|
|
691
|
+
input,
|
|
692
|
+
stdio: [
|
|
693
|
+
"pipe",
|
|
694
|
+
"pipe",
|
|
695
|
+
"pipe"
|
|
696
|
+
],
|
|
697
|
+
encoding: "utf8",
|
|
698
|
+
maxBuffer: 256 * 1024,
|
|
699
|
+
timeout: 6e4
|
|
700
|
+
});
|
|
701
|
+
if (child.error) {
|
|
702
|
+
throw child.error;
|
|
703
|
+
}
|
|
704
|
+
if (child.status !== 0) {
|
|
705
|
+
throw new Error(redactWalletDiagnosticText(String(child.stderr || "native signer RPC update failed").trim()));
|
|
706
|
+
}
|
|
707
|
+
let result;
|
|
708
|
+
try {
|
|
709
|
+
result = JSON.parse(String(child.stdout ?? ""));
|
|
710
|
+
} catch {
|
|
711
|
+
throw new Error("native signer RPC update returned invalid JSON");
|
|
712
|
+
}
|
|
713
|
+
const record = result;
|
|
714
|
+
if (!record || record.walletId !== params.walletId || record.configured !== true || record.ready !== true || !Number.isSafeInteger(record.version) || typeof record.hash !== "string" || !/^hmac-sha256:[0-9a-f]{64}$/.test(record.hash)) {
|
|
715
|
+
throw new Error("native signer RPC update returned an invalid readiness summary");
|
|
716
|
+
}
|
|
717
|
+
return {
|
|
718
|
+
walletId: String(record.walletId),
|
|
719
|
+
configured: true,
|
|
720
|
+
ready: true,
|
|
721
|
+
version: Number(record.version),
|
|
722
|
+
hash: String(record.hash)
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
function invokeNativeSignerWalletReadiness(params) {
|
|
726
|
+
const child = spawnSync(params.signerBinPath, [
|
|
727
|
+
"admin",
|
|
728
|
+
"wallet",
|
|
729
|
+
"readiness",
|
|
730
|
+
params.socketFlag,
|
|
731
|
+
params.socketPath,
|
|
732
|
+
"--wallet-id",
|
|
733
|
+
params.walletId
|
|
734
|
+
], {
|
|
735
|
+
env: nativeSignerLifecycleEnv(params.env),
|
|
736
|
+
stdio: [
|
|
737
|
+
"ignore",
|
|
738
|
+
"pipe",
|
|
739
|
+
"pipe"
|
|
740
|
+
],
|
|
741
|
+
encoding: "utf8",
|
|
742
|
+
maxBuffer: 256 * 1024,
|
|
743
|
+
timeout: 3e4
|
|
744
|
+
});
|
|
745
|
+
if (child.error) {
|
|
746
|
+
throw child.error;
|
|
611
747
|
}
|
|
748
|
+
if (child.status !== 0) {
|
|
749
|
+
throw new Error(redactWalletDiagnosticText(String(child.stderr || "native signer readiness failed").trim()));
|
|
750
|
+
}
|
|
751
|
+
let result;
|
|
752
|
+
try {
|
|
753
|
+
result = JSON.parse(String(child.stdout ?? ""));
|
|
754
|
+
} catch {
|
|
755
|
+
throw new Error("native signer readiness returned invalid JSON");
|
|
756
|
+
}
|
|
757
|
+
const readiness = result;
|
|
758
|
+
if (!readiness || readiness.walletId !== params.walletId || typeof readiness.publicKey !== "string" || !Number.isSafeInteger(readiness.walletVersion) || typeof readiness.role !== "string" || !Number.isSafeInteger(readiness.policyVersion) || !Number.isSafeInteger(readiness.networkVersion) || typeof readiness.ready !== "boolean") {
|
|
759
|
+
throw new Error("native signer readiness returned an invalid result");
|
|
760
|
+
}
|
|
761
|
+
return readiness;
|
|
762
|
+
}
|
|
763
|
+
function invokeNativeSignerJSON(params) {
|
|
764
|
+
const child = spawnSync(params.signerBinPath, params.args, {
|
|
765
|
+
env: nativeSignerLifecycleEnv(params.env),
|
|
766
|
+
input: params.input,
|
|
767
|
+
stdio: [
|
|
768
|
+
params.input === undefined ? "ignore" : "pipe",
|
|
769
|
+
"pipe",
|
|
770
|
+
"pipe"
|
|
771
|
+
],
|
|
772
|
+
encoding: "utf8",
|
|
773
|
+
maxBuffer: 512 * 1024,
|
|
774
|
+
timeout: 12e4
|
|
775
|
+
});
|
|
776
|
+
if (child.error) {
|
|
777
|
+
throw child.error;
|
|
778
|
+
}
|
|
779
|
+
if (child.status !== 0) {
|
|
780
|
+
throw new Error(redactWalletDiagnosticText(String(child.stderr || `${params.label} failed`).trim()));
|
|
781
|
+
}
|
|
782
|
+
let parsed;
|
|
783
|
+
try {
|
|
784
|
+
parsed = JSON.parse(String(child.stdout ?? ""));
|
|
785
|
+
} catch {
|
|
786
|
+
throw new Error(`${params.label} returned invalid JSON`);
|
|
787
|
+
}
|
|
788
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
789
|
+
throw new Error(`${params.label} returned an invalid result`);
|
|
790
|
+
}
|
|
791
|
+
return parsed;
|
|
792
|
+
}
|
|
793
|
+
function invokeNativeSignerBalance(params) {
|
|
794
|
+
const result = invokeNativeSignerJSON({
|
|
795
|
+
signerBinPath: params.signerBinPath,
|
|
796
|
+
args: [
|
|
797
|
+
"admin",
|
|
798
|
+
"wallet",
|
|
799
|
+
"balance",
|
|
800
|
+
params.socketFlag,
|
|
801
|
+
params.socketPath,
|
|
802
|
+
"--wallet-id",
|
|
803
|
+
params.walletId
|
|
804
|
+
],
|
|
805
|
+
env: params.env,
|
|
806
|
+
label: "native signer balance lookup"
|
|
807
|
+
});
|
|
808
|
+
const balance = typeof result.balance === "string" ? result.balance : "";
|
|
809
|
+
if (result.address !== params.publicKey || result.chain !== "solana" || result.unit !== "lamports" || !/^\d+$/u.test(balance)) {
|
|
810
|
+
throw new Error("native signer balance lookup returned an invalid result");
|
|
811
|
+
}
|
|
812
|
+
return balance;
|
|
813
|
+
}
|
|
814
|
+
function parseNativeSignerRotation(value) {
|
|
815
|
+
if (typeof value.rotationId !== "string" || !/^sha256:[0-9a-f]{64}$/u.test(value.rotationId) || typeof value.sourceWalletId !== "string" || typeof value.sourcePublicKey !== "string" || typeof value.successorWalletId !== "string" || typeof value.successorPublicKey !== "string" || value.state !== "prepared" && value.state !== "committed" || !Number.isSafeInteger(value.version)) {
|
|
816
|
+
throw new Error("native signer rotation returned an invalid result");
|
|
817
|
+
}
|
|
818
|
+
return value;
|
|
819
|
+
}
|
|
820
|
+
function invokeNativeSignerRotationCreate(params) {
|
|
821
|
+
return parseNativeSignerRotation(invokeNativeSignerJSON({
|
|
822
|
+
signerBinPath: params.signerBinPath,
|
|
823
|
+
args: [
|
|
824
|
+
"admin",
|
|
825
|
+
"wallet",
|
|
826
|
+
"rotate-successor",
|
|
827
|
+
params.socketFlag,
|
|
828
|
+
params.socketPath,
|
|
829
|
+
"--wallet-id",
|
|
830
|
+
params.sourceWalletId,
|
|
831
|
+
"--successor-wallet-id",
|
|
832
|
+
params.successorWalletId,
|
|
833
|
+
"--expected-source-public-key",
|
|
834
|
+
params.sourcePublicKey,
|
|
835
|
+
"--expected-source-wallet-version",
|
|
836
|
+
String(params.sourceWalletVersion),
|
|
837
|
+
"--expected-source-policy-version",
|
|
838
|
+
String(params.sourcePolicyVersion)
|
|
839
|
+
],
|
|
840
|
+
env: params.env,
|
|
841
|
+
label: "native signer Mining successor preparation"
|
|
842
|
+
}));
|
|
843
|
+
}
|
|
844
|
+
function invokeNativeSignerRotationStatus(params) {
|
|
845
|
+
try {
|
|
846
|
+
return parseNativeSignerRotation(invokeNativeSignerJSON({
|
|
847
|
+
signerBinPath: params.signerBinPath,
|
|
848
|
+
args: [
|
|
849
|
+
"admin",
|
|
850
|
+
"wallet",
|
|
851
|
+
"rotation-status",
|
|
852
|
+
params.socketFlag,
|
|
853
|
+
params.socketPath,
|
|
854
|
+
"--wallet-id",
|
|
855
|
+
params.sourceWalletId
|
|
856
|
+
],
|
|
857
|
+
env: params.env,
|
|
858
|
+
label: "native signer Mining rotation status"
|
|
859
|
+
}));
|
|
860
|
+
} catch (error) {
|
|
861
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
862
|
+
if (/rotation.*not found/iu.test(message)) {
|
|
863
|
+
return null;
|
|
864
|
+
}
|
|
865
|
+
throw error;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
function invokeNativeSignerRotationCommit(params) {
|
|
869
|
+
const rotation = params.rotation;
|
|
870
|
+
return parseNativeSignerRotation(invokeNativeSignerJSON({
|
|
871
|
+
signerBinPath: params.signerBinPath,
|
|
872
|
+
args: [
|
|
873
|
+
"admin",
|
|
874
|
+
"wallet",
|
|
875
|
+
"rotation-commit",
|
|
876
|
+
params.socketFlag,
|
|
877
|
+
params.socketPath,
|
|
878
|
+
"--wallet-id",
|
|
879
|
+
rotation.sourceWalletId,
|
|
880
|
+
"--successor-wallet-id",
|
|
881
|
+
rotation.successorWalletId,
|
|
882
|
+
"--rotation-id",
|
|
883
|
+
rotation.rotationId,
|
|
884
|
+
"--expected-source-public-key",
|
|
885
|
+
rotation.sourcePublicKey,
|
|
886
|
+
"--expected-successor-public-key",
|
|
887
|
+
rotation.successorPublicKey,
|
|
888
|
+
"--expected-source-wallet-version",
|
|
889
|
+
String(rotation.prepareExpectedSourceWalletVersion),
|
|
890
|
+
"--expected-source-policy-version",
|
|
891
|
+
String(rotation.prepareExpectedSourcePolicyVersion),
|
|
892
|
+
"--expected-successor-wallet-version",
|
|
893
|
+
"1",
|
|
894
|
+
"--expected-successor-policy-version",
|
|
895
|
+
"1",
|
|
896
|
+
"--expected-rotation-version",
|
|
897
|
+
"1",
|
|
898
|
+
"--expected-successor-network-version",
|
|
899
|
+
String(params.successorNetworkVersion),
|
|
900
|
+
"--expected-successor-network-hash",
|
|
901
|
+
params.successorNetworkHash
|
|
902
|
+
],
|
|
903
|
+
input: JSON.stringify({
|
|
904
|
+
recoveryPackageHash: params.recoveryPackageHash,
|
|
905
|
+
safetyEvidence: params.safetyEvidence
|
|
906
|
+
}),
|
|
907
|
+
env: params.env,
|
|
908
|
+
label: "native signer Mining retirement commit"
|
|
909
|
+
}));
|
|
910
|
+
}
|
|
911
|
+
function invokeNativeSignerRecoveryImport(params) {
|
|
612
912
|
const input = requireOwnerOnlySignerImportFile(params.recoveryFile);
|
|
613
913
|
try {
|
|
614
914
|
const child = spawnSync(params.signerBinPath, [
|
|
615
915
|
"admin",
|
|
616
916
|
"wallet",
|
|
617
917
|
"recovery-import",
|
|
618
|
-
"--control-socket",
|
|
918
|
+
params.hosted ? "--operator-socket" : "--control-socket",
|
|
619
919
|
params.controlSocketPath,
|
|
620
920
|
"--wallet-id",
|
|
621
921
|
params.walletId,
|
|
622
|
-
"--
|
|
922
|
+
"--baseline-role",
|
|
623
923
|
params.role,
|
|
624
924
|
"--recovery-file",
|
|
625
925
|
input.path
|
|
@@ -678,7 +978,7 @@ async function importSignerOwnedWalletForSetup(params) {
|
|
|
678
978
|
throw new Error(`native signer wallet ID ${signerWalletId} is already registered as ${collision.id}; choose a distinct wallet ID`);
|
|
679
979
|
}
|
|
680
980
|
const signerBinPath = hosted ? "/opt/fased/signer/fased-signerd" : resolveSignerdBinaryPath(mergedEnv);
|
|
681
|
-
const controlSocketPath = hosted ? "/run/fased-signerd/
|
|
981
|
+
const controlSocketPath = hosted ? "/run/fased-signerd/operator.sock" : resolveLocalSignerControlSocketPath(mergedEnv);
|
|
682
982
|
if (!hosted) {
|
|
683
983
|
writeLocalSignerEnvFile({
|
|
684
984
|
config: cfg,
|
|
@@ -706,7 +1006,15 @@ async function importSignerOwnedWalletForSetup(params) {
|
|
|
706
1006
|
importFile: params.importFile,
|
|
707
1007
|
env: mergedEnv
|
|
708
1008
|
});
|
|
709
|
-
const network =
|
|
1009
|
+
const network = hosted ? invokeNativeSignerNetworkSetPrimary({
|
|
1010
|
+
signerBinPath,
|
|
1011
|
+
socketFlag: "--operator-socket",
|
|
1012
|
+
socketPath: controlSocketPath,
|
|
1013
|
+
walletId: signerWalletId,
|
|
1014
|
+
primaryRpcUrl: params.rpcUrl,
|
|
1015
|
+
expectedVersion: 0,
|
|
1016
|
+
env: mergedEnv
|
|
1017
|
+
}) : await configureSignerOwnedWalletNetwork({
|
|
710
1018
|
walletId: signerWalletId,
|
|
711
1019
|
primaryRpcUrl: params.rpcUrl,
|
|
712
1020
|
env: mergedEnv,
|
|
@@ -714,6 +1022,19 @@ async function importSignerOwnedWalletForSetup(params) {
|
|
|
714
1022
|
});
|
|
715
1023
|
cfg = setConfigEnvVar(cfg, rpcEnvKeyFor(params.chain, params.walletId), params.rpcUrl);
|
|
716
1024
|
await writeConfigFile(cfg);
|
|
1025
|
+
const readiness = hosted ? invokeNativeSignerWalletReadiness({
|
|
1026
|
+
signerBinPath,
|
|
1027
|
+
socketFlag: "--operator-socket",
|
|
1028
|
+
socketPath: controlSocketPath,
|
|
1029
|
+
walletId: signerWalletId,
|
|
1030
|
+
env: mergedEnv
|
|
1031
|
+
}) : await readSignerOwnedWalletReadiness({
|
|
1032
|
+
socketPath,
|
|
1033
|
+
walletId: signerWalletId
|
|
1034
|
+
});
|
|
1035
|
+
if (!readiness.ready || readiness.publicKey !== result.wallet.publicKey || readiness.role !== params.role || readiness.baselineVersion !== 1 || readiness.policyHash !== result.policy.hash || readiness.policyVersion !== result.policy.version || readiness.networkHash !== network.hash || readiness.networkVersion !== network.version) {
|
|
1036
|
+
throw new Error(`Setup incomplete: signer readiness does not match the imported ${params.role} wallet lifecycle`);
|
|
1037
|
+
}
|
|
717
1038
|
const wallet = upsertNamedWallet({
|
|
718
1039
|
walletId: params.walletId,
|
|
719
1040
|
name: params.options.walletName || "Wallet",
|
|
@@ -726,19 +1047,16 @@ async function importSignerOwnedWalletForSetup(params) {
|
|
|
726
1047
|
signerWalletId,
|
|
727
1048
|
policyHash: result.policy.hash,
|
|
728
1049
|
policyVersion: result.policy.version,
|
|
729
|
-
|
|
1050
|
+
baselineVersion: readiness.baselineVersion,
|
|
1051
|
+
policyState: "ready",
|
|
730
1052
|
networkHash: network.hash,
|
|
731
1053
|
networkVersion: network.version,
|
|
732
|
-
networkReady: network.ready
|
|
1054
|
+
networkReady: network.ready,
|
|
1055
|
+
operationLane: readiness.operationLane,
|
|
1056
|
+
roleReady: readiness.ready
|
|
733
1057
|
},
|
|
734
1058
|
env: params.env
|
|
735
1059
|
});
|
|
736
|
-
if (params.role === "agent") {
|
|
737
|
-
setDefaultWallet({
|
|
738
|
-
walletId: wallet.id,
|
|
739
|
-
env: params.env
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
1060
|
if (params.role === "mining") {
|
|
743
1061
|
const currentEntry = cfg.plugins?.entries?.["sat-mining"];
|
|
744
1062
|
const currentConfig = currentEntry?.config && typeof currentEntry.config === "object" && !Array.isArray(currentEntry.config) ? currentEntry.config : {};
|
|
@@ -770,14 +1088,17 @@ async function importSignerOwnedWalletForSetup(params) {
|
|
|
770
1088
|
signerWalletId,
|
|
771
1089
|
role: params.role,
|
|
772
1090
|
address: result.wallet.publicKey,
|
|
773
|
-
|
|
1091
|
+
baselineVersion: readiness.baselineVersion,
|
|
1092
|
+
policyState: "ready",
|
|
774
1093
|
networkVersion: network.version,
|
|
775
|
-
networkReady: network.ready
|
|
1094
|
+
networkReady: network.ready,
|
|
1095
|
+
operationLane: readiness.operationLane,
|
|
1096
|
+
roleReady: readiness.ready
|
|
776
1097
|
}, null, 2));
|
|
777
1098
|
} else {
|
|
778
1099
|
params.runtime.log(`Imported ${params.role} wallet ${wallet.id} into fased-signerd.`);
|
|
779
1100
|
params.runtime.log(`SOLANA address: ${result.wallet.publicKey}`);
|
|
780
|
-
params.runtime.log(`Readiness:
|
|
1101
|
+
params.runtime.log(`Readiness: ${readiness.ready ? "ready" : "setup incomplete"} (${readiness.operationLane})`);
|
|
781
1102
|
}
|
|
782
1103
|
return result;
|
|
783
1104
|
}
|
|
@@ -1035,6 +1356,71 @@ async function walletSetupCommand(runtime = defaultRuntime, options = {}) {
|
|
|
1035
1356
|
return;
|
|
1036
1357
|
}
|
|
1037
1358
|
}
|
|
1359
|
+
async function walletRpcSetCommand(runtime = defaultRuntime, options) {
|
|
1360
|
+
const walletId = options.walletId.trim();
|
|
1361
|
+
const rpcUrl = options.rpcUrl.trim();
|
|
1362
|
+
if (!walletId) {
|
|
1363
|
+
throw new Error("--wallet-id is required");
|
|
1364
|
+
}
|
|
1365
|
+
if (!rpcUrl) {
|
|
1366
|
+
throw new Error("--rpc-url is required");
|
|
1367
|
+
}
|
|
1368
|
+
const cfg = loadConfig();
|
|
1369
|
+
const effectiveEnv = {
|
|
1370
|
+
...process.env,
|
|
1371
|
+
...cfg.env?.vars
|
|
1372
|
+
};
|
|
1373
|
+
const registry = readWalletProviderRegistry(effectiveEnv);
|
|
1374
|
+
const wallet = registry.wallets.find((entry) => entry.id === walletId);
|
|
1375
|
+
if (!wallet || wallet.providerId !== "local-socket-signer") {
|
|
1376
|
+
throw new Error(`native signer wallet not found: ${walletId}`);
|
|
1377
|
+
}
|
|
1378
|
+
const signerWalletId = typeof wallet.metadata?.signerWalletId === "string" && wallet.metadata.signerWalletId.trim() ? wallet.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(wallet.id);
|
|
1379
|
+
const hosted = String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting";
|
|
1380
|
+
const signerBinPath = hosted ? "/opt/fased/signer/fased-signerd" : resolveSignerdBinaryPath(effectiveEnv);
|
|
1381
|
+
const socketFlag = hosted ? "--operator-socket" : "--control-socket";
|
|
1382
|
+
const socketPath = hosted ? "/run/fased-signerd/operator.sock" : resolveLocalSignerControlSocketPath(effectiveEnv);
|
|
1383
|
+
const current = hosted ? invokeNativeSignerWalletReadiness({
|
|
1384
|
+
signerBinPath,
|
|
1385
|
+
socketFlag,
|
|
1386
|
+
socketPath,
|
|
1387
|
+
walletId: signerWalletId,
|
|
1388
|
+
env: effectiveEnv
|
|
1389
|
+
}) : await readSignerOwnedWalletReadiness({
|
|
1390
|
+
socketPath: resolveLocalSignerSocketPath(effectiveEnv),
|
|
1391
|
+
walletId: signerWalletId
|
|
1392
|
+
});
|
|
1393
|
+
const network = invokeNativeSignerNetworkSetPrimary({
|
|
1394
|
+
signerBinPath,
|
|
1395
|
+
socketFlag,
|
|
1396
|
+
socketPath,
|
|
1397
|
+
walletId: signerWalletId,
|
|
1398
|
+
primaryRpcUrl: rpcUrl,
|
|
1399
|
+
expectedVersion: current.networkVersion,
|
|
1400
|
+
env: effectiveEnv
|
|
1401
|
+
});
|
|
1402
|
+
const nextConfig = setConfigEnvVar(cfg, rpcEnvKeyFor("solana", walletId), rpcUrl);
|
|
1403
|
+
await writeConfigFile(nextConfig);
|
|
1404
|
+
registry.wallets = registry.wallets.map((entry) => entry.id === wallet.id ? {
|
|
1405
|
+
...entry,
|
|
1406
|
+
metadata: {
|
|
1407
|
+
...entry.metadata,
|
|
1408
|
+
networkHash: network.hash,
|
|
1409
|
+
networkVersion: network.version,
|
|
1410
|
+
networkReady: network.ready
|
|
1411
|
+
},
|
|
1412
|
+
updatedAt: new Date().toISOString()
|
|
1413
|
+
} : entry);
|
|
1414
|
+
writeWalletProviderRegistry(registry, effectiveEnv);
|
|
1415
|
+
if (options.json) {
|
|
1416
|
+
runtime.log(JSON.stringify({
|
|
1417
|
+
ok: true,
|
|
1418
|
+
network
|
|
1419
|
+
}, null, 2));
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
runtime.log(`Primary Solana RPC verified for ${wallet.name} (${wallet.id}); signer network v${network.version} is ready.`);
|
|
1423
|
+
}
|
|
1038
1424
|
async function walletRecoveryExportCommand(runtime = defaultRuntime, options) {
|
|
1039
1425
|
const walletId = options.walletId.trim();
|
|
1040
1426
|
const output = options.output.trim();
|
|
@@ -1058,18 +1444,15 @@ async function walletRecoveryExportCommand(runtime = defaultRuntime, options) {
|
|
|
1058
1444
|
throw new Error(`native signer wallet ${walletId} has no verified Solana public address`);
|
|
1059
1445
|
}
|
|
1060
1446
|
const hosted = String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting";
|
|
1061
|
-
if (hosted) {
|
|
1062
|
-
throw new Error("Hosting recovery export remains root/signer-owner only so a compromised Gateway cannot choose the encryption password and export a wallet. Use the provider root console and fased-signerd admin wallet recovery-export until a signer-owned owner-authorization ceremony is configured.");
|
|
1063
|
-
}
|
|
1064
1447
|
const signerWalletId = typeof wallet.metadata?.signerWalletId === "string" && wallet.metadata.signerWalletId.trim() ? wallet.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(wallet.id);
|
|
1065
|
-
const signerBinPath = resolveSignerdBinaryPath(effectiveEnv);
|
|
1066
|
-
const controlSocketPath = resolveLocalSignerControlSocketPath(effectiveEnv);
|
|
1448
|
+
const signerBinPath = hosted ? "/opt/fased/signer/fased-signerd" : resolveSignerdBinaryPath(effectiveEnv);
|
|
1449
|
+
const controlSocketPath = hosted ? "/run/fased-signerd/operator.sock" : resolveLocalSignerControlSocketPath(effectiveEnv);
|
|
1067
1450
|
runtime.log(`Creating encrypted recovery package for ${wallet.id}. The signer will ask for and confirm the recovery password without echoing it.`);
|
|
1068
1451
|
const child = spawnSync(signerBinPath, [
|
|
1069
1452
|
"admin",
|
|
1070
1453
|
"wallet",
|
|
1071
1454
|
"recovery-export",
|
|
1072
|
-
"--control-socket",
|
|
1455
|
+
hosted ? "--operator-socket" : "--control-socket",
|
|
1073
1456
|
controlSocketPath,
|
|
1074
1457
|
"--wallet-id",
|
|
1075
1458
|
signerWalletId,
|
|
@@ -1109,6 +1492,270 @@ async function walletRecoveryImportCommand(runtime = defaultRuntime, options) {
|
|
|
1109
1492
|
noSignerHints: true
|
|
1110
1493
|
});
|
|
1111
1494
|
}
|
|
1495
|
+
async function walletRetireCommand(runtime = defaultRuntime, options) {
|
|
1496
|
+
const walletId = options.walletId.trim();
|
|
1497
|
+
const successorWalletId = options.successorWalletId.trim();
|
|
1498
|
+
const successorWalletName = options.successorWalletName.trim();
|
|
1499
|
+
const rpcUrl = options.rpcUrl.trim();
|
|
1500
|
+
if (!walletId || !successorWalletId || !successorWalletName || !rpcUrl) {
|
|
1501
|
+
throw new Error("--wallet-id, --successor-wallet-id, --successor-wallet-name, and --rpc-url are required");
|
|
1502
|
+
}
|
|
1503
|
+
if (walletId === successorWalletId) {
|
|
1504
|
+
throw new Error("Mining successor wallet id must differ from the retired wallet id");
|
|
1505
|
+
}
|
|
1506
|
+
if (!/^[a-zA-Z0-9_-]+$/u.test(successorWalletId)) {
|
|
1507
|
+
throw new Error("--successor-wallet-id must contain only letters, numbers, hyphens, or underscores");
|
|
1508
|
+
}
|
|
1509
|
+
const cfg = loadConfig();
|
|
1510
|
+
const effectiveEnv = {
|
|
1511
|
+
...process.env,
|
|
1512
|
+
...cfg.env?.vars
|
|
1513
|
+
};
|
|
1514
|
+
const registry = readWalletProviderRegistry(effectiveEnv);
|
|
1515
|
+
const source = registry.wallets.find((entry) => entry.id === walletId);
|
|
1516
|
+
if (!source || source.providerId !== "local-socket-signer" || resolveWalletUserRole(source) !== "mining") {
|
|
1517
|
+
throw new Error(`active signer-owned Mining wallet not found: ${walletId}`);
|
|
1518
|
+
}
|
|
1519
|
+
const configuredMiningWalletId = resolveConfiguredMiningWalletId(cfg);
|
|
1520
|
+
if (configuredMiningWalletId !== walletId && configuredMiningWalletId !== successorWalletId) {
|
|
1521
|
+
throw new Error(`${walletId} is not the active singleton Mining wallet`);
|
|
1522
|
+
}
|
|
1523
|
+
if (registry.wallets.some((entry) => entry.id === successorWalletId)) {
|
|
1524
|
+
throw new Error(`successor wallet id is already registered: ${successorWalletId}`);
|
|
1525
|
+
}
|
|
1526
|
+
const sourcePublicKey = source.addresses?.solana?.trim() ?? "";
|
|
1527
|
+
if (!sourcePublicKey) {
|
|
1528
|
+
throw new Error("source Mining wallet has no verified Solana address");
|
|
1529
|
+
}
|
|
1530
|
+
const sourceSignerWalletId = typeof source.metadata?.signerWalletId === "string" && source.metadata.signerWalletId.trim() ? source.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(source.id);
|
|
1531
|
+
const successorSignerWalletId = normalizeNativeSignerWalletId(successorWalletId);
|
|
1532
|
+
if (sourceSignerWalletId === successorSignerWalletId) {
|
|
1533
|
+
throw new Error("Mining successor signer wallet id must be distinct");
|
|
1534
|
+
}
|
|
1535
|
+
const recovery = verifyMiningRecoveryPackage({
|
|
1536
|
+
recoveryFile: options.recoveryFile,
|
|
1537
|
+
walletId: sourceSignerWalletId,
|
|
1538
|
+
publicKey: sourcePublicKey
|
|
1539
|
+
});
|
|
1540
|
+
const hosted = String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting";
|
|
1541
|
+
const signerBinPath = hosted ? "/opt/fased/signer/fased-signerd" : resolveSignerdBinaryPath(effectiveEnv);
|
|
1542
|
+
const socketFlag = hosted ? "--operator-socket" : "--control-socket";
|
|
1543
|
+
const socketPath = hosted ? "/run/fased-signerd/operator.sock" : resolveLocalSignerControlSocketPath(effectiveEnv);
|
|
1544
|
+
let rotation = invokeNativeSignerRotationStatus({
|
|
1545
|
+
signerBinPath,
|
|
1546
|
+
socketFlag,
|
|
1547
|
+
socketPath,
|
|
1548
|
+
sourceWalletId: sourceSignerWalletId,
|
|
1549
|
+
env: effectiveEnv
|
|
1550
|
+
});
|
|
1551
|
+
if (rotation && rotation.successorWalletId !== successorSignerWalletId) {
|
|
1552
|
+
throw new Error(`source wallet is already bound to immutable successor ${rotation.successorWalletId}`);
|
|
1553
|
+
}
|
|
1554
|
+
if (!rotation && configuredMiningWalletId !== walletId) {
|
|
1555
|
+
throw new Error("Mining configuration changed before the signer prepared a successor");
|
|
1556
|
+
}
|
|
1557
|
+
let evidence;
|
|
1558
|
+
if (!rotation || rotation.state !== "committed") {
|
|
1559
|
+
const sourceReadiness = invokeNativeSignerWalletReadiness({
|
|
1560
|
+
signerBinPath,
|
|
1561
|
+
socketFlag,
|
|
1562
|
+
socketPath,
|
|
1563
|
+
walletId: sourceSignerWalletId,
|
|
1564
|
+
env: effectiveEnv
|
|
1565
|
+
});
|
|
1566
|
+
if (sourceReadiness.publicKey !== sourcePublicKey || sourceReadiness.role !== "mining" || !sourceReadiness.keyReady || !sourceReadiness.policyReady || !sourceReadiness.networkReady) {
|
|
1567
|
+
throw new Error("source Mining wallet is not live and role-ready in the signer");
|
|
1568
|
+
}
|
|
1569
|
+
const signerSOL = invokeNativeSignerBalance({
|
|
1570
|
+
signerBinPath,
|
|
1571
|
+
socketFlag,
|
|
1572
|
+
socketPath,
|
|
1573
|
+
walletId: sourceSignerWalletId,
|
|
1574
|
+
publicKey: sourcePublicKey,
|
|
1575
|
+
env: effectiveEnv
|
|
1576
|
+
});
|
|
1577
|
+
evidence = buildMiningRetirementEvidence({
|
|
1578
|
+
walletId,
|
|
1579
|
+
signerWalletId: sourceSignerWalletId,
|
|
1580
|
+
publicKey: sourcePublicKey,
|
|
1581
|
+
signerSolBalanceLamports: signerSOL,
|
|
1582
|
+
liveStatus: options.liveMiningStatus,
|
|
1583
|
+
env: effectiveEnv
|
|
1584
|
+
});
|
|
1585
|
+
rotation ??= invokeNativeSignerRotationCreate({
|
|
1586
|
+
signerBinPath,
|
|
1587
|
+
socketFlag,
|
|
1588
|
+
socketPath,
|
|
1589
|
+
sourceWalletId: sourceSignerWalletId,
|
|
1590
|
+
successorWalletId: successorSignerWalletId,
|
|
1591
|
+
sourcePublicKey,
|
|
1592
|
+
sourceWalletVersion: Number(sourceReadiness.walletVersion),
|
|
1593
|
+
sourcePolicyVersion: sourceReadiness.policyVersion,
|
|
1594
|
+
env: effectiveEnv
|
|
1595
|
+
});
|
|
1596
|
+
if (rotation.role !== "mining" || rotation.sourcePublicKey !== sourcePublicKey || rotation.successorPublicKey === sourcePublicKey) {
|
|
1597
|
+
throw new Error("signer prepared an invalid Mining successor binding");
|
|
1598
|
+
}
|
|
1599
|
+
let successorReadiness = invokeNativeSignerWalletReadiness({
|
|
1600
|
+
signerBinPath,
|
|
1601
|
+
socketFlag,
|
|
1602
|
+
socketPath,
|
|
1603
|
+
walletId: successorSignerWalletId,
|
|
1604
|
+
env: effectiveEnv
|
|
1605
|
+
});
|
|
1606
|
+
let successorNetwork = {
|
|
1607
|
+
version: successorReadiness.networkVersion,
|
|
1608
|
+
hash: successorReadiness.networkHash ?? "",
|
|
1609
|
+
ready: successorReadiness.networkReady
|
|
1610
|
+
};
|
|
1611
|
+
if (successorNetwork.version === 0) {
|
|
1612
|
+
successorNetwork = invokeNativeSignerNetworkSetPrimary({
|
|
1613
|
+
signerBinPath,
|
|
1614
|
+
socketFlag,
|
|
1615
|
+
socketPath,
|
|
1616
|
+
walletId: successorSignerWalletId,
|
|
1617
|
+
primaryRpcUrl: rpcUrl,
|
|
1618
|
+
expectedVersion: 0,
|
|
1619
|
+
env: effectiveEnv
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
if (!successorNetwork.ready || !successorNetwork.hash) {
|
|
1623
|
+
throw new Error("Mining successor RPC is not verified and ready");
|
|
1624
|
+
}
|
|
1625
|
+
rotation = invokeNativeSignerRotationCommit({
|
|
1626
|
+
signerBinPath,
|
|
1627
|
+
socketFlag,
|
|
1628
|
+
socketPath,
|
|
1629
|
+
rotation,
|
|
1630
|
+
successorNetworkVersion: successorNetwork.version,
|
|
1631
|
+
successorNetworkHash: successorNetwork.hash,
|
|
1632
|
+
recoveryPackageHash: recovery.packageHash,
|
|
1633
|
+
safetyEvidence: evidence,
|
|
1634
|
+
env: effectiveEnv
|
|
1635
|
+
});
|
|
1636
|
+
successorReadiness = invokeNativeSignerWalletReadiness({
|
|
1637
|
+
signerBinPath,
|
|
1638
|
+
socketFlag,
|
|
1639
|
+
socketPath,
|
|
1640
|
+
walletId: successorSignerWalletId,
|
|
1641
|
+
env: effectiveEnv
|
|
1642
|
+
});
|
|
1643
|
+
if (rotation.state !== "committed" || successorReadiness.publicKey !== rotation.successorPublicKey || successorReadiness.role !== "mining" || successorReadiness.operationLane !== "mining-typed-sat" || !successorReadiness.ready || successorReadiness.policyHash !== rotation.successorActivatedPolicyHash) {
|
|
1644
|
+
throw new Error("Mining successor did not become authoritatively role-ready after retirement");
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
if (rotation.state !== "committed" || rotation.recoveryPackageHash !== recovery.packageHash || !rotation.sourceRetiredPolicyHash || !rotation.successorActivatedPolicyHash || !rotation.safetyEvidenceHash) {
|
|
1648
|
+
throw new Error("signer retirement acknowledgement is incomplete");
|
|
1649
|
+
}
|
|
1650
|
+
const successorReadiness = invokeNativeSignerWalletReadiness({
|
|
1651
|
+
signerBinPath,
|
|
1652
|
+
socketFlag,
|
|
1653
|
+
socketPath,
|
|
1654
|
+
walletId: successorSignerWalletId,
|
|
1655
|
+
env: effectiveEnv
|
|
1656
|
+
});
|
|
1657
|
+
if (successorReadiness.publicKey !== rotation.successorPublicKey || successorReadiness.role !== "mining" || !successorReadiness.ready) {
|
|
1658
|
+
throw new Error("committed Mining successor is not live and ready");
|
|
1659
|
+
}
|
|
1660
|
+
const receipt = {
|
|
1661
|
+
version: 1,
|
|
1662
|
+
kind: "fased-mining-wallet-retirement",
|
|
1663
|
+
rotationId: rotation.rotationId,
|
|
1664
|
+
committedAt: rotation.committedAt,
|
|
1665
|
+
sourceWalletId: walletId,
|
|
1666
|
+
sourceSignerWalletId,
|
|
1667
|
+
sourcePublicKey,
|
|
1668
|
+
sourceRetiredPolicyVersion: rotation.sourceRetiredPolicyVersion,
|
|
1669
|
+
sourceRetiredPolicyHash: rotation.sourceRetiredPolicyHash,
|
|
1670
|
+
successorWalletId,
|
|
1671
|
+
successorSignerWalletId,
|
|
1672
|
+
successorPublicKey: rotation.successorPublicKey,
|
|
1673
|
+
successorPolicyVersion: successorReadiness.policyVersion,
|
|
1674
|
+
successorPolicyHash: successorReadiness.policyHash,
|
|
1675
|
+
successorNetworkVersion: successorReadiness.networkVersion,
|
|
1676
|
+
successorNetworkHash: successorReadiness.networkHash,
|
|
1677
|
+
recoveryPackageHash: rotation.recoveryPackageHash,
|
|
1678
|
+
safetyEvidenceHash: rotation.safetyEvidenceHash,
|
|
1679
|
+
balances: evidence ?? rotation.safetyEvidence ? {
|
|
1680
|
+
solBalanceLamports: (evidence ?? rotation.safetyEvidence).solBalanceLamports,
|
|
1681
|
+
satBalanceRaw: (evidence ?? rotation.safetyEvidence).satBalanceRaw
|
|
1682
|
+
} : undefined
|
|
1683
|
+
};
|
|
1684
|
+
const receiptPath = writeMiningRetirementReceipt({
|
|
1685
|
+
sourceWalletId: walletId,
|
|
1686
|
+
receipt,
|
|
1687
|
+
env: effectiveEnv
|
|
1688
|
+
});
|
|
1689
|
+
const currentEntry = cfg.plugins?.entries?.["sat-mining"];
|
|
1690
|
+
const currentPluginConfig = currentEntry?.config && typeof currentEntry.config === "object" && !Array.isArray(currentEntry.config) ? currentEntry.config : {};
|
|
1691
|
+
await writeConfigFile({
|
|
1692
|
+
...cfg,
|
|
1693
|
+
plugins: {
|
|
1694
|
+
...cfg.plugins,
|
|
1695
|
+
entries: {
|
|
1696
|
+
...cfg.plugins?.entries,
|
|
1697
|
+
"sat-mining": {
|
|
1698
|
+
...currentEntry,
|
|
1699
|
+
enabled: true,
|
|
1700
|
+
config: {
|
|
1701
|
+
...currentPluginConfig,
|
|
1702
|
+
walletId: successorWalletId
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
});
|
|
1708
|
+
replaceRetiredMiningWallet({
|
|
1709
|
+
sourceWalletId: walletId,
|
|
1710
|
+
signerAcknowledgement: {
|
|
1711
|
+
rotationId: rotation.rotationId,
|
|
1712
|
+
sourceRetiredPolicyHash: String(rotation.sourceRetiredPolicyHash),
|
|
1713
|
+
successorPublicKey: rotation.successorPublicKey,
|
|
1714
|
+
successorPolicyHash: successorReadiness.policyHash
|
|
1715
|
+
},
|
|
1716
|
+
successor: {
|
|
1717
|
+
id: successorWalletId,
|
|
1718
|
+
name: successorWalletName,
|
|
1719
|
+
providerId: "local-socket-signer",
|
|
1720
|
+
addresses: { solana: rotation.successorPublicKey },
|
|
1721
|
+
metadata: {
|
|
1722
|
+
role: "mining",
|
|
1723
|
+
purpose: "mining",
|
|
1724
|
+
keyAuthority: "signer-owned-v2",
|
|
1725
|
+
signerWalletId: successorSignerWalletId,
|
|
1726
|
+
policyHash: successorReadiness.policyHash,
|
|
1727
|
+
policyVersion: successorReadiness.policyVersion,
|
|
1728
|
+
baselineVersion: successorReadiness.baselineVersion,
|
|
1729
|
+
policyState: "ready",
|
|
1730
|
+
networkHash: successorReadiness.networkHash,
|
|
1731
|
+
networkVersion: successorReadiness.networkVersion,
|
|
1732
|
+
networkReady: true,
|
|
1733
|
+
operationLane: successorReadiness.operationLane,
|
|
1734
|
+
roleReady: true,
|
|
1735
|
+
predecessorWalletId: walletId,
|
|
1736
|
+
rotationId: rotation.rotationId
|
|
1737
|
+
}
|
|
1738
|
+
},
|
|
1739
|
+
env: effectiveEnv
|
|
1740
|
+
});
|
|
1741
|
+
const result = {
|
|
1742
|
+
ok: true,
|
|
1743
|
+
retiredWalletId: walletId,
|
|
1744
|
+
retiredAddress: sourcePublicKey,
|
|
1745
|
+
successorWalletId,
|
|
1746
|
+
successorAddress: rotation.successorPublicKey,
|
|
1747
|
+
rotationId: rotation.rotationId,
|
|
1748
|
+
receiptPath
|
|
1749
|
+
};
|
|
1750
|
+
if (options.json) {
|
|
1751
|
+
runtime.log(JSON.stringify(result, null, 2));
|
|
1752
|
+
} else {
|
|
1753
|
+
runtime.log(`Retired Mining wallet ${walletId} (${sourcePublicKey}).`);
|
|
1754
|
+
runtime.log(`Active Mining successor: ${successorWalletId} (${rotation.successorPublicKey}).`);
|
|
1755
|
+
runtime.log(`Audit receipt: ${receiptPath}`);
|
|
1756
|
+
}
|
|
1757
|
+
return result;
|
|
1758
|
+
}
|
|
1112
1759
|
async function walletRawExportCommand(runtime = defaultRuntime, options) {
|
|
1113
1760
|
const walletId = options.walletId.trim();
|
|
1114
1761
|
const output = options.output.trim();
|
|
@@ -1134,17 +1781,15 @@ async function walletRawExportCommand(runtime = defaultRuntime, options) {
|
|
|
1134
1781
|
if (!publicKey) {
|
|
1135
1782
|
throw new Error(`native signer wallet ${walletId} has no verified Solana public address`);
|
|
1136
1783
|
}
|
|
1137
|
-
|
|
1138
|
-
throw new Error("Hosting raw-key export remains signer-owner only. Run the native signer admin command from the provider root console so the Gateway cannot export custody material.");
|
|
1139
|
-
}
|
|
1784
|
+
const hosted = String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting";
|
|
1140
1785
|
const signerWalletId = typeof wallet.metadata?.signerWalletId === "string" && wallet.metadata.signerWalletId.trim() ? wallet.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(wallet.id);
|
|
1141
1786
|
runtime.log("WARNING: raw private-key export reduces signer custody protection.");
|
|
1142
|
-
const child = spawnSync(resolveSignerdBinaryPath(effectiveEnv), [
|
|
1787
|
+
const child = spawnSync(hosted ? "/opt/fased/signer/fased-signerd" : resolveSignerdBinaryPath(effectiveEnv), [
|
|
1143
1788
|
"admin",
|
|
1144
1789
|
"wallet",
|
|
1145
1790
|
"export-raw",
|
|
1146
|
-
"--control-socket",
|
|
1147
|
-
resolveLocalSignerControlSocketPath(effectiveEnv),
|
|
1791
|
+
hosted ? "--operator-socket" : "--control-socket",
|
|
1792
|
+
hosted ? "/run/fased-signerd/operator.sock" : resolveLocalSignerControlSocketPath(effectiveEnv),
|
|
1148
1793
|
"--wallet-id",
|
|
1149
1794
|
signerWalletId,
|
|
1150
1795
|
"--expected-public-key",
|
|
@@ -1217,10 +1862,56 @@ async function walletRoleSetCommand(runtime = defaultRuntime, options) {
|
|
|
1217
1862
|
}, null, 2));
|
|
1218
1863
|
return;
|
|
1219
1864
|
}
|
|
1220
|
-
runtime.log(`${wallet?.name ?? walletId} (${walletId}) set to ${role === "agent" ? "Agent wallet" : "Vault wallet"}${primary ? " and
|
|
1865
|
+
runtime.log(`${wallet?.name ?? walletId} (${walletId}) set to ${role === "agent" ? "Agent wallet" : "Vault wallet"}${primary ? " and Default Agent wallet fallback" : ""}.`);
|
|
1221
1866
|
}
|
|
1222
1867
|
async function walletStatusCommand(runtime = defaultRuntime, options = {}) {
|
|
1223
|
-
const
|
|
1868
|
+
const config = loadConfig();
|
|
1869
|
+
const effectiveEnv = {
|
|
1870
|
+
...process.env,
|
|
1871
|
+
...config.env?.vars
|
|
1872
|
+
};
|
|
1873
|
+
const hostedOperator = String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting" && effectiveEnv.FASED_GATEWAY_SERVICE !== "1";
|
|
1874
|
+
if (hostedOperator) {
|
|
1875
|
+
const registered = readWalletProviderRegistry(effectiveEnv).wallets.filter((wallet) => wallet.providerId === "local-socket-signer" && (!options.walletId || wallet.id === options.walletId));
|
|
1876
|
+
if (options.walletId && registered.length === 0) {
|
|
1877
|
+
throw new Error(`native signer wallet not found: ${options.walletId}`);
|
|
1878
|
+
}
|
|
1879
|
+
const wallets = registered.map((wallet) => {
|
|
1880
|
+
const signerWalletId = typeof wallet.metadata?.signerWalletId === "string" && wallet.metadata.signerWalletId.trim() ? wallet.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(wallet.id);
|
|
1881
|
+
return {
|
|
1882
|
+
id: wallet.id,
|
|
1883
|
+
name: wallet.name,
|
|
1884
|
+
signer: invokeNativeSignerWalletReadiness({
|
|
1885
|
+
signerBinPath: "/opt/fased/signer/fased-signerd",
|
|
1886
|
+
socketFlag: "--operator-socket",
|
|
1887
|
+
socketPath: "/run/fased-signerd/operator.sock",
|
|
1888
|
+
walletId: signerWalletId,
|
|
1889
|
+
env: effectiveEnv
|
|
1890
|
+
})
|
|
1891
|
+
};
|
|
1892
|
+
});
|
|
1893
|
+
if (options.json) {
|
|
1894
|
+
runtime.log(JSON.stringify({
|
|
1895
|
+
ok: true,
|
|
1896
|
+
status: {
|
|
1897
|
+
mode: "hosting-operator",
|
|
1898
|
+
wallets
|
|
1899
|
+
}
|
|
1900
|
+
}, null, 2));
|
|
1901
|
+
return;
|
|
1902
|
+
}
|
|
1903
|
+
if (wallets.length === 0) {
|
|
1904
|
+
runtime.log("No signer-owned wallets are registered.");
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
for (const wallet of wallets) {
|
|
1908
|
+
runtime.log(`Wallet ${wallet.name} (${wallet.id}): ${wallet.signer.ready ? "ready" : "setup incomplete"}`);
|
|
1909
|
+
runtime.log(` signer=${wallet.signer.walletId} role=${wallet.signer.role} lane=${wallet.signer.operationLane} baseline=v${wallet.signer.baselineVersion}`);
|
|
1910
|
+
runtime.log(` key=${String(wallet.signer.keyReady)} policy=v${wallet.signer.policyVersion} ${wallet.signer.policyHash} network=v${wallet.signer.networkVersion} ${wallet.signer.networkHash ?? "unconfigured"}`);
|
|
1911
|
+
}
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
const status = await readWalletStatusSnapshot({ walletId: options.walletId });
|
|
1224
1915
|
if (options.json) {
|
|
1225
1916
|
runtime.log(JSON.stringify({
|
|
1226
1917
|
ok: true,
|
|
@@ -1248,10 +1939,104 @@ async function walletStatusCommand(runtime = defaultRuntime, options = {}) {
|
|
|
1248
1939
|
if (status.addresses?.solana) {
|
|
1249
1940
|
runtime.log(`Address: ${status.addresses.solana}`);
|
|
1250
1941
|
}
|
|
1942
|
+
for (const wallet of status.wallets ?? []) {
|
|
1943
|
+
const signer = wallet.readiness.signer;
|
|
1944
|
+
runtime.log(`Wallet ${wallet.name} (${wallet.id}): ${wallet.readiness.ready ? "ready" : "setup incomplete"}`);
|
|
1945
|
+
if (signer) {
|
|
1946
|
+
runtime.log(` signer=${signer.walletId} role=${signer.role} lane=${signer.operationLane} baseline=v${signer.baselineVersion}`);
|
|
1947
|
+
runtime.log(` key=${String(signer.keyReady)} policy=v${signer.policyVersion} ${signer.policyHash} network=v${signer.networkVersion} ${signer.networkHash ?? "unconfigured"}`);
|
|
1948
|
+
}
|
|
1949
|
+
if (wallet.readiness.error) {
|
|
1950
|
+
runtime.log(` warning=${wallet.readiness.error}`);
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1251
1953
|
if (status.error) {
|
|
1252
1954
|
runtime.log(`Status warning: ${status.error}`);
|
|
1253
1955
|
}
|
|
1254
1956
|
}
|
|
1957
|
+
async function walletPolicyActivateRoleBaselineCommand(runtime = defaultRuntime, options) {
|
|
1958
|
+
if (!options.confirm) {
|
|
1959
|
+
throw new Error("Activate role baseline requires --confirm after reviewing the selected role");
|
|
1960
|
+
}
|
|
1961
|
+
const role = normalizeWalletUserRole(options.role);
|
|
1962
|
+
if (!role) {
|
|
1963
|
+
throw new Error("role must be one of: agent, mining, vault");
|
|
1964
|
+
}
|
|
1965
|
+
const registry = readWalletProviderRegistry(process.env);
|
|
1966
|
+
const wallet = registry.wallets.find((entry) => entry.id === options.walletId.trim());
|
|
1967
|
+
if (!wallet || wallet.providerId !== "local-socket-signer") {
|
|
1968
|
+
throw new Error(`registered signer-owned wallet was not found: ${options.walletId}`);
|
|
1969
|
+
}
|
|
1970
|
+
const registeredRole = resolveWalletUserRole(wallet);
|
|
1971
|
+
if (registeredRole && registeredRole !== role) {
|
|
1972
|
+
throw new Error(`wallet ${wallet.id} has immutable role=${registeredRole}; refusing requested role=${role}`);
|
|
1973
|
+
}
|
|
1974
|
+
const signerWalletId = typeof wallet.metadata?.signerWalletId === "string" && wallet.metadata.signerWalletId.trim() ? wallet.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(wallet.id);
|
|
1975
|
+
const socketPath = requireLocalSocketSignerPath(process.env);
|
|
1976
|
+
const current = await readSignerOwnedWallet({
|
|
1977
|
+
socketPath,
|
|
1978
|
+
walletId: signerWalletId
|
|
1979
|
+
});
|
|
1980
|
+
if (current.wallet.publicKey !== wallet.addresses?.solana) {
|
|
1981
|
+
throw new Error("registered wallet address does not match the signer-owned wallet");
|
|
1982
|
+
}
|
|
1983
|
+
if (current.policy.role !== role) {
|
|
1984
|
+
throw new Error(`signer-owned wallet ${signerWalletId} has immutable role=${current.policy.role}, not ${role}`);
|
|
1985
|
+
}
|
|
1986
|
+
let policy = current.policy;
|
|
1987
|
+
if (policy.baselineVersion === undefined || policy.baselineVersion === 0) {
|
|
1988
|
+
policy = await activateSignerOwnedRoleBaseline({
|
|
1989
|
+
socketPath,
|
|
1990
|
+
walletId: signerWalletId,
|
|
1991
|
+
role,
|
|
1992
|
+
expectedPolicyVersion: policy.version
|
|
1993
|
+
});
|
|
1994
|
+
} else if (policy.baselineVersion !== 1) {
|
|
1995
|
+
throw new Error(`signer-owned wallet ${signerWalletId} uses unsupported baseline version ${policy.baselineVersion}`);
|
|
1996
|
+
}
|
|
1997
|
+
const readiness = await readSignerOwnedWalletReadiness({
|
|
1998
|
+
socketPath,
|
|
1999
|
+
walletId: signerWalletId
|
|
2000
|
+
});
|
|
2001
|
+
if (readiness.publicKey !== current.wallet.publicKey || readiness.role !== role || readiness.policyHash !== policy.hash || readiness.policyVersion !== policy.version || readiness.baselineVersion !== 1) {
|
|
2002
|
+
throw new Error("Setup incomplete: activated role baseline does not match live signer state");
|
|
2003
|
+
}
|
|
2004
|
+
upsertNamedWallet({
|
|
2005
|
+
walletId: wallet.id,
|
|
2006
|
+
name: wallet.name,
|
|
2007
|
+
providerId: wallet.providerId,
|
|
2008
|
+
addresses: wallet.addresses,
|
|
2009
|
+
metadata: {
|
|
2010
|
+
...wallet.metadata,
|
|
2011
|
+
role,
|
|
2012
|
+
purpose: role,
|
|
2013
|
+
signerWalletId,
|
|
2014
|
+
policyHash: readiness.policyHash,
|
|
2015
|
+
policyVersion: readiness.policyVersion,
|
|
2016
|
+
baselineVersion: readiness.baselineVersion,
|
|
2017
|
+
policyState: readiness.policyReady ? "ready" : "setup-incomplete",
|
|
2018
|
+
networkHash: readiness.networkHash,
|
|
2019
|
+
networkVersion: readiness.networkVersion,
|
|
2020
|
+
networkReady: readiness.networkReady,
|
|
2021
|
+
operationLane: readiness.operationLane,
|
|
2022
|
+
roleReady: readiness.ready
|
|
2023
|
+
},
|
|
2024
|
+
env: process.env
|
|
2025
|
+
});
|
|
2026
|
+
const payload = {
|
|
2027
|
+
ok: true,
|
|
2028
|
+
walletId: wallet.id,
|
|
2029
|
+
signerWalletId,
|
|
2030
|
+
role,
|
|
2031
|
+
readiness
|
|
2032
|
+
};
|
|
2033
|
+
if (options.json) {
|
|
2034
|
+
runtime.log(JSON.stringify(payload, null, 2));
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
runtime.log(`Activated ${role} role baseline v${readiness.baselineVersion} for ${wallet.name} (${wallet.id}).`);
|
|
2038
|
+
runtime.log(`Readiness: ${readiness.ready ? "ready" : "setup incomplete"} (${readiness.operationLane}).`);
|
|
2039
|
+
}
|
|
1255
2040
|
async function walletKeystoreInitCommand(runtime = defaultRuntime, options = {}) {
|
|
1256
2041
|
void runtime;
|
|
1257
2042
|
void options;
|
|
@@ -1936,4 +2721,4 @@ async function walletInboundReconcileCommand(runtime = defaultRuntime, options =
|
|
|
1936
2721
|
}
|
|
1937
2722
|
|
|
1938
2723
|
//#endregion
|
|
1939
|
-
export { collectWalletSignerDoctorReport, walletCanaryCommand, walletInboundListCommand, walletInboundPollCommand, walletInboundReconcileCommand, walletLegacyMigrationFinalizeCommand, walletLimitOrdersConfigureCommand, walletMigrateCommand, walletPolicyProfileApplyCommand, walletProviderConfigureCommand, walletRawExportCommand, walletRecoveryExportCommand, walletRecoveryImportCommand, walletRoleSetCommand, walletRotateKeysCommand, walletSetupCommand, walletSignerDoctorCommand, walletSignerServeCommand, walletStatusCommand };
|
|
2724
|
+
export { collectWalletSignerDoctorReport, walletCanaryCommand, walletInboundListCommand, walletInboundPollCommand, walletInboundReconcileCommand, walletLegacyMigrationFinalizeCommand, walletLimitOrdersConfigureCommand, walletMigrateCommand, walletPolicyActivateRoleBaselineCommand, walletPolicyProfileApplyCommand, walletProviderConfigureCommand, walletRawExportCommand, walletRecoveryExportCommand, walletRecoveryImportCommand, walletRetireCommand, walletRoleSetCommand, walletRotateKeysCommand, walletRpcSetCommand, walletSetupCommand, walletSignerDoctorCommand, walletSignerServeCommand, walletStatusCommand };
|