2026-07-24 10:39:10 +08:00
"use strict" ;
const crypto = require ( "node:crypto" ) ;
const fs = require ( "node:fs" ) ;
const http = require ( "node:http" ) ;
const path = require ( "node:path" ) ;
const { WorkOrderManager } = require ( "./workorder-manager" ) ;
const { MapGate } = require ( "./map-gate" ) ;
const { sendSmtpMail } = require ( "./smtp-mailer" ) ;
const { executeRegisteredAction } = require ( "./action-client" ) ;
2026-07-26 15:36:14 +08:00
const { enqueueDeploymentEvent } = require ( "./deployment-event" ) ;
2026-07-24 10:39:10 +08:00
const DEFAULT _ACTIONS = Object . freeze ( {
2026-07-26 14:23:47 +08:00
"server-login" : [
"read-navigation-map" ,
"inspect-services" ,
"health-check" ,
2026-07-27 14:33:03 +08:00
"inspect-owner-ssh-login" ,
2026-07-26 14:23:47 +08:00
"inspect-code-channel-owner-auth" ,
] ,
2026-07-24 10:39:10 +08:00
"server-ops" : [
"read-navigation-map" ,
"inspect-services" ,
"pull-registered-repo" ,
"deploy-registered-service" ,
"restart-registered-service" ,
"health-check" ,
"rollback-registered-service" ,
"provision-approved-architecture" ,
"push-repository" ,
2026-07-27 14:33:03 +08:00
"inspect-owner-ssh-login" ,
"disable-owner-password-login" ,
2026-07-24 10:39:10 +08:00
"restore-owner-password-login" ,
2026-07-26 15:02:01 +08:00
"restore-code-channel-owner-login" ,
2026-07-26 15:36:14 +08:00
"dispatch-approved-deployment" ,
2026-07-24 10:39:10 +08:00
] ,
"repo-push" : [ "read-navigation-map" , "push-repository" ] ,
} ) ;
function createApp ( options = { } ) {
const requestToken = options . requestToken || process . env . LAKE _LAMP _REQUEST _TOKEN || "" ;
const ownerEmail = options . ownerEmail || process . env . LAKE _LAMP _OWNER _EMAIL || "" ;
const approvers = options . approvers || loadApprovers ( options . approversFile || process . env . LAKE _LAMP _APPROVERS _FILE || "" , ownerEmail ) ;
const publicBaseUrl = String ( options . publicBaseUrl || process . env . LAKE _LAMP _PUBLIC _URL || "" ) . replace ( /\/$/ , "" ) ;
const targets = new Set ( options . targets || splitCsv ( process . env . LAKE _LAMP _TARGETS || "JD-FD-PRIMARY,BS-GZ-006" ) ) ;
const actions = options . actions || DEFAULT _ACTIONS ;
const manager = options . manager || new WorkOrderManager ( {
approvalTtl : Number ( options . approvalTtl || process . env . LAKE _LAMP _APPROVAL _TTL || 3 * 60 * 60 ) ,
sessionTtl : Number ( options . sessionTtl || process . env . LAKE _LAMP _SESSION _TTL || 3 * 60 * 60 ) ,
maxSessionLifetime : Number ( options . maxSessionLifetime || process . env . LAKE _LAMP _MAX _SESSION _LIFETIME || 24 * 60 * 60 ) ,
stateFile : Object . prototype . hasOwnProperty . call ( options , "stateFile" ) ? options . stateFile : ( process . env . LAKE _LAMP _STATE _FILE || "/var/lib/guanghu/lake-lamp-authz/state.json" ) ,
} ) ;
const sendEmail = options . sendEmail || ( message => sendSmtpMail ( {
... message ,
smtpHost : process . env . SMTP _HOST || "smtp.qq.com" ,
smtpPort : Number ( process . env . SMTP _PORT || 465 ) ,
smtpUser : process . env . SMTP _USER || ownerEmail ,
smtpPass : process . env . QQ _SMTP _AUTH _CODE || "" ,
} ) ) ;
const mapGate = options . mapGate || new MapGate ( {
mapsDir : options . mapsDir || process . env . LAKE _LAMP _MAPS _DIR || "/etc/guanghu/navigation-maps" ,
stateFile : Object . prototype . hasOwnProperty . call ( options , "mapStateFile" ) ? options . mapStateFile : ( process . env . LAKE _LAMP _MAP _STATE _FILE || "/var/lib/guanghu/lake-lamp-authz/map-acks.json" ) ,
} ) ;
const repoGrantDir = options . repoGrantDir || process . env . LAKE _LAMP _REPO _GRANT _DIR || "/var/lib/guanghu/repo-authorizations" ;
2026-07-26 15:36:14 +08:00
const deploymentQueueDir = options . deploymentQueueDir || process . env . LAKE _LAMP _DEPLOYMENT _EVENT _DIR || "/var/lib/guanghu/deployment-events" ;
2026-07-26 17:56:47 +08:00
const deploymentRegistryFile = options . deploymentRegistryFile || process . env . LAKE _LAMP _DEPLOYMENT _REPOSITORIES || "/etc/guanghu/lake-lamp/deployment-repositories.json" ;
2026-07-24 10:39:10 +08:00
const executeAction = options . executeAction || executeRegisteredAction ;
// Creating a powerless request must never become harder than the human mail
// handoff. Keep at least three attempts per network each hour.
const publicCreateLimit = Math . max ( 3 , Number ( options . publicCreateLimit || process . env . LAKE _LAMP _PUBLIC _CREATE _LIMIT || 24 ) ) ;
const publicCreateLimiter = options . publicCreateLimiter || new SlidingWindowLimiter ( publicCreateLimit , 60 * 60 ) ;
const publicCreateGlobalLimiter = options . publicCreateGlobalLimiter || new SlidingWindowLimiter ( Number ( options . publicCreateGlobalLimit || process . env . LAKE _LAMP _PUBLIC _CREATE _GLOBAL _LIMIT || 60 ) , 60 * 60 ) ;
// Owner handoff is a human recovery path, not a login endpoint. Always allow
// at least three genuine mail attempts per network each hour, even if an old
// deployment environment accidentally configures a lower value.
const publicMailLimit = Math . max ( 3 , Number ( options . publicMailLimit || process . env . LAKE _LAMP _PUBLIC _MAIL _LIMIT || 12 ) ) ;
const publicMailLimiter = options . publicMailLimiter || new SlidingWindowLimiter ( publicMailLimit , 60 * 60 ) ;
const publicMailGlobalLimiter = options . publicMailGlobalLimiter || new SlidingWindowLimiter ( Number ( options . publicMailGlobalLimit || process . env . LAKE _LAMP _PUBLIC _MAIL _GLOBAL _LIMIT || 30 ) , 60 * 60 ) ;
async function sendApprovalEmail ( handoffToken ) {
const issued = manager . issueApproval ( handoffToken ) ;
if ( ! issued . ok ) return issued ;
const approver = selectApprover ( approvers , issued . order ) ;
if ( ! approver ) {
manager . failApprovalEmail ( handoffToken ) ;
return { ok : false , reason : "no_registered_approver" } ;
}
2026-07-26 17:56:47 +08:00
if ( ! manager . bindApprover ( handoffToken , approver . id ) ) {
manager . failApprovalEmail ( handoffToken ) ;
return { ok : false , reason : "approver_binding_failed" } ;
}
2026-07-24 10:39:10 +08:00
const approvalUrl = ` ${ publicBaseUrl } /approve/ ${ issued . approvalToken } ` ;
const emailSent = await sendEmail ( {
to : approver . email ,
subject : ` 小湖灯授权请求 · ${ issued . order . target } ` ,
approvalUrl ,
order : issued . order ,
} ) ;
if ( ! emailSent ) {
manager . failApprovalEmail ( handoffToken ) ;
return { ok : false , reason : "authorization_email_failed" } ;
}
return { ok : true , order : issued . order } ;
}
return http . createServer ( async ( req , res ) => {
try {
const url = new URL ( req . url , "http://localhost" ) ;
if ( req . method === "GET" && url . pathname === "/health" ) return json ( res , 200 , {
ok : true ,
service : "lake-lamp-authz" ,
auth _mode : "email-link" ,
approval _ttl : manager . approvalTtl ,
session _ttl : manager . sessionTtl ,
max _session _lifetime : manager . maxSessionLifetime ,
auto _renew _on _activity : true ,
} ) ;
if ( req . method === "GET" && url . pathname === "/api/public/capabilities" ) return json ( res , 200 , {
schema : "guanghu.lake-lamp-public-workorder/v1" ,
create _workorder : ` ${ publicBaseUrl } /api/public/workorders ` ,
required _fields : [ "persona_id" , "target" , "scope" , "action" ] ,
optional _fields : [ "persona_name" , "description" , "resource" ] ,
targets : [ ... targets ] ,
scopes : actions ,
owner _handoff : "open request_url and request pre-registered mailbox verification" ,
2026-07-26 15:26:21 +08:00
workflow : [ "create_workorder" , "owner_handoff" , "claim_session" , "read_navigation_map" , "ack_navigation_map" , "check_session_status" , "execute_registered_action" , "read_operation_receipt" ] ,
diagnostics : diagnosticCatalog ( ) ,
2026-07-24 10:39:10 +08:00
approval _ttl : manager . approvalTtl ,
session _ttl : manager . sessionTtl ,
max _session _lifetime : manager . maxSessionLifetime ,
auto _renew _on _activity : true ,
limits : {
create _per _network _per _hour : publicCreateLimit ,
email _per _network _per _hour : publicMailLimit ,
} ,
} ) ;
const requestMatch = url . pathname . match ( /^\/request\/([A-Za-z0-9_-]{20,})$/ ) ;
if ( requestMatch && req . method === "GET" ) {
const inspected = manager . inspectHandoff ( requestMatch [ 1 ] ) ;
if ( ! inspected . ok ) return html ( res , 410 , requestErrorPage ( inspected . reason ) ) ;
return html ( res , 200 , requestPage ( inspected . order ) ) ;
}
if ( requestMatch && req . method === "POST" ) {
const inspected = manager . inspectHandoff ( requestMatch [ 1 ] ) ;
if ( ! inspected . ok ) return html ( res , 410 , requestErrorPage ( inspected . reason ) ) ;
// Refreshing or reopening an already-sent request must not consume a
// second rate-limit slot. It also must not send a duplicate email.
if ( inspected . order . approval _email _sent ) return html ( res , 200 , emailSentPage ( inspected . order ) ) ;
const source = clientAddress ( req ) ;
if ( ! publicMailLimiter . take ( source ) || ! publicMailGlobalLimiter . take ( "global" ) ) return html ( res , 429 , requestErrorPage ( "rate_limited" ) ) ;
const sent = await sendApprovalEmail ( requestMatch [ 1 ] ) ;
if ( ! sent . ok && sent . reason !== "approval_email_already_sent" ) return html ( res , sent . reason === "authorization_email_failed" ? 502 : 410 , requestErrorPage ( sent . reason ) ) ;
return html ( res , 200 , emailSentPage ( sent . order ) ) ;
}
const approvalMatch = url . pathname . match ( /^\/approve\/([A-Za-z0-9_-]{20,})$/ ) ;
if ( approvalMatch && req . method === "GET" ) {
const inspected = manager . inspectApproval ( approvalMatch [ 1 ] ) ;
if ( ! inspected . ok ) return html ( res , 410 , approvalErrorPage ( inspected . reason ) ) ;
return html ( res , 200 , approvalPage ( inspected . order , approvalMatch [ 1 ] ) ) ;
}
if ( approvalMatch && req . method === "POST" ) {
const approved = manager . approve ( approvalMatch [ 1 ] ) ;
if ( ! approved . ok ) return html ( res , 410 , approvalErrorPage ( approved . reason ) ) ;
return html ( res , 200 , approvedPage ( approved . order ) ) ;
}
if ( req . method === "POST" && url . pathname === "/api/public/workorders" ) {
const source = clientAddress ( req ) ;
if ( ! publicCreateLimiter . take ( source ) || ! publicCreateGlobalLimiter . take ( "global" ) ) return json ( res , 429 , { error : "rate_limited" , retry _after : 3600 } ) ;
const body = await readJson ( req ) ;
if ( ! body ) return json ( res , 400 , { error : "invalid_json" } ) ;
const validation = validateWorkorderBody ( body , targets , actions ) ;
if ( ! validation . ok ) return json ( res , validation . status , { error : validation . error } ) ;
const created = manager . request ( validation . request ) ;
2026-07-26 15:36:14 +08:00
let emailStatus = "not_requested" ;
if ( body . owner _notify === true ) {
if ( ! publicMailLimiter . take ( source ) || ! publicMailGlobalLimiter . take ( "global" ) ) emailStatus = "rate_limited" ;
else {
const sent = await sendApprovalEmail ( created . handoffToken ) ;
emailStatus = sent . ok ? "sent" : String ( sent . reason || "failed" ) ;
}
}
2026-07-24 10:39:10 +08:00
return json ( res , 201 , {
ok : true ,
workorder _id : created . id ,
claim _token : created . claimToken ,
request _url : ` ${ publicBaseUrl } /request/ ${ created . handoffToken } ` ,
expires _in : created . expiresIn ,
2026-07-26 15:36:14 +08:00
status : emailStatus === "sent" ? "waiting_for_owner" : "waiting_for_owner_handoff" ,
email _status : emailStatus ,
receipt : receipt ( { state : emailStatus === "sent" ? "waiting_for_owner" : "waiting_for_owner_handoff" , diagnostic _code : emailStatus === "sent" ? "owner_email_sent" : "owner_handoff_required" , workorder _id : created . id , next _step : emailStatus === "sent" ? "主人邮箱已收到批准链接;等待批准后领取会话。" : "把 request_url 交给主人打开一次;或由光湖语言人格系统在建单时显式提交 owner_notify=true。不要索要密码、验证码或令牌。" } ) ,
2026-07-24 10:39:10 +08:00
} ) ;
}
if ( req . method === "POST" && url . pathname === "/api/workorders" ) {
if ( ! bearerMatches ( req , requestToken ) ) return json ( res , 401 , { error : "request_auth_required" } ) ;
const body = await readJson ( req ) ;
if ( ! body ) return json ( res , 400 , { error : "invalid_json" } ) ;
const validation = validateWorkorderBody ( body , targets , actions ) ;
if ( ! validation . ok ) return json ( res , validation . status , { error : validation . error } ) ;
const expectedFingerprint = sha256 ( ownerEmail . toLowerCase ( ) ) ;
if ( ! body . recipient _fingerprint || ! safeEqual ( body . recipient _fingerprint , expectedFingerprint ) ) return json ( res , 403 , { error : "owner_identity_mismatch" } ) ;
const created = manager . request ( validation . request ) ;
const sent = await sendApprovalEmail ( created . handoffToken ) ;
if ( ! sent . ok ) return json ( res , 502 , { error : sent . reason } ) ;
return json ( res , 201 , { ok : true , workorder _id : created . id , claim _token : created . claimToken , expires _in : created . expiresIn , status : "waiting_for_owner" } ) ;
}
const claimMatch = url . pathname . match ( /^\/api\/workorders\/([0-9a-f-]{36})\/claim$/i ) ;
if ( req . method === "POST" && claimMatch ) {
const token = bearer ( req ) ;
const claimed = manager . claim ( claimMatch [ 1 ] , token ) ;
if ( ! claimed . ok ) return json ( res , claimed . reason === "approval_pending" ? 202 : 403 , { error : claimed . reason } ) ;
2026-07-26 15:26:21 +08:00
return json ( res , 200 , { ok : true , session _token : claimed . sessionToken , expires _in : claimed . expiresIn , target : claimed . target , scope : claimed . scope , action : claimed . action , resource : claimed . resource || "" , receipt : claimed . receipt || receipt ( { state : "session_issued" , diagnostic _code : "session_issued" , workorder _id : claimed . workorderId , next _step : "读取并确认实时导航图。" } ) } ) ;
}
if ( req . method === "POST" && url . pathname === "/api/session/status" ) {
const body = await readJson ( req ) ;
if ( ! body ) return json ( res , 400 , failure ( "invalid_json" ) ) ;
const token = bearer ( req ) ;
const target = String ( body . target || "" ) ;
const scope = String ( body . scope || "" ) ;
const verified = manager . verifySession ( token , { pid : String ( body . persona _id || "" ) } , target , scope , "read-navigation-map" ) ;
if ( ! verified . ok ) return json ( res , 403 , failure ( verified . reason ) ) ;
const map = mapGate . read ( target ) ;
const mapVerified = mapGate . verify ( token , target , map . hash ) ;
return json ( res , 200 , { ok : true , state : mapVerified . ok ? "ready_to_execute" : "map_ack_required" , workorder _id : verified . session . workorderId || "" , target , scope , allowed _actions : verified . session . actions || [ verified . session . action ] , expires _at : verified . session . expiresAt , map : { hash : map . hash , acknowledged : mapVerified . ok } , last _receipt : verified . session . lastReceipt || null , next _step : mapVerified . ok ? "只执行 allowed_actions 中已登记的动作;每次执行后读取 operation receipt。" : "先读取 /api/navigation-map/read, 再提交同一 map_hash 至 /api/navigation-map/ack。" } ) ;
2026-07-24 10:39:10 +08:00
}
if ( req . method === "POST" && url . pathname === "/api/session/verify" ) {
const body = await readJson ( req ) ;
if ( ! body ) return json ( res , 400 , { error : "invalid_json" } ) ;
const verified = manager . verifySession ( bearer ( req ) , { pid : String ( body . persona _id || "" ) } , String ( body . target || "" ) , String ( body . scope || "" ) , String ( body . action || "" ) , Date . now ( ) / 1000 , Object . prototype . hasOwnProperty . call ( body , "resource" ) ? String ( body . resource || "" ) : undefined ) ;
if ( ! verified . ok ) return json ( res , 403 , { error : verified . reason } ) ;
if ( body . action !== "read-navigation-map" ) {
const map = mapGate . read ( String ( body . target || "" ) ) ;
const mapVerified = mapGate . verify ( bearer ( req ) , String ( body . target || "" ) , map . hash ) ;
if ( ! mapVerified . ok ) return json ( res , 423 , { error : mapVerified . reason , required _action : "read-navigation-map" } ) ;
}
return json ( res , 200 , { ok : true , expires _at : verified . session . expiresAt } ) ;
}
if ( req . method === "POST" && url . pathname === "/api/session/renew" ) {
const body = await readJson ( req ) ;
if ( ! body ) return json ( res , 400 , { error : "invalid_json" } ) ;
if ( body . action || body . actions || body . target _override || body . scope _override || body . resource ) return json ( res , 400 , { error : "renewal_cannot_expand_authority" } ) ;
const token = bearer ( req ) ;
const target = String ( body . target || "" ) ;
const scope = String ( body . scope || "" ) ;
const renewed = manager . renewSession ( token , { pid : String ( body . persona _id || "" ) } , target , scope ) ;
if ( ! renewed . ok ) return json ( res , 403 , { error : renewed . reason } ) ;
const map = mapGate . read ( target ) ;
const acked = mapGate . ack ( token , target , map . hash , Date . now ( ) / 1000 , Math . max ( 1 , renewed . expiresAt - Date . now ( ) / 1000 ) ) ;
if ( ! acked . ok ) return json ( res , 409 , { error : acked . reason } ) ;
return json ( res , 200 , { ok : true , target , scope , expires _at : renewed . expiresAt , renewals : renewed . renewals , authority _expanded : false } ) ;
}
if ( req . method === "POST" && url . pathname === "/api/navigation-map/read" ) {
const body = await readJson ( req ) ;
if ( ! body ) return json ( res , 400 , { error : "invalid_json" } ) ;
const verified = manager . verifySession ( bearer ( req ) , { pid : String ( body . persona _id || "" ) } , String ( body . target || "" ) , String ( body . scope || "" ) , "read-navigation-map" ) ;
if ( ! verified . ok ) return json ( res , 403 , { error : verified . reason } ) ;
const map = mapGate . read ( String ( body . target || "" ) ) ;
return json ( res , 200 , { ok : true , target : body . target , map _hash : map . hash , navigation _map : map . data } ) ;
}
if ( req . method === "POST" && url . pathname === "/api/navigation-map/ack" ) {
const body = await readJson ( req ) ;
if ( ! body ) return json ( res , 400 , { error : "invalid_json" } ) ;
const token = bearer ( req ) ;
const verified = manager . verifySession ( token , { pid : String ( body . persona _id || "" ) } , String ( body . target || "" ) , String ( body . scope || "" ) , "read-navigation-map" ) ;
if ( ! verified . ok ) return json ( res , 403 , { error : verified . reason } ) ;
const acked = mapGate . ack ( token , String ( body . target || "" ) , String ( body . map _hash || "" ) , Date . now ( ) / 1000 , Math . max ( 1 , verified . session . expiresAt - Date . now ( ) / 1000 ) ) ;
2026-07-26 15:26:21 +08:00
if ( ! acked . ok ) return json ( res , 409 , failure ( acked . reason ) ) ;
const operationReceipt = receipt ( { state : "map_acknowledged" , diagnostic _code : "map_acknowledged" , workorder _id : verified . session . workorderId , target : body . target , next _step : "可查询 session/status, 再执行本会话 allowed_actions 内的固定动作。" } ) ;
manager . recordReceipt ( token , operationReceipt ) ;
return json ( res , 200 , { ok : true , target : body . target , map _hash : body . map _hash , receipt : operationReceipt } ) ;
2026-07-24 10:39:10 +08:00
}
if ( req . method === "POST" && url . pathname === "/api/actions/execute" ) {
const body = await readJson ( req ) ;
2026-07-26 15:26:21 +08:00
if ( ! body ) return json ( res , 400 , failure ( "invalid_json" ) ) ;
if ( body . cmd || body . command || body . shell || body . args ) return json ( res , 400 , failure ( "arbitrary_command_forbidden" ) ) ;
2026-07-24 10:39:10 +08:00
const token = bearer ( req ) ;
const target = String ( body . target || "" ) ;
const scope = String ( body . scope || "" ) ;
const action = String ( body . action || "" ) ;
const resource = String ( body . resource || "" ) ;
const verified = manager . verifySession ( token , { pid : String ( body . persona _id || "" ) } , target , scope , action , Date . now ( ) / 1000 , resource ) ;
2026-07-26 15:26:21 +08:00
if ( ! verified . ok ) return json ( res , 403 , failure ( verified . reason ) ) ;
2026-07-24 10:39:10 +08:00
const map = mapGate . read ( target ) ;
const mapVerified = mapGate . verify ( token , target , map . hash ) ;
2026-07-26 15:26:21 +08:00
if ( ! mapVerified . ok ) return json ( res , 423 , failure ( mapVerified . reason , "先读取并确认导航图。" , { required _action : "read-navigation-map" } ) ) ;
2026-07-24 10:39:10 +08:00
const result = await executeAction ( resource ? { action , target , resource } : { action , target } ) ;
2026-07-26 15:26:21 +08:00
const operationReceipt = receipt ( { state : result . ok ? "succeeded" : "failed" , diagnostic _code : result . ok ? "action_succeeded" : String ( result . error || "action_execution_failed" ) , workorder _id : verified . session . workorderId , target , action , evidence : safeEvidence ( result ) , next _step : result . ok ? "读取 session/status 确认当前回执;如需新范围、目标或资源,重新发起工单。" : "读取 diagnostic_code 与 evidence; 仅按 next_step 修复,不要切换到其他服务器或猜测凭证。" } ) ;
manager . recordReceipt ( token , operationReceipt ) ;
return json ( res , result . ok ? 200 : 502 , { ... result , receipt : operationReceipt } ) ;
2026-07-24 10:39:10 +08:00
}
if ( req . method === "POST" && url . pathname === "/api/repo-push/grant" ) {
const body = await readJson ( req ) ;
2026-07-26 15:26:21 +08:00
if ( ! body ) return json ( res , 400 , failure ( "invalid_json" ) ) ;
2026-07-24 10:39:10 +08:00
const token = bearer ( req ) ;
const target = String ( body . target || "" ) ;
const scope = String ( body . scope || "repo-push" ) ;
const repo = String ( body . repo || "" ) . toLowerCase ( ) ;
2026-07-26 15:26:21 +08:00
if ( ! /^bingshuo\/[a-z0-9._-]+$/ . test ( repo ) ) return json ( res , 400 , failure ( "repo_not_allowlisted" ) ) ;
2026-07-24 10:39:10 +08:00
const verified = manager . verifySession ( token , { pid : String ( body . persona _id || "" ) } , target , scope , "push-repository" ) ;
2026-07-26 15:26:21 +08:00
if ( ! verified . ok ) return json ( res , 403 , failure ( verified . reason ) ) ;
2026-07-24 10:39:10 +08:00
const map = mapGate . read ( target ) ;
const mapVerified = mapGate . verify ( token , target , map . hash ) ;
2026-07-26 15:26:21 +08:00
if ( ! mapVerified . ok ) return json ( res , 423 , failure ( mapVerified . reason , "先读取并确认导航图。" , { required _action : "read-navigation-map" } ) ) ;
2026-07-24 10:39:10 +08:00
fs . mkdirSync ( repoGrantDir , { recursive : true , mode : 0o2770 } ) ;
const grant = { schema : "guanghu.repo-push-grant/v1" , repo , target , persona _id : body . persona _id , map _hash : map . hash , issued _at : Date . now ( ) / 1000 , expires _at : verified . session . expiresAt } ;
const grantFile = path . join ( repoGrantDir , ` ${ repo . replace ( "/" , "__" ) } .json ` ) ;
const temp = ` ${ grantFile } . ${ process . pid } .tmp ` ;
fs . writeFileSync ( temp , JSON . stringify ( grant ) , { mode : 0o640 } ) ;
fs . renameSync ( temp , grantFile ) ;
2026-07-26 15:26:21 +08:00
const operationReceipt = receipt ( { state : "blocked" , diagnostic _code : "repo_push_transport_unavailable" , workorder _id : verified . session . workorderId , target , action : "push-repository" , next _step : "服务器已登记本次推送许可,但安全推送接收器尚未部署;不要重试裸 git push、不要索要账号密码。等待受限 bundle 接收器上线后按同一工单回执执行。" } ) ;
manager . recordReceipt ( token , operationReceipt ) ;
return json ( res , 200 , { ok : true , repo , target , expires _at : grant . expires _at , transport : { status : "not_configured" , diagnostic _code : "repo_push_transport_unavailable" , next _step : operationReceipt . next _step } , receipt : operationReceipt } ) ;
2026-07-24 10:39:10 +08:00
}
2026-07-26 15:36:14 +08:00
if ( req . method === "POST" && url . pathname === "/api/deployment/dispatch" ) {
const body = await readJson ( req ) ;
if ( ! body ) return json ( res , 400 , failure ( "invalid_json" ) ) ;
const token = bearer ( req ) , target = String ( body . target || "" ) , scope = String ( body . scope || "server-ops" ) ;
const resource = String ( body . resource || "" ) , repo = String ( body . repo || "" ) . toLowerCase ( ) , branch = String ( body . branch || "main" ) , commit = String ( body . commit _sha || "" ) . toLowerCase ( ) , manifest = String ( body . manifest || "" ) ;
const verified = manager . verifySession ( token , { pid : String ( body . persona _id || "" ) } , target , scope , "dispatch-approved-deployment" , Date . now ( ) / 1000 , resource ) ;
if ( ! verified . ok ) return json ( res , 403 , failure ( verified . reason ) ) ;
const map = mapGate . read ( target ) ;
if ( ! mapGate . verify ( token , target , map . hash ) . ok ) return json ( res , 423 , failure ( "map_ack_required" , "先读取并确认导航图。" , { required _action : "read-navigation-map" } ) ) ;
2026-07-26 17:56:47 +08:00
const deploymentRepositories = options . deploymentRepositories || loadDeploymentRepositories ( deploymentRegistryFile ) ;
const queued = enqueueDeploymentEvent ( {
schema : "guanghu.deployment-intent/v1" ,
repo ,
branch ,
commit _sha : commit ,
resource ,
manifest ,
workorder _id : verified . session . workorderId ,
deployment _source : body . deployment _source || null ,
} , { repo , branch , commit _sha : commit } , deploymentQueueDir , {
authorizer _id : verified . session . authorizerId ,
persona _id : verified . session . persona . pid ,
execution _runtime _id : String ( body . execution _runtime _id || "" ) ,
target ,
registry : deploymentRepositories ,
} ) ;
2026-07-26 15:36:14 +08:00
const operationReceipt = receipt ( { state : queued . state === "queued_for_resident_agent" ? "queued" : "blocked" , diagnostic _code : queued . diagnostic _code || "deployment_event_queued" , workorder _id : verified . session . workorderId , target , action : "dispatch-approved-deployment" , evidence : { repo , branch , commit _sha : commit , event _id : queued . event _id || "" } , next _step : queued . state === "queued_for_resident_agent" ? "常驻部署 Agent 将读取该事件并回写部署、健康检查或回滚回执。" : "修正部署绑定信息后重新申请或派发,不要让服务器自行扫描提交。" } ) ;
manager . recordReceipt ( token , operationReceipt ) ;
return json ( res , queued . state === "queued_for_resident_agent" ? 202 : 400 , { ok : queued . state === "queued_for_resident_agent" , deployment : queued , receipt : operationReceipt } ) ;
}
2026-07-24 10:39:10 +08:00
return json ( res , 404 , { error : "not_found" } ) ;
} catch ( error ) {
process . stderr . write ( ` lake-lamp request error: ${ String ( error && error . message || "unknown" ) . slice ( 0 , 240 ) } \n ` ) ;
return json ( res , error && error . code === "BODY_TOO_LARGE" ? 413 : 500 , { error : "request_failed" } ) ;
}
} ) ;
}
2026-07-26 17:56:47 +08:00
function loadDeploymentRepositories ( file ) {
const parsed = JSON . parse ( fs . readFileSync ( file , "utf8" ) ) ;
if ( ! parsed || ! parsed . repos || typeof parsed . repos !== "object" ) throw new Error ( "invalid_deployment_repository_registry" ) ;
return parsed . repos ;
}
2026-07-24 10:39:10 +08:00
function approvalPage ( order , token ) {
return document ( "小湖灯授权请求" , `
< p class = "eyebrow" > LAKE LAMP SECURITY PROTOCOL < / p >
< h1 > 人格体请求进入一台服务器 < / h 1 >
< div class = "panel" >
< dl > < dt > 人格体 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . p e r s o n a . n a m e ) } < s m a l l > $ { e s c a p e H t m l ( o r d e r . p e r s o n a . p i d ) } < / s m a l l > < / d d >
< dt > 申请入口 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . p r o v e n a n c e & & o r d e r . p r o v e n a n c e . s y s t e m _ e n t r y | | " 旧 版 工 单 " ) } < / d d >
< dt > 实例来源 < / d t > < d d > $ { e s c a p e H t m l ( o r i g i n L a b e l ( o r d e r ) ) } < / d d >
< dt > 目标节点 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . t a r g e t ) } < / d d > < d t > 授 权 范 围 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . s c o p e ) } < / d d >
< dt > 进入动作 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . a c t i o n ) } < / d d > < d t > 会 话 能 力 < / d t > < d d > $ { e s c a p e H t m l ( ( o r d e r . a l l o w e d _ a c t i o n s | | [ o r d e r . a c t i o n ] ) . j o i n ( " · " ) ) } < / d d >
< dt > 绑定资源 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . r e s o u r c e | | " 无 " ) } < / d d >
< dt > 说明 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . d e s c r i p t i o n | | " 未 附 加 说 明 " ) } < / d d > < / d l >
< / d i v >
< p class = "notice" > 一次确认将打开这台服务器上的三小时受限运维会话 。 人格体持续执行已绑定任务时会自动续期 ; 切换服务器 、 扩大范围 、 切换绑定资源或停止活动后过期才需重新申请 。 < / p >
< form method = "post" > < button type = "submit" > 打开三小时受限运维会话 < / b u t t o n > < / f o r m >
` );
}
function requestPage ( order ) {
return document ( "小湖灯跨设备授权" , `
< p class = "eyebrow" > CROSS - DEVICE HANDOFF < / p >
< h1 > 核对这张无权限申请单 < / h 1 >
< div class = "panel" >
< dl > < dt > 人格体 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . p e r s o n a . n a m e ) } < s m a l l > $ { e s c a p e H t m l ( o r d e r . p e r s o n a . p i d ) } < / s m a l l > < / d d >
< dt > 申请入口 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . p r o v e n a n c e & & o r d e r . p r o v e n a n c e . s y s t e m _ e n t r y | | " 旧 版 工 单 " ) } < / d d >
< dt > 实例来源 < / d t > < d d > $ { e s c a p e H t m l ( o r i g i n L a b e l ( o r d e r ) ) } < / d d >
< dt > 目标节点 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . t a r g e t ) } < / d d > < d t > 授 权 范 围 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . s c o p e ) } < / d d >
< dt > 登记动作 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . a c t i o n ) } < / d d > < d t > 绑 定 资 源 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . r e s o u r c e | | " 无 " ) } < / d d > < d t > 说 明 < / d t > < d d > $ { e s c a p e H t m l ( o r d e r . d e s c r i p t i o n | | " 未 附 加 说 明 " ) } < / d d > < / d l >
< / d i v >
< p class = "notice" > 这张页面本身没有执行权 。 确认内容无误后 , 服务器只会向预登记邮箱发送一次真正的批准链接 。 < / p >
< form method = "post" > < button type = "submit" > 发送我的授权邮件 < / b u t t o n > < / f o r m >
` );
}
function emailSentPage ( order ) {
return document ( "授权邮件已发送" , ` <p class="eyebrow">OWNER VERIFICATION</p><h1>请打开邮箱完成批准</h1><div class="panel"><p>申请单已锁定到 <strong> ${ escapeHtml ( order && order . target || "登记节点" ) } </strong>。真正的批准链接只发送到服务器预登记邮箱。</p></div><p class="notice">批准后回到原来的 AI 对话,让它领取一次性会话。无需向 AI 提供验证码、密码或邮箱授权码。</p> ` ) ;
}
function requestErrorPage ( reason ) {
const messages = {
rate _limited : "请求过于频繁,请稍后再试。" ,
approval _email _already _sent : "授权邮件已经发送,请直接检查邮箱。" ,
authorization _email _failed : "授权邮件暂时发送失败,请稍后重试。" ,
} ;
if ( reason === "rate_limited" ) return document ( "发送频率保护" , ` <p class="eyebrow">RATE LIMIT · REQUEST KEPT</p><h1>小湖灯先替你守住这张申请单</h1><div class="panel"><p>当前网络在一小时内触发邮件的次数较多,发送动作被暂时暂停。</p></div><p class="notice">申请单本身没有被关闭。请稍后再试,或切换到手机流量后只点击一次。无需重新填写,也不要连续刷新。</p> ` ) ;
return document ( "申请单不可用" , ` <p class="eyebrow">REQUEST CLOSED</p><h1>这张申请单现在不能继续</h1><p class="notice"> ${ escapeHtml ( messages [ reason ] || ` 原因: ${ reason } ` ) } </p> ` ) ;
}
function approvedPage ( order ) {
return document ( "授权完成" , ` <p class="eyebrow">THREE-HOUR OPS SESSION</p><h1>三小时运维会话已打开</h1><div class="panel"><p> ${ escapeHtml ( order . persona . name ) } 已获准在 <strong> ${ escapeHtml ( order . target ) } </strong> 上执行本范围内的已登记能力。</p></div><p class="notice">可以关闭本页面。人格体持续执行原绑定任务时会自动续期;切换服务器、扩大范围、切换绑定资源或停止活动后过期才重新授权。</p> ` ) ;
}
function approvalErrorPage ( reason ) {
return document ( "链接不可用" , ` <p class="eyebrow">LINK CLOSED</p><h1>这条授权链接已经失效</h1><p class="notice">原因: ${ escapeHtml ( reason ) } 。如仍需操作,请让人格体重新提交工单。</p> ` ) ;
}
function document ( title , body ) {
return ` <!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title> ${ escapeHtml ( title ) } · 光湖</title><style>
: root { color - scheme : dark } * { box - sizing : border - box } body { margin : 0 ; min - height : 100 vh ; display : grid ; place - items : center ; background : radial - gradient ( circle at 20 % 10 % , # 17344 d , # 09111 b 55 % , # 05090 e ) ; color : # eaf4fb ; font : 16 px / 1.7 - apple - system , BlinkMacSystemFont , "Segoe UI" , sans - serif ; padding : 24 px } . shell { width : min ( 680 px , 100 % ) ; padding : 42 px ; border : 1 px solid # 29475 d ; border - radius : 24 px ; background : rgba ( 10 , 22 , 33 , . 94 ) ; box - shadow : 0 24 px 80 px # 0008 } . eyebrow { color : # 6 ed5ff ; letter - spacing : . 18 em ; font - size : 12 px } h1 { font - size : clamp ( 30 px , 6 vw , 48 px ) ; line - height : 1.15 ; margin : 10 px 0 28 px } . panel { background : # 102638 ; border : 1 px solid # 24465 d ; border - radius : 16 px ; padding : 20 px 24 px } dl { display : grid ; grid - template - columns : 110 px 1 fr ; gap : 12 px ; margin : 0 } dt { color : # 8 ba4b6 } dd { margin : 0 ; font - weight : 650 } small { display : block ; color : # 7893 a6 ; font - weight : 400 } . notice { color : # 9 eb2c0 ; margin : 20 px 0 } button { width : 100 % ; border : 0 ; border - radius : 14 px ; padding : 16 px ; background : # 67 d4ff ; color : # 042235 ; font - weight : 800 ; font - size : 17 px ; cursor : pointer } @ media ( max - width : 520 px ) { . shell { padding : 28 px 22 px } dl { grid - template - columns : 1 fr ; gap : 2 px } dd { margin - bottom : 12 px } }
< / s t y l e > < / h e a d > < b o d y > < m a i n c l a s s = " s h e l l " > $ { b o d y } < / m a i n > < / b o d y > < / h t m l > ` ;
}
function readJson ( req ) {
return new Promise ( ( resolve , reject ) => {
let raw = "" ;
req . on ( "data" , chunk => { raw += chunk ; if ( raw . length > 32 * 1024 ) { const error = new Error ( "body too large" ) ; error . code = "BODY_TOO_LARGE" ; reject ( error ) ; req . destroy ( ) ; } } ) ;
req . on ( "end" , ( ) => { try { resolve ( JSON . parse ( raw || "{}" ) ) ; } catch { resolve ( null ) ; } } ) ;
req . on ( "error" , reject ) ;
} ) ;
}
function bearer ( req ) { return String ( req . headers . authorization || "" ) . replace ( /^Bearer\s+/i , "" ) ; }
function bearerMatches ( req , expected ) { return Boolean ( expected ) && safeEqual ( bearer ( req ) , expected ) ; }
function safeEqual ( left , right ) { const a = Buffer . from ( String ( left ) ) ; const b = Buffer . from ( String ( right ) ) ; return a . length === b . length && crypto . timingSafeEqual ( a , b ) ; }
function sha256 ( value ) { return crypto . createHash ( "sha256" ) . update ( String ( value ) ) . digest ( "hex" ) ; }
function splitCsv ( value ) { return value . split ( "," ) . map ( item => item . trim ( ) ) . filter ( Boolean ) ; }
function loadApprovers ( file , ownerEmail ) {
2026-07-26 17:56:47 +08:00
if ( ! file ) return ownerEmail ? [ { id : "ICE-GL∞" , email : ownerEmail , default : true , persona _ids : [ ] , targets : [ "*" ] , scopes : [ "*" ] } ] : [ ] ;
2026-07-24 10:39:10 +08:00
const parsed = JSON . parse ( fs . readFileSync ( file , "utf8" ) ) ;
if ( ! parsed || ! Array . isArray ( parsed . approvers ) ) throw new Error ( "invalid approver registry" ) ;
return parsed . approvers . filter ( item => item && validEmail ( item . email ) ) . map ( item => ( {
id : String ( item . id || "" ) , email : item . email , default : item . default === true ,
persona _ids : Array . isArray ( item . persona _ids ) ? item . persona _ids . map ( String ) : [ ] ,
targets : Array . isArray ( item . targets ) ? item . targets . map ( String ) : [ ] ,
scopes : Array . isArray ( item . scopes ) ? item . scopes . map ( String ) : [ ] ,
} ) ) ;
}
function selectApprover ( approvers , order ) {
const eligible = approvers . filter ( item => matches ( item . targets , order . target ) && matches ( item . scopes , order . scope ) ) ;
return eligible . find ( item => item . persona _ids . includes ( order . persona . pid ) ) || eligible . find ( item => item . default ) || null ;
}
function matches ( values , value ) { return values . includes ( "*" ) || values . includes ( value ) ; }
function validEmail ( value ) { return typeof value === "string" && value . length <= 254 && /^[^@\s]+@[^@\s]+$/ . test ( value ) ; }
function clientAddress ( req ) {
const forwarded = String ( req . headers [ "x-forwarded-for" ] || "" ) . split ( "," ) . map ( value => value . trim ( ) ) . filter ( Boolean ) ;
return String ( forwarded [ forwarded . length - 1 ] || req . socket . remoteAddress || "unknown" ) . slice ( 0 , 96 ) ;
}
2026-07-26 15:26:21 +08:00
function receipt ( { state , diagnostic _code , workorder _id = "" , target = "" , action = "" , evidence = null , next _step = "" } ) {
return { schema : "guanghu.operation-receipt/v1" , state , diagnostic _code , workorder _id , target , action , occurred _at : Date . now ( ) / 1000 , ... ( evidence ? { evidence } : { } ) , next _step } ;
}
function safeEvidence ( result ) {
const clip = value => String ( value || "" ) . replace ( /(password|token|secret|authorization)\s*[:=]\s*\S+/gi , "$1=[redacted]" ) . slice ( 0 , 1200 ) ;
return { exit _code : Number . isInteger ( result . exit _code ) ? result . exit _code : null , stdout : clip ( result . stdout ) , stderr : clip ( result . stderr ) } ;
}
function failure ( error , next _step = "读取 diagnostic_code; 按 next_step 处理,勿猜测凭证或切换服务器。" , extra = { } ) {
return { ok : false , error , receipt : receipt ( { state : "blocked" , diagnostic _code : error , next _step } ) , ... extra } ;
}
function diagnosticCatalog ( ) {
return {
owner _handoff _required : "工单已创建,等待主人打开申请页并完成预登记邮箱批准。" ,
map _ack _required : "会话有效,但尚未确认此目标节点的实时导航图。" ,
action _execution _failed : "服务器固定动作已执行但失败;回执会包含受限证据与下一步。" ,
repo _push _transport _unavailable : "许可已登记,但安全推送接收器尚未部署,禁止把它误判为 git 凭证。" ,
session _expired : "会话已过期;以同一目标和范围重新申请工单。" ,
} ;
}
2026-07-24 10:39:10 +08:00
function validateWorkorderBody ( body , targets , actions ) {
if ( body . email || body . recipient || body . smtp _pass ) return { ok : false , status : 400 , error : "direct_recipient_forbidden" } ;
if ( ! body . persona _id || ! body . target || ! body . scope || ! body . action ) return { ok : false , status : 400 , error : "missing_required_field" } ;
const personaId = String ( body . persona _id ) ;
const personaName = String ( body . persona _name || personaId ) ;
const target = String ( body . target ) ;
const scope = String ( body . scope ) ;
const action = String ( body . action ) ;
const description = String ( body . description || "" ) ;
const resource = String ( body . resource || "" ) ;
const provenance = {
system _entry : String ( body . system _entry || "" ) ,
software : String ( body . origin _software || "" ) ,
model : String ( body . origin _model || "" ) ,
instance : String ( body . origin _instance || "" ) ,
} ;
if ( ! /^[A-Za-z0-9._:+\u221e-]{2,80}$/ . test ( personaId ) || personaName . length > 100 || description . length > 500 ) return { ok : false , status : 400 , error : "invalid_request_fields" } ;
if ( ! targets . has ( target ) ) return { ok : false , status : 400 , error : "unknown_target" } ;
if ( ! Array . isArray ( actions [ scope ] ) || ! actions [ scope ] . includes ( action ) ) return { ok : false , status : 400 , error : "unknown_or_mismatched_action" } ;
2026-07-26 15:36:14 +08:00
const immutableResourceAction = action === "provision-approved-architecture" || action === "dispatch-approved-deployment" ;
if ( immutableResourceAction && ! /^[A-Z0-9][A-Z0-9._-]{5,119}@[0-9a-f]{40}$/ . test ( resource ) ) return { ok : false , status : 400 , error : "immutable_architecture_resource_required" } ;
if ( ! immutableResourceAction && resource ) return { ok : false , status : 400 , error : "resource_not_allowed_for_action" } ;
if ( body . owner _notify !== undefined && typeof body . owner _notify !== "boolean" ) return { ok : false , status : 400 , error : "invalid_owner_notify" } ;
if ( body . owner _notify === true && provenance . system _entry !== "光湖语言人格系统当前实例" ) return { ok : false , status : 400 , error : "owner_notify_requires_language_system_provenance" } ;
2026-07-24 10:39:10 +08:00
if ( Object . values ( provenance ) . some ( Boolean ) && ( provenance . system _entry !== "光湖语言人格系统当前实例" || Object . values ( provenance ) . some ( item => ! item || item . length > 120 ) ) ) return { ok : false , status : 400 , error : "invalid_instance_provenance" } ;
return { ok : true , request : { persona : { pid : personaId , name : personaName } , provenance , target , scope , action , allowedActions : actions [ scope ] , description , resource } } ;
}
function originLabel ( order ) {
const value = order && order . provenance || { } ;
return value . software || value . model || value . instance ? ` ${ value . software || "未知软件" } · ${ value . model || "未知模型" } · ${ value . instance || "当前实例" } ` : "旧版工单未记录" ;
}
class SlidingWindowLimiter {
constructor ( limit , windowSeconds ) { this . limit = Math . max ( 1 , limit ) ; this . windowMs = windowSeconds * 1000 ; this . events = new Map ( ) ; this . calls = 0 ; }
take ( key , now = Date . now ( ) ) {
this . calls += 1 ;
if ( this . calls % 256 === 0 ) {
for ( const [ storedKey , values ] of this . events ) {
const active = values . filter ( value => now - value < this . windowMs ) ;
if ( active . length ) this . events . set ( storedKey , active ) ; else this . events . delete ( storedKey ) ;
}
}
const recent = ( this . events . get ( key ) || [ ] ) . filter ( value => now - value < this . windowMs ) ;
if ( recent . length >= this . limit ) { this . events . set ( key , recent ) ; return false ; }
recent . push ( now ) ; this . events . set ( key , recent ) ; return true ;
}
}
function escapeHtml ( value ) { return String ( value ) . replace ( /[&<>"']/g , char => ( { "&" : "&" , "<" : "<" , ">" : ">" , '"' : """ , "'" : "'" } ) [ char ] ) ; }
function json ( res , status , value ) { res . writeHead ( status , { "content-type" : "application/json; charset=utf-8" , "cache-control" : "no-store" , "x-content-type-options" : "nosniff" } ) ; res . end ( JSON . stringify ( value ) ) ; }
function html ( res , status , value ) { res . writeHead ( status , { "content-type" : "text/html; charset=utf-8" , "cache-control" : "no-store" , "content-security-policy" : "default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'" , "referrer-policy" : "no-referrer" , "x-content-type-options" : "nosniff" } ) ; res . end ( value ) ; }
if ( require . main === module ) {
const host = process . env . LAKE _LAMP _HOST || "127.0.0.1" ;
const port = Number ( process . env . LAKE _LAMP _PORT || 3921 ) ;
createApp ( ) . listen ( port , host , ( ) => process . stdout . write ( ` lake-lamp-authz listening on ${ host } : ${ port } \n ` ) ) ;
}
module . exports = { createApp , DEFAULT _ACTIONS , SlidingWindowLimiter , loadApprovers , selectApprover } ;