local createSandbox = require('/apis/libsandbox'); local createVersion = require('/apis/libversion'); local URL_SETTING = 'sandbox.url'; local PASSWORD_SETTING = 'sandbox.password'; local LOG_PATH = '/logs/sandbox.log'; -- Single-instance guard: a clean reboot resets _G, so this is a no-op then. It only -- bites if boot ever re-runs in a VM whose previous eventloop survived, which would -- otherwise spawn a second daemon that fights the first over the shared traposId. if _G.__trapos_sandbox_daemon then print('sandbox: daemon already running, skipping.'); return; end local url = settings.get(URL_SETTING); if type(url) ~= 'string' or url == '' then print('sandbox: ' .. URL_SETTING .. ' not set, daemon inactive.'); return; end if not http or not http.websocket then print('sandbox: HTTP/WebSocket unavailable, daemon inactive.'); return; end -- Daemon diagnostics go to a file, never the terminal: the daemon runs headless under -- the boot event loop, so terminal output would corrupt the shell. log(level, msg, fields). local function fileLog(level, msg, fields) local f = fs.open(LOG_PATH, 'a'); if not f then return; end local line = '[' .. os.date('%H:%M:%S') .. '] ' .. string.upper(level) .. ': ' .. msg; if type(fields) == 'table' then for k, v in pairs(fields) do line = line .. ' ' .. tostring(k) .. '=' .. tostring(v); end end f.writeLine(line); f.close(); end local secret = settings.get(PASSWORD_SETTING); _G.__trapos_sandbox_daemon = true; createSandbox().startSession({ eventloop = _G.bootEventLoop, url = url, secret = secret, os = os, log = fileLog, }); print('sandbox v' .. createVersion().forSelf() .. ' started (' .. url .. '), logging to ' .. LOG_PATH .. '.');