@@ -46,18 +46,93 @@ class PolicyEngine {
4646
4747 /**
4848 * Build common headers for API requests
49+ * Includes machine identification for first-time registration
4950 * @returns {Object }
5051 */
5152 buildApiHeaders ( ) {
5253 const authToken = this . configManager . get ( 'authToken' ) ;
54+ const agentId = this . configManager . get ( 'agentId' ) ;
55+
56+ // Get machine ID for first-time registration
57+ const machineId = this . getMachineId ( ) ;
58+ const hostname = require ( 'os' ) . hostname ( ) ;
59+
5360 return {
5461 'Authorization' : `Bearer ${ authToken } ` ,
5562 'Content-Type' : 'application/json' ,
5663 'X-Agent-Version' : this . getAgentVersion ( ) ,
57- 'X-Agent-Platform' : process . platform
64+ 'X-Agent-Platform' : process . platform ,
65+ 'X-Machine-Id' : machineId ,
66+ 'X-Hostname' : hostname ,
67+ 'X-Agent-Id' : agentId || ''
5868 } ;
5969 }
6070
71+ /**
72+ * Get or generate a stable machine ID
73+ * Uses platform-specific unique identifiers
74+ * @returns {string }
75+ */
76+ getMachineId ( ) {
77+ // Check if we have a cached machine ID
78+ let machineId = this . configManager . get ( 'machineId' ) ;
79+ if ( machineId ) return machineId ;
80+
81+ // Generate machine ID from system info
82+ const os = require ( 'os' ) ;
83+ const crypto = require ( 'crypto' ) ;
84+
85+ // Create a hash from stable system properties
86+ const components = [
87+ os . hostname ( ) ,
88+ os . platform ( ) ,
89+ os . arch ( ) ,
90+ os . cpus ( ) [ 0 ] ?. model || 'unknown'
91+ ] ;
92+
93+ // Add network interface MACs for uniqueness
94+ const networkInterfaces = os . networkInterfaces ( ) ;
95+ for ( const iface of Object . values ( networkInterfaces ) ) {
96+ for ( const addr of iface ) {
97+ if ( ! addr . internal && addr . mac && addr . mac !== '00:00:00:00:00:00' ) {
98+ components . push ( addr . mac ) ;
99+ break ; // Only use first non-internal MAC
100+ }
101+ }
102+ }
103+
104+ machineId = crypto . createHash ( 'sha256' )
105+ . update ( components . join ( ':' ) )
106+ . digest ( 'hex' )
107+ . substring ( 0 , 32 ) ;
108+
109+ // Cache the machine ID
110+ this . configManager . set ( 'machineId' , machineId ) ;
111+ this . logger . info ( 'Generated machine ID' , { machineId } ) ;
112+
113+ return machineId ;
114+ }
115+
116+ /**
117+ * Handle JWT upgrade from parent
118+ * When parent returns a new JWT token, store it for future requests
119+ * @param {Response } response - Fetch response object
120+ */
121+ handleTokenUpgrade ( response ) {
122+ const newToken = response . headers . get ( 'X-Agent-Token' ) ;
123+ const newAgentId = response . headers . get ( 'X-Agent-Id' ) ;
124+
125+ if ( newToken ) {
126+ this . configManager . set ( 'authToken' , newToken ) ;
127+ this . logger . info ( 'Received and stored new auth token from parent' ) ;
128+ }
129+
130+ if ( newAgentId ) {
131+ this . configManager . set ( 'agentId' , newAgentId ) ;
132+ this . logger . info ( 'Received and stored new agent ID from parent' , { agentId : newAgentId } ) ;
133+ }
134+ }
135+
61136 /**
62137 * Load policies from configuration cache
63138 */
@@ -295,12 +370,15 @@ class PolicyEngine {
295370 }
296371
297372 try {
298- // Use common headers with X-Agent-Version
299- const response = await fetch ( `${ parentApiUrl } /api/agents/ ${ agentId } /policies` , {
373+ // Use common headers with machine info for auto-registration
374+ const response = await fetch ( `${ parentApiUrl } /api/agent /policies` , {
300375 method : 'GET' ,
301376 headers : this . buildApiHeaders ( )
302377 } ) ;
303378
379+ // Check for token upgrade (first-time registration)
380+ this . handleTokenUpgrade ( response ) ;
381+
304382 if ( ! response . ok ) {
305383 throw new Error ( `HTTP ${ response . status } : ${ response . statusText } ` ) ;
306384 }
@@ -365,14 +443,19 @@ class PolicyEngine {
365443 offlineDurationSeconds : Math . round ( offlineDuration / 1000 )
366444 } ) ;
367445
368- await fetch ( `${ parentApiUrl } /api/agents/ ${ agentId } /offline-recovery ` , {
446+ const response = await fetch ( `${ parentApiUrl } /api/agent/heartbeat ` , {
369447 method : 'POST' ,
370448 headers : this . buildApiHeaders ( ) ,
371449 body : JSON . stringify ( {
372- offlineDuration,
373- recoveredAt : Date . now ( )
450+ metadata : {
451+ offlineRecovery : true ,
452+ offlineDuration,
453+ recoveredAt : Date . now ( )
454+ }
374455 } )
375456 } ) ;
457+ // Handle token upgrade
458+ this . handleTokenUpgrade ( response ) ;
376459 } catch ( error ) {
377460 // Non-critical - log and continue
378461 this . logger . warn ( 'Failed to report offline recovery' , {
@@ -422,6 +505,9 @@ class PolicyEngine {
422505 } )
423506 } ) ;
424507
508+ // Handle token upgrade
509+ this . handleTokenUpgrade ( response ) ;
510+
425511 if ( response . ok ) {
426512 // Clear synced data
427513 this . pluginExtensionManager . clearQueuedData ( ) ;
@@ -469,13 +555,16 @@ class PolicyEngine {
469555 action : 'terminated'
470556 } ;
471557
472- // Use common headers with X-Agent-Version
473- const response = await fetch ( `${ parentApiUrl } /api/violations` , {
558+ // Use common headers with machine info
559+ const response = await fetch ( `${ parentApiUrl } /api/agent/ violations` , {
474560 method : 'POST' ,
475561 headers : this . buildApiHeaders ( ) ,
476562 body : JSON . stringify ( violation )
477563 } ) ;
478564
565+ // Handle token upgrade
566+ this . handleTokenUpgrade ( response ) ;
567+
479568 if ( ! response . ok ) {
480569 throw new Error ( `HTTP ${ response . status } : ${ response . statusText } ` ) ;
481570 }
0 commit comments