1- #V03122026
1+ #V04082026
22# =============================================================================
33# PROJECT ANDREW – Master Integration & Sovereign Boot
44# =============================================================================
1212# Ensure 'knowledge_base/' directory exists in the root.
1313# 5. Permissions:
1414# Script requires write access for JSONL logging and Socket binding (ZMQ).
15- #6. System Identity Initialization (First Boot Only)
15+ # 6. System Identity Initialization (First Boot Only)
1616# [DEPLOYMENT CONFIG] - Uncomment preferred Authentication Layer
17- #7. Test Multi-Model Swarm (if clients available)
17+ # 7. Test Multi-Model Swarm (if clients available)
1818# =============================================================================
1919
2020import os
2121import time
2222import json
2323import traceback
24+ import hashlib
25+ import getpass
26+ from datetime import datetime
27+ from typing import Optional
2428
2529# Project Andrew Imports
2630from chaos_encryption import generate_raw_q_seed , CPOLQuantumManifold
@@ -136,6 +140,23 @@ def save_api_client_config(clients: dict, filepath: str = "api_clients.json"):
136140
137141 return filepath
138142
143+ def _prompt_password (username : str ) -> Optional [str ]:
144+ """
145+ Optionally set a password for a user during setup.
146+ Returns SHA-256 hash if set, None if skipped.
147+ Family-friendly: explains why before asking.
148+ """
149+ print (f"\n Password for '{ username } ' (optional)" )
150+ print (" Set one if others share this device (e.g. teenagers in the house)." )
151+ want_pw = input (" Set a password? (y/n): " ).strip ().lower ()
152+ if want_pw != 'y' :
153+ return None
154+ while True :
155+ pw = getpass .getpass (" Enter password: " )
156+ pw2 = getpass .getpass (" Confirm password: " )
157+ if pw == pw2 :
158+ return hashlib .sha256 (pw .encode ()).hexdigest ()
159+ print (" Passwords don't match, try again." )
139160
140161# =============================================================================
141162# Main Diagnostic
@@ -179,7 +200,7 @@ def run_system_diagnostic():
179200 print (f"✗ CPOL Initialization failed: { e } " )
180201 return
181202
182- # 4. Initialize Mesh Transport Layer
203+ # 4. Initialize Mesh Transport Layer
183204 print ("\n " + "=" * 70 )
184205 print ("NETWORK TOPOLOGY CONFIGURATION" )
185206 print ("=" * 70 )
@@ -268,11 +289,15 @@ def run_system_diagnostic():
268289 if choice == '1' :
269290 system_id = input ("Enter model/serial number (e.g., AG00001): " ).strip ()
270291 identity_type = 'corporate'
292+ user_passwords = {}
271293 primary_user = input ("Enter facility manager ID: " ).strip ()
294+ user_passwords [primary_user ] = _prompt_password (primary_user )
272295 else :
273296 system_id = input ("Enter given name (e.g., Andrew, Galatea): " ).strip ()
274297 identity_type = 'personal'
298+ user_passwords = {}
275299 primary_user = input ("Enter owner/primary user name: " ).strip ()
300+ user_passwords [primary_user ] = _prompt_password (primary_user )
276301
277302 # Optional: Additional users
278303 add_more = input ("Add additional authorized users? (y/n): " ).strip ().lower ()
@@ -284,6 +309,7 @@ def run_system_diagnostic():
284309 if not user :
285310 break
286311 authorized_users .append (user )
312+ user_passwords [user ] = _prompt_password (user )
287313
288314 # Initialize identity (single call - all params correct)
289315 identity .initialize (
@@ -297,11 +323,13 @@ def run_system_diagnostic():
297323
298324 print ("\n ✓ Identity initialized successfully" )
299325 print (f"✓ { identity .get_identity_summary ()} " )
326+ write_authorized_users (identity , passwords = user_passwords )
300327 else :
301328 # Existing identity loaded
302329 print (f"✓ Identity loaded: { identity .get_identity_summary ()} " )
303330 print (f" Primary user: { identity .identity_data ['primary_user' ]} " )
304331 print (f" Authorized users: { len (identity .identity_data ['authorized_users' ])} " )
332+ write_authorized_users (identity )
305333
306334 # Store identity in shared memory for orchestrator
307335 shared_memory ['system_identity' ] = identity
@@ -329,14 +357,45 @@ def run_system_diagnostic():
329357
330358 # 8. Test Multi-Model Swarm (if clients available)
331359 if shared_memory ['api_clients' ]:
332- print ("\n [STEP 5 ] Testing Multi-Model Swarm..." )
360+ print ("\n [STEP 8 ] Testing Multi-Model Swarm..." )
333361 test_swarm_capabilities (shared_memory ['api_clients' ])
334362
335363 print ("\n " + "=" * 80 )
336364 print (" DIAGNOSTIC COMPLETE: SYSTEM READY FOR SOVEREIGN BOOT" )
337365 print ("=" * 80 )
338366
339367
368+ def write_authorized_users (identity : SystemIdentity , filepath : str = "users.json" , passwords : dict = None ):
369+ """
370+ Derive flat authorized user list from system_identity.
371+ This is the ONLY place users.json gets written.
372+ Runtime session logic reads it but never writes it.
373+ """
374+ primary = identity .identity_data ['primary_user' ]
375+ authorized = identity .identity_data ['authorized_users' ]
376+ sub_users = list (identity .identity_data .get ('user_profiles' , {}).keys ())
377+ passwords = passwords or {}
378+
379+ def make_entry (uid , utype ):
380+ entry = {'id' : uid , 'type' : utype }
381+ pw_hash = passwords .get (uid )
382+ if pw_hash :
383+ entry ['password_hash' ] = pw_hash
384+ return entry
385+
386+ user_list = {
387+ 'users' : (
388+ [make_entry (primary , 'primary' )] +
389+ [make_entry (u , 'authorized' ) for u in authorized if u != primary ] +
390+ [make_entry (u , 'sub_user' ) for u in sub_users ]
391+ ),
392+ 'generated_at' : datetime .utcnow ().isoformat () + "Z" ,
393+ 'source' : 'system_identity'
394+ }
395+ with open (filepath , 'w' ) as f :
396+ json .dump (user_list , f , indent = 2 )
397+ print (f"✓ users.json written: { len (user_list ['users' ])} users" )
398+
340399def test_swarm_capabilities (clients : dict ):
341400 """Test that API clients can actually make calls."""
342401 for provider , client in clients .items ():
0 commit comments