Added the guarded, one-shot monero:pilot-address command

This commit is contained in:
2026-10-22 19:10:08 -04:00
parent 83d3d104c1
commit 36963fed30
7 changed files with 135 additions and 2 deletions
+64
View File
@@ -0,0 +1,64 @@
import { Pool } from 'pg';
import { z } from 'zod';
import { IS_PILOT } from '../shared/network.ts';
import { createMoneroDepositAddressRpc, loadMoneroObserverConfig } from '../server/market/monero-observer.ts';
import { loadMoneroMtlsTransport, loadMoneroMtlsTransportConfig } from '../server/market/monero-mtls-transport.ts';
import { postgresConfig } from '../server/market/postgres-config.ts';
import { createOrRecoverPilotMoneroDepositAddress } from '../server/market/pilot-monero-deposit-allocation.ts';
import { parsePilotCustodianId } from '../server/market/pilot-custody-identity.ts';
import { loadPilotMoneroDepositRequestFile } from '../server/market/pilot-monero-deposit-request-file.ts';
if (!IS_PILOT || process.env.WIKIDECK_PILOT_MODE !== 'private-mock-only') {
throw new Error('Pilot address command requires the private mock-only pilot profile');
}
if (process.env.WIKIDECK_PILOT_CUSTODY_OPERATIONS !== 'operator-triggered-addresses-only') {
throw new Error('Set WIKIDECK_PILOT_CUSTODY_OPERATIONS=operator-triggered-addresses-only for this explicit operator action');
}
if (process.argv.length !== 3) throw new Error('Usage: pilot-monero-deposit-address.ts SIGNED_REQUEST_JSON_FILE');
if (process.platform === 'win32') throw new Error('Pilot address command requires POSIX protected-file permission checks');
const request = await loadPilotMoneroDepositRequestFile(process.argv[2]);
const custodianId = parsePilotCustodianId(process.env.WIKIDECK_PILOT_CUSTODIAN_ID ?? '');
const requestCustodian = z.object({ custodianId: z.string().uuid() }).passthrough().parse(request).custodianId.toLowerCase();
if (requestCustodian !== custodianId) throw new Error('Signed request does not match the configured pilot custodian');
const walletGenerationId = process.env.WIKIDECK_MONERO_WALLET_GENERATION_ID ?? '';
const walletFingerprint = process.env.WIKIDECK_MONERO_WALLET_FINGERPRINT ?? '';
const accountText = process.env.WIKIDECK_MONERO_DEPOSIT_ACCOUNT_INDEX;
if (!/^[a-f0-9-]{36}$/i.test(walletGenerationId) || !/^[a-f0-9]{64}$/.test(walletFingerprint) ||
!accountText || !/^(0|[1-9]\d{0,9})$/.test(accountText) || Number(accountText) > 0xffff_ffff) {
throw new Error('Invalid protected pilot watch-only wallet generation, fingerprint, or account configuration');
}
const observerConfig = await loadMoneroObserverConfig({ ...process.env, WIKIDECK_MONERO_OBSERVER: '1' });
const mtlsConfig = await loadMoneroMtlsTransportConfig();
if (observerConfig.network !== 'mainnet' || new URL(observerConfig.url).href !== new URL(mtlsConfig.url).href) {
throw new Error('Pilot Wallet-RPC config must match the protected Mainnet mTLS gateway endpoint');
}
const transport = await loadMoneroMtlsTransport(mtlsConfig);
const rpc = createMoneroDepositAddressRpc(observerConfig, transport);
const actualFingerprint = await rpc.verifyWallet();
if (actualFingerprint !== walletFingerprint) throw new Error('Configured pilot wallet fingerprint does not match the protected Wallet-RPC wallet');
const pool = new Pool(await postgresConfig({
...process.env,
WIKIDECK_CUSTODY_PG_URL_FILE: process.env.WIKIDECK_PILOT_ADDRESS_PG_URL_FILE,
WIKIDECK_CUSTODY_PG_CA_FILE: process.env.WIKIDECK_PILOT_ADDRESS_PG_CA_FILE,
WIKIDECK_CUSTODY_PG_TRANSPORT: 'tls',
}));
pool.on('error', () => console.error(JSON.stringify({ component: 'pilot-address', event: 'database-unavailable' })));
try {
const result = await createOrRecoverPilotMoneroDepositAddress(pool, rpc, {
walletGenerationId,
walletFingerprint: actualFingerprint,
accountIndex: Number(accountText),
}, request);
// This is an operator-invoked address response only; no balance credit or deposit acceptance occurs here.
console.log(JSON.stringify({ network: 'mainnet', marketNetwork: result.reservation.marketNetwork,
marketChainId: result.reservation.marketChainId, custodianId: result.reservation.custodianId,
allocationId: result.reservation.allocationId, beneficiary: result.reservation.beneficiary,
addressIndex: result.reservation.addressIndex, address: result.address, replayed: result.replayed,
crediting: 'disabled' }));
} catch {
console.error('Pilot address allocation failed or requires manual reconciliation; no funding/credit action was performed.');
process.exitCode = 1;
} finally { await pool.end(); }