diff --git a/CoreModule.js b/CoreModule.js
index af28464d3..2dcf1f781 100644
--- a/CoreModule.js
+++ b/CoreModule.js
@@ -719,6 +719,43 @@ var http = require('http');
var net = require('net');
var fs = require('fs');
var rtc = require('ILibWebRTC');
+
+// OpenFrame: Read machine ID from shared location
+var openframeMachineId = null;
+function getOpenFrameMachineId() {
+ if (openframeMachineId != null) return openframeMachineId;
+ try {
+ var machineIdPath = (process.platform == 'win32')
+ ? (process.env['ProgramData'] + '\\OpenFrame\\machine_id')
+ : ((process.platform == 'darwin')
+ ? '/Library/Application Support/OpenFrame/machine_id'
+ : '/var/lib/openframe/machine_id');
+ openframeMachineId = fs.readFileSync(machineIdPath).toString().trim();
+ } catch (ex) { openframeMachineId = null; }
+ return openframeMachineId;
+}
+
+// OpenFrame: Add x-machine-id and Authorization headers to request options (only in openFrameMode)
+function addOpenFrameHeaders(options) {
+ // Only add headers if running in OpenFrame mode
+ if (!mesh.openFrameMode) return options;
+
+ if (!options.headers) options.headers = {};
+
+ // Add x-machine-id header
+ var machineId = getOpenFrameMachineId();
+ if (machineId) {
+ options.headers['x-machine-id'] = machineId;
+ }
+
+ // Add Authorization header with JWT token
+ var token = mesh.authToken();
+ if (token) {
+ options.headers['Authorization'] = 'Bearer ' + token;
+ }
+
+ return options;
+}
var amt = null;
var processManager = require('process-manager');
var wifiScannerLib = null;
@@ -1169,6 +1206,7 @@ function handleServerCommand(data) {
//sendConsoleText(JSON.stringify(woptions));
//sendConsoleText('TUNNEL: ' + JSON.stringify(data, null, 2));
+ addOpenFrameHeaders(woptions); // Add X-MACHINE-ID and Authorization headers
var tunnel = http.request(woptions);
tunnel.upgrade = onTunnelUpgrade;
tunnel.on('error', tunnel_onError);
@@ -1812,6 +1850,7 @@ function downloadFile(downloadoptions) {
if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') }
}
//options.checkServerIdentity.servertlshash = downloadoptions.serverhash;
+ addOpenFrameHeaders(options); // Add X-MACHINE-ID header
trustedDownloads[downloadoptions.name] = downloadoptions;
trustedDownloads[downloadoptions.name].dl = require('https').get(options);
trustedDownloads[downloadoptions.name].dl.on('error', function (e) { downloadoptions.func(downloadoptions, false); delete trustedDownloads[downloadoptions.name]; });
@@ -1864,6 +1903,7 @@ function serverFetchFile() {
agentFileHttpOptions.checkServerIdentity.servertlshash = data.servertlshash;
if (agentFileHttpOptions == null) return;
+ addOpenFrameHeaders(agentFileHttpOptions); // Add X-MACHINE-ID header
var agentFileHttpRequest = http.request(agentFileHttpOptions,
function (response) {
response.xparent = this;
@@ -5096,6 +5136,7 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
if (options == null) {
response = 'Invalid url.';
} else {
+ addOpenFrameHeaders(options); // Add X-MACHINE-ID header
try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (ex) { response = 'Invalid HTTP GET request'; }
consoleHttpRequest.sessionid = sessionid;
if (consoleHttpRequest != null) {
@@ -5124,6 +5165,7 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
try {
var options = http.parseUri(args['_'][0].split('$').join('%24').split('@').join('%40')); // Escape the $ and @ characters in the URL
options.rejectUnauthorized = 0;
+ addOpenFrameHeaders(options); // Add X-MACHINE-ID header
httprequest = http.request(options);
} catch (ex) { response = 'Invalid HTTP websocket request'; }
if (httprequest != null) {
@@ -5773,6 +5815,7 @@ function agentUpdate_Start(updateurl, updateoptions) {
}
}
options.checkServerIdentity.servertlshash = (updateoptions != null ? updateoptions.tlshash : null);
+ addOpenFrameHeaders(options); // Add X-MACHINE-ID header
agentUpdate_Start._selfupdate = require('https').get(options);
agentUpdate_Start._selfupdate.on('error', function (e) {
sendConsoleText('Self Update failed, because there was a problem trying to download the update from ' + updateurl, sessionid);
diff --git a/makefile b/makefile
index ff7622676..69fc51dc1 100644
--- a/makefile
+++ b/makefile
@@ -186,7 +186,7 @@ SOURCES += $(ADDITIONALSOURCES)
ADDITIONALOBJECTS ?=
# Mesh Agent core
-SOURCES += meshcore/agentcore.c meshconsole/main.c meshcore/meshinfo.c openframe/token_extractor.c
+SOURCES += meshcore/agentcore.c meshconsole/main.c meshcore/meshinfo.c openframe/token_extractor.c openframe/machine_id_reader.c
# Mesh Agent settings
MESH_VER = 194
diff --git a/meshconsole/MeshConsole-2022.vcxproj b/meshconsole/MeshConsole-2022.vcxproj
index 473add809..67a60debe 100644
--- a/meshconsole/MeshConsole-2022.vcxproj
+++ b/meshconsole/MeshConsole-2022.vcxproj
@@ -82,6 +82,7 @@
+
@@ -138,6 +139,7 @@
+
diff --git a/meshcore/agentcore.c b/meshcore/agentcore.c
index c3994d12f..84cef21e4 100644
--- a/meshcore/agentcore.c
+++ b/meshcore/agentcore.c
@@ -45,6 +45,7 @@ limitations under the License.
#include "microscript/ILibDuktape_ScriptContainer.h"
#include "../microstack/ILibIPAddressMonitor.h"
#include "../openframe/token_extractor.h"
+#include "../openframe/machine_id_reader.h"
#ifdef _POSIX
#include
@@ -2344,6 +2345,7 @@ void ILibDuktape_MeshAgent_PUSH(duk_context *ctx, void *chain)
ILibDuktape_CreateInstanceMethod(ctx, "DataPing", ILibDuktape_MeshAgent_DataPing, DUK_VARARGS);
ILibDuktape_CreateReadonlyProperty_int(ctx, "ARCHID", MESH_AGENTID);
ILibDuktape_CreateReadonlyProperty_int(ctx, "ConsoleTextMaxRate", agent->consoleText_maxRate);
+ ILibDuktape_CreateReadonlyProperty_int(ctx, "openFrameMode", agent->openFrameMode ? 1 : 0);
#ifdef _LINKVM
ILibDuktape_CreateReadonlyProperty_int(ctx, "hasKVM", 1);
ILibDuktape_EventEmitter_CreateEventEx(emitter, "kvmConnected");
@@ -4614,19 +4616,30 @@ void MeshServer_ConnectEx(MeshAgentHostContainer *agent)
strcat(combined, SOURCE_COMMIT_DATE);
ILibAddHeaderLine(req, "User-Agent", 10, combined, (int)strnlen_s(combined, 50));
- // Add custom JWT for openframe mode
- if (agent->openFrameMode && agent->openFrameSecret != NULL)
+ // Add custom headers for openframe mode
+ if (agent->openFrameMode)
{
- char* extracted_token = extract_token(agent->openFrameSecret, agent->openFrameTokenPath);
- if (extracted_token != NULL) {
- int authLen = 7 + strlen(extracted_token) + 1; // "Bearer " + token + null terminator
- char *openframeAuthorization = (char*)malloc(authLen);
- if (openframeAuthorization != NULL) {
- sprintf(openframeAuthorization, "Bearer %s", extracted_token);
- ILibAddHeaderLine(req, "Authorization", 13, openframeAuthorization, (int)strlen(openframeAuthorization));
- free(openframeAuthorization);
+ // Add x-machine-id header
+ char* machineId = read_machine_id();
+ if (machineId != NULL) {
+ ILibAddHeaderLine(req, "x-machine-id", 12, machineId, (int)strlen(machineId));
+ free(machineId);
+ }
+
+ // Add Authorization header with JWT
+ if (agent->openFrameSecret != NULL)
+ {
+ char* extracted_token = extract_token(agent->openFrameSecret, agent->openFrameTokenPath);
+ if (extracted_token != NULL) {
+ int authLen = 7 + strlen(extracted_token) + 1; // "Bearer " + token + null terminator
+ char *openframeAuthorization = (char*)malloc(authLen);
+ if (openframeAuthorization != NULL) {
+ sprintf(openframeAuthorization, "Bearer %s", extracted_token);
+ ILibAddHeaderLine(req, "Authorization", 13, openframeAuthorization, (int)strlen(openframeAuthorization));
+ free(openframeAuthorization);
+ }
+ free(extracted_token);
}
- free(extracted_token);
}
}
diff --git a/meshservice/MeshService-2022.vcxproj b/meshservice/MeshService-2022.vcxproj
index 3f5cc1fce..22d05a59d 100644
--- a/meshservice/MeshService-2022.vcxproj
+++ b/meshservice/MeshService-2022.vcxproj
@@ -822,6 +822,7 @@ powershell -ExecutionPolicy Unrestricted $(ProjectDir)prebuild.ps1 $(ProjectDir)
+
@@ -879,6 +880,7 @@ powershell -ExecutionPolicy Unrestricted $(ProjectDir)prebuild.ps1 $(ProjectDir)
+
diff --git a/microscript/ILibDuktape_Commit.h b/microscript/ILibDuktape_Commit.h
index ba83c1bef..9b2e7580f 100644
--- a/microscript/ILibDuktape_Commit.h
+++ b/microscript/ILibDuktape_Commit.h
@@ -1,3 +1,3 @@
// This file is auto-generated, any edits may be overwritten
-#define SOURCE_COMMIT_DATE "2026-Mar-3 13:49:57+0000"
-#define SOURCE_COMMIT_HASH "26d32854a436dc65e45ee3576bf602c898444cc1"
+#define SOURCE_COMMIT_DATE "2026-Mar-4 12:31:15+0000"
+#define SOURCE_COMMIT_HASH "1242c456119125053a41282337d3f7d159ac4784"
diff --git a/modules/RecoveryCore.js b/modules/RecoveryCore.js
index 762fa91f5..aedef9dd8 100644
--- a/modules/RecoveryCore.js
+++ b/modules/RecoveryCore.js
@@ -6,6 +6,43 @@ var nextTunnelIndex = 1;
var tunnels = {};
var fs = require('fs');
+// OpenFrame: Read machine ID from shared location
+var openframeMachineId = null;
+function getOpenFrameMachineId() {
+ if (openframeMachineId != null) return openframeMachineId;
+ try {
+ var machineIdPath = (process.platform == 'win32')
+ ? (process.env['ProgramData'] + '\\OpenFrame\\machine_id')
+ : ((process.platform == 'darwin')
+ ? '/Library/Application Support/OpenFrame/machine_id'
+ : '/var/lib/openframe/machine_id');
+ openframeMachineId = fs.readFileSync(machineIdPath).toString().trim();
+ } catch (ex) { openframeMachineId = null; }
+ return openframeMachineId;
+}
+
+// OpenFrame: Add x-machine-id and Authorization headers to request options (only in openFrameMode)
+function addOpenFrameHeaders(options) {
+ var mesh = require('MeshAgent');
+ if (!mesh.openFrameMode) return options;
+
+ if (!options.headers) options.headers = {};
+
+ // Add x-machine-id header
+ var machineId = getOpenFrameMachineId();
+ if (machineId) {
+ options.headers['x-machine-id'] = machineId;
+ }
+
+ // Add Authorization header with JWT token
+ var token = mesh.authToken();
+ if (token) {
+ options.headers['Authorization'] = 'Bearer ' + token;
+ }
+
+ return options;
+}
+
//attachDebugger({ webport: 9994, wait: 1 }).then(function (p) { console.log('Debug on port: ' + p); });
function sendConsoleText(msg)
@@ -155,6 +192,7 @@ require('MeshAgent').AddCommandHandler(function (data)
var woptions = http.parseUri(xurl);
woptions.rejectUnauthorized = 0;
//sendConsoleText(JSON.stringify(woptions));
+ addOpenFrameHeaders(woptions); // Add X-MACHINE-ID header
var tunnel = http.request(woptions);
tunnel.on('upgrade', function (response, s, head)
{
diff --git a/openframe/machine_id_reader.c b/openframe/machine_id_reader.c
new file mode 100644
index 000000000..c0e5f562f
--- /dev/null
+++ b/openframe/machine_id_reader.c
@@ -0,0 +1,97 @@
+#include
+#include
+#include
+#include "machine_id_reader.h"
+
+#define MAX_MACHINE_ID_LEN 128
+
+#ifdef _WIN32
+#include
+
+char* read_machine_id() {
+ // Read from C:\ProgramData\OpenFrame\machine_id
+ char path[MAX_PATH];
+ char* programData = getenv("ProgramData");
+ if (!programData) {
+ return NULL;
+ }
+
+ snprintf(path, sizeof(path), "%s\\OpenFrame\\machine_id", programData);
+
+ FILE* file = fopen(path, "r");
+ if (!file) {
+ return NULL;
+ }
+
+ char* machineId = malloc(MAX_MACHINE_ID_LEN);
+ if (!machineId) {
+ fclose(file);
+ return NULL;
+ }
+
+ if (fgets(machineId, MAX_MACHINE_ID_LEN, file) == NULL) {
+ free(machineId);
+ fclose(file);
+ return NULL;
+ }
+
+ fclose(file);
+
+ // Trim newline
+ size_t len = strlen(machineId);
+ while (len > 0 && (machineId[len-1] == '\n' || machineId[len-1] == '\r')) {
+ machineId[--len] = '\0';
+ }
+
+ if (len == 0) {
+ free(machineId);
+ return NULL;
+ }
+
+ return machineId;
+}
+
+#else
+// macOS / Linux
+
+char* read_machine_id() {
+#ifdef __APPLE__
+ const char* path = "/Library/Application Support/OpenFrame/machine_id";
+#else
+ const char* path = "/var/lib/openframe/machine_id";
+#endif
+
+ FILE* file = fopen(path, "r");
+ if (!file) {
+ return NULL;
+ }
+
+ char* machineId = malloc(MAX_MACHINE_ID_LEN);
+ if (!machineId) {
+ fclose(file);
+ return NULL;
+ }
+
+ if (fgets(machineId, MAX_MACHINE_ID_LEN, file) == NULL) {
+ free(machineId);
+ fclose(file);
+ return NULL;
+ }
+
+ fclose(file);
+
+ // Trim newline
+ size_t len = strlen(machineId);
+ while (len > 0 && (machineId[len-1] == '\n' || machineId[len-1] == '\r')) {
+ machineId[--len] = '\0';
+ }
+
+ if (len == 0) {
+ free(machineId);
+ return NULL;
+ }
+
+ return machineId;
+}
+
+#endif
diff --git a/openframe/machine_id_reader.h b/openframe/machine_id_reader.h
new file mode 100644
index 000000000..8b4162dbc
--- /dev/null
+++ b/openframe/machine_id_reader.h
@@ -0,0 +1,22 @@
+#ifndef MACHINE_ID_READER_H
+#define MACHINE_ID_READER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Read machine ID from shared OpenFrame location
+ * - macOS: /Library/Application Support/OpenFrame/machine_id
+ * - Windows: C:\ProgramData\OpenFrame\machine_id
+ * - Linux: /var/lib/openframe/machine_id
+ *
+ * @return Machine ID string (caller must free) or NULL if not found
+ */
+char* read_machine_id(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MACHINE_ID_READER_H