From 67a50e3a9f8cc7420a1b0a283449bbaebe8e4fa9 Mon Sep 17 00:00:00 2001 From: VinceJuliano Date: Sun, 22 Feb 2026 14:08:00 -0500 Subject: [PATCH 1/2] feat(mu): process whitelisting --- servers/mu/src/config.js | 6 ++--- servers/mu/src/domain/api/monitorProcess.js | 14 ++++++++-- servers/mu/src/domain/api/sendAssign.js | 9 ++++++- servers/mu/src/domain/api/sendDataItem.js | 11 ++++++-- servers/mu/src/domain/index.js | 30 ++++++++++++--------- 5 files changed, 49 insertions(+), 21 deletions(-) diff --git a/servers/mu/src/config.js b/servers/mu/src/config.js index fdd136db3..5d3767066 100644 --- a/servers/mu/src/config.js +++ b/servers/mu/src/config.js @@ -94,7 +94,7 @@ export const domainConfigSchema = z.object({ ENABLE_HB_WALLET_CHECK: z.boolean(), HB_GRAPHQL_URL: z.string(), RATE_LIMIT_FILE_URL: z.string().optional(), - HB_PROCESSES_URL: z.string().optional(), + PROCESS_WHITELIST_URL: z.string().optional(), SKIP_REPUSH_CHECKS_TOKEN: z.string().optional() }) @@ -159,7 +159,7 @@ const CONFIG_ENVS = { ENABLE_HB_WALLET_CHECK: process.env.ENABLE_HB_WALLET_CHECK !== 'false', HB_GRAPHQL_URL: process.env.HB_GRAPHQL_URL || 'https://cache.forward.computer', RATE_LIMIT_FILE_URL: process.env.RATE_LIMIT_FILE_URL || '', - HB_PROCESSES_URL: process.env.HB_PROCESSES_URL || '', + PROCESS_WHITELIST_URL: process.env.PROCESS_WHITELIST_URL || '', SKIP_REPUSH_CHECKS_TOKEN: process.env.SKIP_REPUSH_CHECKS_TOKEN || '' }, production: { @@ -201,7 +201,7 @@ const CONFIG_ENVS = { ENABLE_HB_WALLET_CHECK: process.env.ENABLE_HB_WALLET_CHECK !== 'false', HB_GRAPHQL_URL: process.env.HB_GRAPHQL_URL || 'https://cache.forward.computer', RATE_LIMIT_FILE_URL: process.env.RATE_LIMIT_FILE_URL || '', - HB_PROCESSES_URL: process.env.HB_PROCESSES_URL || '', + PROCESS_WHITELIST_URL: process.env.PROCESS_WHITELIST_URL || '', SKIP_REPUSH_CHECKS_TOKEN: process.env.SKIP_REPUSH_CHECKS_TOKEN || '' } } diff --git a/servers/mu/src/domain/api/monitorProcess.js b/servers/mu/src/domain/api/monitorProcess.js index 39743d2b6..b4756d46f 100644 --- a/servers/mu/src/domain/api/monitorProcess.js +++ b/servers/mu/src/domain/api/monitorProcess.js @@ -1,4 +1,4 @@ -import { of } from 'hyper-async' +import { of, Rejected } from 'hyper-async' import { parseDataItemWith } from '../lib/parse-data-item.js' import { startWith } from '../lib/start-process.js' @@ -6,7 +6,8 @@ import { startWith } from '../lib/start-process.js' export function monitorProcessWith ({ logger, createDataItem, - startProcessMonitor + startProcessMonitor, + fetchProcessWhitelist }) { const parseDataItem = parseDataItemWith({ createDataItem, logger }) const start = startWith({ logger, startProcessMonitor }) @@ -14,6 +15,15 @@ export function monitorProcessWith ({ return (ctx) => { return of(ctx) .chain(parseDataItem) + .chain((ctx) => { + const whitelist = fetchProcessWhitelist ? fetchProcessWhitelist() : {} + if (whitelist && Object.keys(whitelist).length > 0 && !whitelist[ctx.tx.processId]) { + const error = new Error('Forbidden') + error.code = 403 + return Rejected(error) + } + return of(ctx) + }) .chain(start) } } diff --git a/servers/mu/src/domain/api/sendAssign.js b/servers/mu/src/domain/api/sendAssign.js index 03085009b..719335215 100644 --- a/servers/mu/src/domain/api/sendAssign.js +++ b/servers/mu/src/domain/api/sendAssign.js @@ -15,7 +15,8 @@ export function sendAssignWith ({ locateProcess, fetchResult, crank, - logger + logger, + fetchProcessWhitelist }) { const getCuAddress = getCuAddressWith({ selectNode, logger }) const pullResult = pullResultWith({ fetchResult, logger }) @@ -62,6 +63,12 @@ export function sendAssignWith ({ })) return (ctx) => { + const whitelist = fetchProcessWhitelist ? fetchProcessWhitelist() : {} + if (whitelist && Object.keys(whitelist).length > 0 && !whitelist[ctx.assign.processId]) { + const error = new Error('Forbidden') + error.code = 403 + return Rejected(error) + } return of(ctx) .chain((ctx) => locateProcessLocal(ctx.assign.processId) diff --git a/servers/mu/src/domain/api/sendDataItem.js b/servers/mu/src/domain/api/sendDataItem.js index 2f63fb39f..b93059ebb 100644 --- a/servers/mu/src/domain/api/sendDataItem.js +++ b/servers/mu/src/domain/api/sendDataItem.js @@ -40,13 +40,14 @@ export function sendDataItemWith ({ GET_RESULT_MAX_RETRIES, GET_RESULT_RETRY_DELAY, ENABLE_MESSAGE_RECOVERY, - fetchHBProcesses + fetchHBProcesses, + fetchProcessWhitelist }) { const verifyParsedDataItem = verifyParsedDataItemWith() const parseDataItem = parseDataItemWith({ createDataItem, logger }) const getCuAddress = getCuAddressWith({ selectNode, logger }) const writeMessage = writeMessageTxWith({ locateProcess, writeDataItem, logger, fetchSchedulerProcess, writeDataItemArweave }) - const pullResult = pullResultWith({ fetchResult, fetchHyperBeamResult, logger, fetchHBProcesses}) + const pullResult = pullResultWith({ fetchResult, fetchHyperBeamResult, logger, fetchHBProcesses }) const writeProcess = writeProcessTxWith({ locateScheduler, writeDataItem, logger }) const getResult = getResultWith({ selectNode, fetchResult, logger, GET_RESULT_MAX_RETRIES, GET_RESULT_RETRY_DELAY }) const insertMessage = insertMessageWith({ db }) @@ -319,6 +320,12 @@ export function sendDataItemWith ({ }) .chain(({ isMessage }) => { if (isMessage) { + const whitelist = fetchProcessWhitelist ? fetchProcessWhitelist() : {} + if (whitelist && Object.keys(whitelist).length > 0 && !whitelist[ctx.dataItem.target]) { + const error = new Error('Forbidden') + error.code = 403 + return Rejected(error) + } /* add schedLocation into the context if the target is a process. if its a wallet dont add diff --git a/servers/mu/src/domain/index.js b/servers/mu/src/domain/index.js index 73eb39289..cce1f5ceb 100644 --- a/servers/mu/src/domain/index.js +++ b/servers/mu/src/domain/index.js @@ -136,7 +136,7 @@ export const createApis = async (ctx) => { const ENABLE_HB_WALLET_CHECK = ctx.ENABLE_HB_WALLET_CHECK const HB_GRAPHQL_URL = ctx.HB_GRAPHQL_URL const RATE_LIMIT_FILE_URL = ctx.RATE_LIMIT_FILE_URL - const HB_PROCESSES_URL = ctx.HB_PROCESSES_URL + const PROCESS_WHITELIST_URL = ctx.PROCESS_WHITELIST_URL let rateLimitFile = {} cron.schedule('*/10 * * * *', async () => { @@ -189,19 +189,20 @@ export const createApis = async (ctx) => { let processesFile = {} cron.schedule('*/5 * * * *', async () => { - if (!HB_PROCESSES_URL || HB_PROCESSES_URL === '') return - console.log('Updating HB_PROCESSES file after 5 minutes', HB_PROCESSES_URL) - const fetchedProcessesFile = await fetch(HB_PROCESSES_URL) + if (!PROCESS_WHITELIST_URL || PROCESS_WHITELIST_URL === '') return + console.log('Updating process whitelist file after 5 minutes', PROCESS_WHITELIST_URL) + const json = await fetch(PROCESS_WHITELIST_URL) .then((res) => res.json()) .catch(err => { console.error('Error updating hb processes file', err) return {} }) - processesFile = { HB_PROCESSES: fetchedProcessesFile } + processesFile = { HB_PROCESSES: json.hb_processes || {}, PROCESSES: json.processes || {} } console.log('Updated processes file') }, { runOnInit: true }) const fetchHBProcesses = () => { return processesFile } + const fetchProcessWhitelist = () => processesFile.PROCESSES || {} // Create trace database metrics MetricsClient.gaugeWith({})({ @@ -322,7 +323,8 @@ export const createApis = async (ctx) => { GET_RESULT_MAX_RETRIES: ctx.GET_RESULT_MAX_RETRIES, GET_RESULT_RETRY_DELAY: ctx.GET_RESULT_RETRY_DELAY, ENABLE_MESSAGE_RECOVERY: ctx.ENABLE_MESSAGE_RECOVERY, - fetchHBProcesses + fetchHBProcesses, + fetchProcessWhitelist }) const sendAssignLogger = logger.child('sendAssign') @@ -332,7 +334,8 @@ export const createApis = async (ctx) => { writeAssignment: schedulerClient.writeAssignmentWith({ fetch, histogram, logger: sendAssignLogger }), fetchResult: cuClient.resultWith({ fetch: fetchWithCache, histogram, CU_URL, logger: sendDataItemLogger }), crank, - logger: sendAssignLogger + logger: sendAssignLogger, + fetchProcessWhitelist }) /** @@ -388,7 +391,8 @@ export const createApis = async (ctx) => { const monitorProcess = monitorProcessWith({ startProcessMonitor, createDataItem, - logger: monitorProcessLogger + logger: monitorProcessLogger, + fetchProcessWhitelist }) const stopMonitorProcessLogger = logger.child('stopMonitorProcess') @@ -473,7 +477,7 @@ export const createResultApis = async (ctx) => { const HB_ROUTER_URL = ctx.HB_ROUTER_URL const ENABLE_HB_WALLET_CHECK = ctx.ENABLE_HB_WALLET_CHECK const HB_GRAPHQL_URL = ctx.HB_GRAPHQL_URL - const HB_PROCESSES_URL = ctx.HB_PROCESSES_URL + const PROCESS_WHITELIST_URL = ctx.PROCESS_WHITELIST_URL const logger = ctx.logger const fetch = ctx.fetch @@ -503,15 +507,15 @@ export const createResultApis = async (ctx) => { let processesFile = {} cron.schedule('*/5 * * * *', async () => { - if (!HB_PROCESSES_URL || HB_PROCESSES_URL === '') return - console.log('Updating HB_PROCESSES file after 5 minutes', HB_PROCESSES_URL) - const fetchedProcessesFile = await fetch(HB_PROCESSES_URL) + if (!PROCESS_WHITELIST_URL || PROCESS_WHITELIST_URL === '') return + console.log('Updating process whitelist file after 5 minutes', PROCESS_WHITELIST_URL) + const json = await fetch(PROCESS_WHITELIST_URL) .then((res) => res.json()) .catch(err => { console.error('Error updating hb processes file', err) return {} }) - processesFile = { HB_PROCESSES: fetchedProcessesFile } + processesFile = { HB_PROCESSES: json.hb_processes || {}, PROCESSES: json.processes || {} } console.log('Updated processes file') }, { runOnInit: true }) From 0db88a45aabeb5e88471e5c263790fa51a563461 Mon Sep 17 00:00:00 2001 From: VinceJuliano Date: Sun, 22 Feb 2026 14:18:08 -0500 Subject: [PATCH 2/2] chore(mu): modify error message --- servers/mu/src/domain/api/monitorProcess.js | 2 +- servers/mu/src/domain/api/sendAssign.js | 2 +- servers/mu/src/domain/api/sendDataItem.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/servers/mu/src/domain/api/monitorProcess.js b/servers/mu/src/domain/api/monitorProcess.js index b4756d46f..4088b6014 100644 --- a/servers/mu/src/domain/api/monitorProcess.js +++ b/servers/mu/src/domain/api/monitorProcess.js @@ -18,7 +18,7 @@ export function monitorProcessWith ({ .chain((ctx) => { const whitelist = fetchProcessWhitelist ? fetchProcessWhitelist() : {} if (whitelist && Object.keys(whitelist).length > 0 && !whitelist[ctx.tx.processId]) { - const error = new Error('Forbidden') + const error = new Error('Forbidden, process not whitelisted') error.code = 403 return Rejected(error) } diff --git a/servers/mu/src/domain/api/sendAssign.js b/servers/mu/src/domain/api/sendAssign.js index 719335215..438d68da1 100644 --- a/servers/mu/src/domain/api/sendAssign.js +++ b/servers/mu/src/domain/api/sendAssign.js @@ -65,7 +65,7 @@ export function sendAssignWith ({ return (ctx) => { const whitelist = fetchProcessWhitelist ? fetchProcessWhitelist() : {} if (whitelist && Object.keys(whitelist).length > 0 && !whitelist[ctx.assign.processId]) { - const error = new Error('Forbidden') + const error = new Error('Forbidden, process not whitelisted') error.code = 403 return Rejected(error) } diff --git a/servers/mu/src/domain/api/sendDataItem.js b/servers/mu/src/domain/api/sendDataItem.js index b93059ebb..af6d12747 100644 --- a/servers/mu/src/domain/api/sendDataItem.js +++ b/servers/mu/src/domain/api/sendDataItem.js @@ -322,7 +322,7 @@ export function sendDataItemWith ({ if (isMessage) { const whitelist = fetchProcessWhitelist ? fetchProcessWhitelist() : {} if (whitelist && Object.keys(whitelist).length > 0 && !whitelist[ctx.dataItem.target]) { - const error = new Error('Forbidden') + const error = new Error('Forbidden, process not whitelisted') error.code = 403 return Rejected(error) }