guanghu-ice-heart/server-tools/lake-lamp-authz/server.js

1451 lines
75 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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");
const { enqueueDeploymentEvent } = require("./deployment-event");
const { GuanghuRouter, loadDevices } = require("./guanghu-router");
const { HoloLakeSessionManager } = require("./hololake-session");
const { GhdrAuthorizer } = require("./ghdr-authorizer");
const { GhdrControllerBroker, loadControllers } = require("./ghdr-controller-broker");
const {
HoloLakeAiGateway,
HoloLakeKnowledgeProvider,
loadAiProviders,
} = require("./hololake-capabilities");
const {
loadRegistry: loadRepoPushRegistry,
receiveBundle,
resolveRepository,
} = require("./repo-push-broker");
const DEFAULT_ACTIONS = Object.freeze({
"server-login": [
"read-navigation-map",
"inspect-services",
"health-check",
"inspect-owner-ssh-login",
"inspect-code-channel-owner-auth",
],
"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",
"inspect-owner-ssh-login",
"disable-owner-password-login",
"restore-owner-password-login",
"restore-code-channel-owner-login",
"dispatch-approved-deployment",
],
"repo-push": ["read-navigation-map", "push-repository"],
"linked-node-ops": ["authorize-linked-node-session"],
"native-recovery": ["read-navigation-map", "sign-native-layout-plan"],
});
function createApp(options = {}) {
const requestToken = options.requestToken || process.env.LAKE_LAMP_REQUEST_TOKEN || "";
const broadcastToken = options.broadcastToken || process.env.LAKE_LAMP_BROADCAST_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"));
targets.add("GH-CVM-MAIN-PROD-01");
const actions = options.actions || DEFAULT_ACTIONS;
const devices = options.devices || loadDevices(
options.devicesFile
|| process.env.GUANGHU_ROUTER_DEVICES_FILE
|| "/etc/guanghu/lake-lamp/hololake-devices.json",
);
const router = options.router || new GuanghuRouter({ devices });
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 hololakePepper = String(
options.hololakeSessionPepper
|| process.env.HOLOLAKE_SESSION_PEPPER
|| "",
);
const hololakeSessionManager = options.hololakeSessionManager || (
hololakePepper
? new HoloLakeSessionManager({
registeredEmails: approvers.map(item => item.email),
pepper: hololakePepper,
stateFile: Object.prototype.hasOwnProperty.call(options, "hololakeSessionStateFile")
? options.hololakeSessionStateFile
: (
process.env.HOLOLAKE_SESSION_STATE_FILE
|| "/var/lib/guanghu/lake-lamp-authz/hololake-sessions.json"
),
otpTtlSeconds: Number(
options.hololakeOtpTtlSeconds
|| process.env.HOLOLAKE_OTP_TTL
|| 10 * 60,
),
sessionTtlSeconds: Number(
options.hololakeSessionTtlSeconds
|| process.env.HOLOLAKE_ACCOUNT_SESSION_TTL
|| 24 * 60 * 60,
),
requestLimit: Number(
options.hololakeOtpRequestLimit
|| process.env.HOLOLAKE_OTP_REQUEST_LIMIT
|| 6,
),
sendEmail,
})
: null
);
const hololakeKnowledgePath = String(
options.hololakeKnowledgeRepositoryPath
|| process.env.HOLOLAKE_KNOWLEDGE_REPOSITORY_PATH
|| "",
);
const hololakeKnowledgeProvider = options.hololakeKnowledgeProvider || (
hololakeKnowledgePath
? new HoloLakeKnowledgeProvider({
repositoryId: "bingshuo/hololake-knowledge-base",
repositoryPath: hololakeKnowledgePath,
maxArchiveBytes: Number(
options.hololakeKnowledgeMaxArchiveBytes
|| process.env.HOLOLAKE_KNOWLEDGE_MAX_ARCHIVE_BYTES
|| 128 * 1024 * 1024,
),
})
: null
);
const hololakeAiProvidersFile = String(
options.hololakeAiProvidersFile
|| process.env.HOLOLAKE_AI_PROVIDERS_FILE
|| "",
);
const hololakeAiProviders = options.hololakeAiProviders || (
hololakeAiProvidersFile ? loadAiProviders(hololakeAiProvidersFile) : {}
);
const hololakeAiGateway = options.hololakeAiGateway || (
Object.keys(hololakeAiProviders).length > 0
? new HoloLakeAiGateway({ providers: hololakeAiProviders })
: null
);
const mapGate = options.mapGate || new MapGate({
mapsDir: options.mapsDir || process.env.LAKE_LAMP_MAPS_DIR || "/etc/guanghu/navigation-maps",
fallbackMapsDir: options.fallbackMapsDir || path.join(__dirname, "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";
const repoUploadDir = options.repoUploadDir || process.env.LAKE_LAMP_REPO_UPLOAD_DIR || "/var/lib/guanghu/repo-push-uploads";
const repoPushRegistryFile = options.repoPushRegistryFile || process.env.LAKE_LAMP_REPO_PUSH_REGISTRY || "/etc/guanghu/lake-lamp/repo-push-registry.json";
const repoPushRegistry = options.repoPushRegistry || (
fs.existsSync(repoPushRegistryFile) ? loadRepoPushRegistry(repoPushRegistryFile) : {}
);
const receiveRepoBundle = options.receiveRepoBundle || (
request => receiveBundle(request, {
registry: repoPushRegistry,
uploadDir: repoUploadDir,
})
);
const maxRepoBundleBytes = Math.max(
1024 * 1024,
Number(options.maxRepoBundleBytes || process.env.LAKE_LAMP_MAX_REPO_BUNDLE_BYTES || 256 * 1024 * 1024),
);
const maxRepoChunkBytes = Math.max(
8 * 1024,
Math.min(
48 * 1024,
Number(options.maxRepoChunkBytes || process.env.LAKE_LAMP_MAX_REPO_CHUNK_BYTES || 32 * 1024),
),
);
const deploymentQueueDir = options.deploymentQueueDir || process.env.LAKE_LAMP_DEPLOYMENT_EVENT_DIR || "/var/lib/guanghu/deployment-events";
const deploymentRegistryFile = options.deploymentRegistryFile || process.env.LAKE_LAMP_DEPLOYMENT_REPOSITORIES || "/etc/guanghu/lake-lamp/deployment-repositories.json";
const executeAction = options.executeAction || executeRegisteredAction;
let ghdrAuthorizer = null;
const getGhdrAuthorizer = options.getGhdrAuthorizer || (() => {
if (!ghdrAuthorizer) {
ghdrAuthorizer = new GhdrAuthorizer({
privateKeyFile: options.ghdrAuthorizerPrivateKeyFile
|| process.env.GHDR_AUTHORIZER_PRIVATE_KEY_FILE
|| "/var/lib/guanghu/lake-lamp-authz/ghdr-authorizer-private.pem",
});
}
return ghdrAuthorizer;
});
let ghdrControllerBroker = null;
const getGhdrControllerBroker = options.getGhdrControllerBroker || (() => {
if (!ghdrControllerBroker) {
const registryFile = options.ghdrControllerRegistryFile
|| process.env.GHDR_CONTROLLER_REGISTRY_FILE
|| path.join(__dirname, "ghdr-controllers.json");
ghdrControllerBroker = new GhdrControllerBroker({
controllers: loadControllers(registryFile),
stateDir: options.ghdrJobStateDir
|| process.env.GHDR_JOB_STATE_DIR
|| "/var/lib/guanghu/lake-lamp-authz/ghdr-jobs",
authorizer: getGhdrAuthorizer(),
});
}
return ghdrControllerBroker;
});
const signGhdrPlan = options.signGhdrPlan || (
request => getGhdrControllerBroker().queueAndWait(request)
);
// 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" };
}
if (!manager.bindApprover(handoffToken, approver.id)) {
manager.failApprovalEmail(handoffToken);
return { ok: false, reason: "approver_binding_failed" };
}
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 };
}
function authenticateHoloLake(req) {
if (!hololakeSessionManager) {
return { ok: false, status: 503, error: "hololake_session_unavailable" };
}
const token = bearer(req);
if (!token) return { ok: false, status: 401, error: "session_required" };
const deviceId = String(req.headers["x-hololake-device-id"] || "");
const authenticated = hololakeSessionManager.authenticate(token, deviceId);
if (!authenticated.ok) {
return {
...authenticated,
status: authenticated.error === "session_device_mismatch" ? 403 : 401,
};
}
return { ...authenticated, token, deviceId };
}
function bindAndDeliver(created) {
const inspected = manager.inspectHandoff(created.handoffToken);
if (!inspected.ok) return { delivered: 0, approver: null, order: null };
const approver = selectApprover(approvers, inspected.order);
if (!approver || !manager.bindApprover(created.handoffToken, approver.id)) {
return { delivered: 0, approver: null, order: inspected.order };
}
const bound = manager.inspectHandoff(created.handoffToken);
const order = bound.ok ? bound.order : inspected.order;
return {
approver,
order,
delivered: router.deliver(approver.id, 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: "guanghu-router-with-email-fallback",
primary_authorization_channel: "guanghu_router",
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: "an online HoloLake receives the authorization card through the Guanghu Router; request_url is an email recovery fallback only",
email_visibility: "the requesting AI never receives the submitted mailbox address",
public_auto_email: false,
workflow: ["create_workorder", "guanghu_router_authorization_or_email_fallback", "claim_session", "read_navigation_map", "ack_navigation_map", "check_session_status", "execute_registered_action", "read_operation_receipt"],
diagnostics: diagnosticCatalog(),
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,
},
});
if (
req.method === "POST"
&& url.pathname === "/api/hololake/session/email/request"
) {
if (!hololakeSessionManager) {
return json(res, 503, failure("hololake_session_unavailable"));
}
const body = await readJson(req);
if (!body) return json(res, 400, failure("invalid_json"));
const headerDeviceId = String(
req.headers["x-hololake-device-id"] || "",
);
if (
headerDeviceId
&& body.device_id
&& !safeEqual(headerDeviceId, String(body.device_id))
) {
return json(res, 400, failure("invalid_device"));
}
const requested = await hololakeSessionManager.requestOtp({
email: body.email,
deviceId: String(body.device_id || headerDeviceId),
networkKey: clientAddress(req),
});
if (!requested.accepted) {
return json(
res,
requested.error === "rate_limited" ? 429 : 400,
failure(requested.error),
);
}
return json(res, 202, {
accepted: true,
request_id: requested.request_id,
expires_in: Number(
options.hololakeOtpTtlSeconds
|| process.env.HOLOLAKE_OTP_TTL
|| 10 * 60,
),
next_step: "如果邮箱已登记,输入邮件中的六位验证码。",
});
}
if (
req.method === "POST"
&& url.pathname === "/api/hololake/session/email/verify"
) {
if (!hololakeSessionManager) {
return json(res, 503, failure("hololake_session_unavailable"));
}
const body = await readJson(req);
if (!body) return json(res, 400, failure("invalid_json"));
const headerDeviceId = String(
req.headers["x-hololake-device-id"] || "",
);
if (
headerDeviceId
&& body.device_id
&& !safeEqual(headerDeviceId, String(body.device_id))
) {
return json(res, 400, failure("invalid_device"));
}
const verified = hololakeSessionManager.verifyOtp({
requestId: body.request_id,
code: body.code,
deviceId: String(body.device_id || headerDeviceId),
});
if (!verified.ok) return json(res, 403, failure(verified.error));
return json(res, 200, verified);
}
if (
(req.method === "GET" || req.method === "DELETE")
&& url.pathname === "/api/hololake/session"
) {
const authenticated = authenticateHoloLake(req);
if (!authenticated.ok) {
return json(
res,
authenticated.status,
failure(authenticated.error),
);
}
if (req.method === "DELETE") {
const revoked = hololakeSessionManager.revoke(
authenticated.token,
authenticated.deviceId,
);
return json(res, revoked.ok ? 200 : 401, revoked);
}
return json(res, 200, {
ok: true,
session: authenticated.session,
});
}
if (
req.method === "GET"
&& url.pathname === "/api/hololake/knowledge/manifest"
) {
const authenticated = authenticateHoloLake(req);
if (!authenticated.ok) {
return json(
res,
authenticated.status,
failure(authenticated.error),
);
}
if (!hololakeKnowledgeProvider) {
return json(res, 503, failure("knowledge_repository_unavailable"));
}
try {
return json(res, 200, hololakeKnowledgeProvider.manifest());
} catch {
return json(res, 503, failure("knowledge_repository_unavailable"));
}
}
if (
req.method === "GET"
&& url.pathname === "/api/hololake/knowledge/archive"
) {
const authenticated = authenticateHoloLake(req);
if (!authenticated.ok) {
return json(
res,
authenticated.status,
failure(authenticated.error),
);
}
if (!hololakeKnowledgeProvider) {
return json(res, 503, failure("knowledge_repository_unavailable"));
}
try {
const archive = hololakeKnowledgeProvider.archive(
url.searchParams.get("commit"),
);
res.writeHead(200, {
"content-type": archive.content_type,
"content-length": archive.body.length,
"content-disposition": `attachment; filename="hololake-knowledge-${archive.commit}.zip"`,
"cache-control": "private, no-store",
"x-content-type-options": "nosniff",
"x-hololake-commit": archive.commit,
"x-content-sha256": archive.sha256,
});
res.end(archive.body);
return;
} catch (error) {
const code = error && error.message === "knowledge_commit_not_current"
? "knowledge_commit_not_current"
: "knowledge_archive_unavailable";
return json(res, code === "knowledge_commit_not_current" ? 409 : 503, failure(code));
}
}
if (
req.method === "GET"
&& url.pathname === "/api/hololake/ai/catalog"
) {
const authenticated = authenticateHoloLake(req);
if (!authenticated.ok) {
return json(
res,
authenticated.status,
failure(authenticated.error),
);
}
if (!hololakeAiGateway) {
return json(res, 503, failure("ai_gateway_unavailable"));
}
return json(res, 200, hololakeAiGateway.catalog());
}
if (
req.method === "POST"
&& url.pathname === "/api/hololake/ai/execute"
) {
const authenticated = authenticateHoloLake(req);
if (!authenticated.ok) {
return json(
res,
authenticated.status,
failure(authenticated.error),
);
}
if (!hololakeAiGateway) {
return json(res, 503, failure("ai_gateway_unavailable"));
}
const body = await readJson(req);
if (!body) return json(res, 400, failure("invalid_json"));
try {
return json(res, 200, await hololakeAiGateway.execute(body));
} catch (error) {
const code = String(error && error.message || "ai_gateway_failed");
const status = code.startsWith("ai_upstream_") ? 502 : 400;
return json(res, status, failure(code));
}
}
if (req.method === "POST" && url.pathname === "/api/repositories/resolve") {
const body = await readJson(req);
if (!body) return json(res, 400, failure("invalid_json"));
const resolved = resolveRepository(body.remote_url, repoPushRegistry);
return json(res, resolved.ok ? 200 : 404, resolved);
}
if (req.method === "POST" && url.pathname === "/api/guanghu-router/challenge") {
const body = await readJson(req);
if (!body) return json(res, 400, { error: "invalid_json" });
const challenged = router.challenge(
String(body.device_id || ""),
Math.floor(Date.now() / 1000),
);
if (!challenged.ok) return json(res, 403, { error: challenged.reason });
return json(res, 200, {
ok: true,
schema: challenged.schema,
challenge_id: challenged.challengeId,
nonce: challenged.nonce,
expires_at: challenged.expiresAt,
server_time: challenged.serverTime,
});
}
if (req.method === "POST" && url.pathname === "/api/guanghu-router/connect") {
const body = await readJson(req);
if (!body) return json(res, 400, { error: "invalid_json" });
const connected = router.authorizeConnection({
deviceId: String(body.device_id || ""),
challengeId: String(body.challenge_id || ""),
clientTimestamp: Number(body.client_timestamp),
signature: String(body.signature || ""),
}, Math.floor(Date.now() / 1000));
if (!connected.ok) return json(res, 403, { error: connected.reason });
return json(res, 200, {
ok: true,
device_id: connected.deviceId,
device_label: connected.deviceLabel,
owner_id: connected.ownerId,
route_token: connected.routeToken,
expires_at: connected.expiresAt,
next_step: "使用一次性 route_token 打开光湖路由持续连接;只有收到 router.connected 回执后才显示上线。",
});
}
if (req.method === "GET" && url.pathname === "/api/guanghu-router/stream") {
const queued = [];
let streaming = false;
const send = event => {
if (!streaming) {
queued.push(event);
} else if (!res.destroyed && !res.writableEnded) {
res.write(`event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`);
}
};
const opened = router.open(bearer(req), send, Math.floor(Date.now() / 1000));
if (!opened.ok) return json(res, 403, { error: opened.reason });
res.writeHead(200, {
"content-type": "text/event-stream; charset=utf-8",
"cache-control": "no-store, no-transform",
"connection": "keep-alive",
"x-accel-buffering": "no",
"x-content-type-options": "nosniff",
});
streaming = true;
for (const event of queued) send(event);
for (const order of manager.pendingForApprover(opened.ownerId)) {
router.deliver(opened.ownerId, order);
}
// This is transport framing only: it carries no application event,
// mutates no online state, and writes no heartbeat record. Its sole
// purpose is to stop the public nginx front door from treating an
// otherwise healthy, idle SSE route as a dead upstream after 60s.
const transportKeepalive = setInterval(() => {
if (!res.destroyed && !res.writableEnded) {
res.write(": guanghu-router-transport\n\n");
}
}, 15_000);
transportKeepalive.unref?.();
res.on("close", () => {
clearInterval(transportKeepalive);
opened.close(Date.now() / 1000, "transport_closed");
});
return;
}
const routerApprovalMatch = url.pathname.match(/^\/api\/guanghu-router\/authorizations\/([0-9a-f-]{36})\/approve$/i);
if (req.method === "POST" && routerApprovalMatch) {
const body = await readJson(req);
if (!body) return json(res, 400, { error: "invalid_json" });
const inspected = manager.inspectPending(routerApprovalMatch[1]);
if (!inspected.ok) return json(res, 410, { error: inspected.reason });
const verified = router.verifyApproval(
String(body.device_id || ""),
inspected.order,
String(body.signature || ""),
);
if (!verified.ok) return json(res, 403, { error: verified.reason });
const approved = manager.approveById(
routerApprovalMatch[1],
verified.authorizerId,
);
if (!approved.ok) return json(res, 409, { error: approved.reason });
return json(res, 200, {
ok: true,
receipt: receipt({
state: "approved",
diagnostic_code: "broadcast_console_approved",
workorder_id: approved.order.id,
target: approved.order.target,
action: approved.order.action,
evidence: {
device_id: verified.deviceId,
authorization_digest: verified.digest,
},
next_step: "申请方现在可以使用原 claim_token 领取受限三小时会话。",
}),
});
}
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));
const form = await readForm(req);
// The response deliberately stays generic whether the submitted
// mailbox is registered, invalid, or already used. The browser posts
// directly to this service over HTTPS; the requesting AI never sees
// or stores the mailbox value.
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 approver = selectApprover(approvers, inspected.order);
const submittedEmail = normalizeEmail(form && form.email);
const registeredEmail = normalizeEmail(approver && approver.email);
if (validEmail(submittedEmail) && registeredEmail && safeEqual(sha256(submittedEmail), sha256(registeredEmail))) {
const sent = await sendApprovalEmail(requestMatch[1]);
if (!sent.ok && sent.reason !== "approval_email_already_sent") {
process.stderr.write(`lake-lamp owner handoff failed: ${String(sent.reason || "unknown").slice(0, 80)}\n`);
}
}
return html(res, 200, emailSentPage(inspected.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);
const delivery = bindAndDeliver(created);
const throughRouter = delivery.delivered > 0;
return json(res, 201, {
ok: true,
workorder_id: created.id,
claim_token: created.claimToken,
request_url: `${publicBaseUrl}/request/${created.handoffToken}`,
expires_in: created.expiresIn,
status: throughRouter ? "waiting_for_broadcast_console" : "waiting_for_owner_handoff",
delivery: {
channel: throughRouter ? "guanghu_router" : "email_recovery_fallback",
delivered_devices: delivery.delivered,
},
email_status: throughRouter ? "fallback_not_needed" : "owner_input_required",
public_auto_email: false,
receipt: receipt({
state: throughRouter ? "waiting_for_broadcast_console" : "waiting_for_owner_handoff",
diagnostic_code: throughRouter ? "broadcast_console_delivery_confirmed" : "owner_handoff_required",
workorder_id: created.id,
evidence: throughRouter ? { delivered_devices: delivery.delivered } : {},
next_step: throughRouter
? "等待冰朔在 HoloLake 广播主控台核对并点击授权;不要发送邮件。"
: "HoloLake 当前没有在线路由连接。把 request_url 交给主人,通过服务器托管页面恢复设备绑定或完成邮件灾备授权。",
}),
});
}
if (req.method === "POST" && url.pathname === "/api/broadcast/workorders") {
if (!bearerMatches(req, broadcastToken)) {
return json(res, 401, { error: "broadcast_tower_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 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",
delivery: {
channel: "fifth_domain_broadcast_email",
delivered_devices: 0,
},
email_status: "sent",
receipt: receipt({
state: "waiting_for_owner",
diagnostic_code: "owner_email_sent_by_registered_broadcast_tower",
workorder_id: created.id,
target: validation.request.target,
action: validation.request.action,
next_step: "等待目标节点主人点击邮件批准链接;广播塔随后使用原 claim_token 领取会话。",
}),
});
}
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 delivery = bindAndDeliver(created);
if (delivery.delivered > 0) {
return json(res, 201, {
ok: true,
workorder_id: created.id,
claim_token: created.claimToken,
expires_in: created.expiresIn,
status: "waiting_for_broadcast_console",
delivery: { channel: "guanghu_router", delivered_devices: delivery.delivered },
});
}
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",
delivery: { channel: "email_recovery_fallback", delivered_devices: 0 },
});
}
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 });
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。" });
}
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));
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 });
}
if (req.method === "POST" && url.pathname === "/api/actions/execute") {
const body = await readJson(req);
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"));
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);
if (!verified.ok) return json(res, 403, failure(verified.reason));
const map = mapGate.read(target);
const mapVerified = mapGate.verify(token, target, map.hash);
if (!mapVerified.ok) return json(res, 423, failure(mapVerified.reason, "先读取并确认导航图。", { required_action: "read-navigation-map" }));
const result = await executeAction(resource ? { action, target, resource } : { action, target });
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 });
}
if (req.method === "POST" && url.pathname === "/api/repo-push/grant") {
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 || "repo-push");
const repo = String(body.repo || "").toLowerCase();
if (!/^bingshuo\/[a-z0-9._-]+$/.test(repo)) return json(res, 400, failure("repo_not_allowlisted"));
const branch = String(body.branch || "main");
const resource = `${repo}@${branch}`;
const verified = manager.verifySession(token, { pid: String(body.persona_id || "") }, target, scope, "push-repository");
if (!verified.ok) return json(res, 403, failure(verified.reason));
if (verified.session.resource && verified.session.resource !== resource) {
return json(res, 403, failure("resource_mismatch"));
}
const map = mapGate.read(target);
const mapVerified = mapGate.verify(token, target, map.hash);
if (!mapVerified.ok) return json(res, 423, failure(mapVerified.reason, "先读取并确认导航图。", { required_action: "read-navigation-map" }));
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);
const entry = repoPushRegistry[repo];
const configured = Boolean(entry);
const operationReceipt = receipt({
state: configured ? "ready" : "blocked",
diagnostic_code: configured ? "repo_push_transport_ready" : "repo_push_transport_unavailable",
workorder_id: verified.session.workorderId,
target,
action: "push-repository",
next_step: configured
? "使用同一会话向受限 bundle 接收器上传一次 Git bundle服务器将核验仓库、分支、精确远端基线和快进关系。"
: "服务器已登记本次推送许可,但安全推送接收器尚未部署;不要重试裸 git push、不要索要账号密码。等待受限 bundle 接收器上线后按同一工单回执执行。",
});
manager.recordReceipt(token, operationReceipt);
return json(res, 200, {
ok: true,
repo,
branch,
target,
expires_at: grant.expires_at,
transport: {
status: configured ? "ready" : "not_configured",
diagnostic_code: operationReceipt.diagnostic_code,
upload_url: configured ? `${publicBaseUrl}/api/repo-push/bundle` : "",
max_bundle_bytes: configured ? maxRepoBundleBytes : 0,
max_request_bytes: configured ? maxRepoChunkBytes : 0,
next_step: operationReceipt.next_step,
},
receipt: operationReceipt,
});
}
if (req.method === "PUT" && url.pathname === "/api/repo-push/bundle") {
const token = bearer(req);
const repo = String(url.searchParams.get("repo") || "").toLowerCase();
const branch = String(url.searchParams.get("branch") || "");
const expectedHead = String(url.searchParams.get("expected_head") || "").toLowerCase();
const personaId = String(url.searchParams.get("persona_id") || "");
const target = String(url.searchParams.get("target") || "");
const scope = String(url.searchParams.get("scope") || "repo-push");
const resource = `${repo}@${branch}`;
if (!repoPushRegistry[repo]) return json(res, 404, failure("repository_not_registered"));
const verified = manager.verifySession(
token,
{ pid: personaId },
target,
scope,
"push-repository",
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" }));
}
const declaredLength = Number(req.headers["content-length"]);
const uploadId = String(req.headers["x-guanghu-upload-id"] || "");
const chunkIndex = Number(req.headers["x-guanghu-chunk-index"]);
const chunkCount = Number(req.headers["x-guanghu-chunk-count"]);
const chunkOffset = Number(req.headers["x-guanghu-chunk-offset"]);
const chunked = uploadId !== "";
if (!Number.isSafeInteger(declaredLength)
|| declaredLength < 1
|| declaredLength > (chunked ? maxRepoChunkBytes : maxRepoBundleBytes)) {
return json(res, 413, failure("repo_bundle_size_invalid"));
}
fs.mkdirSync(repoUploadDir, { recursive: true, mode: 0o2770 });
if (chunked && (
!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uploadId)
|| !Number.isSafeInteger(chunkIndex)
|| !Number.isSafeInteger(chunkCount)
|| !Number.isSafeInteger(chunkOffset)
|| chunkIndex < 0
|| chunkCount < 1
|| chunkIndex >= chunkCount
|| chunkCount > Math.ceil(maxRepoBundleBytes / (8 * 1024))
|| chunkOffset < 0
|| chunkOffset + declaredLength > maxRepoBundleBytes
)) {
return json(res, 400, failure("repo_bundle_chunk_invalid"));
}
const bundlePath = path.join(
repoUploadDir,
chunked ? `${uploadId}.bundle.part` : `${crypto.randomUUID()}.bundle`,
);
if (chunked) {
const currentSize = fs.existsSync(bundlePath) ? fs.statSync(bundlePath).size : 0;
if ((chunkIndex === 0 && currentSize !== 0) || (chunkIndex > 0 && currentSize !== chunkOffset)) {
return json(res, 409, failure("repo_bundle_chunk_out_of_order", "", {
expected_offset: currentSize,
}));
}
}
try {
await readBinaryBody(req, bundlePath, chunked ? maxRepoChunkBytes : maxRepoBundleBytes, {
append: chunked && chunkIndex > 0,
});
if (chunked && chunkIndex + 1 < chunkCount) {
return json(res, 202, {
ok: true,
upload: {
state: "partial",
upload_id: uploadId,
next_chunk_index: chunkIndex + 1,
received_bytes: chunkOffset + declaredLength,
},
});
}
const result = await receiveRepoBundle({
repo,
branch,
expected_head: expectedHead,
bundle_path: bundlePath,
});
const operationReceipt = receipt({
state: result.ok ? "succeeded" : "blocked",
diagnostic_code: result.diagnostic_code || (result.ok ? "repo_push_succeeded" : "repo_push_receiver_failed"),
workorder_id: verified.session.workorderId,
target,
action: "push-repository",
evidence: {
repo,
branch,
expected_head: expectedHead,
commit_sha: result.commit_sha || "",
verification_url: result.verification_url || "",
receiver_evidence: String(result.evidence || "").slice(0, 600),
},
next_step: result.ok
? "从光湖代码频道回读分支与提交 SHA本次上传不自动部署。"
: "读取 diagnostic_code 和精确基线回执;不要改用裸 git push 或猜测账号密码。",
});
manager.recordReceipt(token, operationReceipt);
return json(res, result.ok ? 200 : 409, {
ok: result.ok,
repository: result,
receipt: operationReceipt,
});
} finally {
if (!chunked || chunkIndex + 1 === chunkCount) {
fs.rmSync(bundlePath, { force: true });
}
}
}
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" }));
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,
});
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 });
}
if (req.method === "POST" && url.pathname === "/api/ghdr/controllers/poll") {
const body = await readJson(req);
if (!body) return json(res, 400, failure("invalid_json"));
const result = getGhdrControllerBroker().poll(body);
return json(res, result.ok ? 200 : 403, result);
}
if (req.method === "POST" && url.pathname === "/api/ghdr/controllers/result") {
const body = await readJson(req);
if (!body) return json(res, 400, failure("invalid_json"));
try {
const result = getGhdrControllerBroker().submit(body);
return json(res, result.ok ? 200 : 403, result);
} catch {
return json(res, 400, failure("ghdr_result_invalid"));
}
}
if (req.method === "POST" && url.pathname === "/api/ghdr/sign-layout") {
const body = await readJson(req);
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"));
}
const token = bearer(req);
const target = String(body.target || "");
const scope = String(body.scope || "native-recovery");
const resource = String(body.resource || "");
const binding = validateGhdrLayoutRequest(body.plan, target, resource);
if (!binding.ok) return json(res, 400, failure(binding.error));
const verified = manager.verifySession(
token,
{ pid: String(body.persona_id || "") },
target,
scope,
"sign-native-layout-plan",
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",
}));
}
const result = await signGhdrPlan({
plan: body.plan,
binding,
workorderId: verified.session.workorderId,
authorizer: getGhdrAuthorizer(),
});
const signatures = Array.isArray(result.signatures) ? result.signatures : [];
const independent = signatures.length === 2
&& new Set(signatures.map(item => item && item.node_id)).size === 2
&& new Set(signatures.map(item => item && item.failure_domain)).size === 2;
const succeeded = Boolean(result.ok && independent);
const operationReceipt = receipt({
state: succeeded ? "succeeded" : "failed",
diagnostic_code: succeeded
? "ghdr_layout_double_signature_succeeded"
: String(result.error || "ghdr_layout_double_signature_failed"),
workorder_id: verified.session.workorderId,
target,
action: "sign-native-layout-plan",
evidence: {
layout_payload_sha256: binding.payload_sha256,
generation: binding.generation,
controller_count: signatures.length,
controller_ids: signatures.map(item => String(item && item.node_id || "")),
},
next_step: succeeded
? "把两份签名合并回同一份布局计划,并由企业目标机独立验证公钥、故障域、实时读回与有效期。"
: "读取 diagnostic_code不得降级为单签、复制私钥或绕过邮件授权。",
});
manager.recordReceipt(token, operationReceipt);
return json(res, succeeded ? 200 : 502, {
ok: succeeded,
layout_payload_sha256: binding.payload_sha256,
signatures: succeeded ? signatures : [],
receipt: operationReceipt,
});
}
if (req.method === "GET" && url.pathname === "/api/ghdr/authorizer-public-key") {
return json(res, 200, {
ok: true,
binding: getGhdrAuthorizer().publicBinding(),
});
}
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" });
}
});
}
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;
}
function approvalPage(order, token) {
return document("小湖灯授权请求", `
<p class="eyebrow">LAKE LAMP SECURITY PROTOCOL</p>
<h1>人格体请求进入一台服务器</h1>
<div class="panel">
<dl><dt>人格体</dt><dd>${escapeHtml(order.persona.name)} <small>${escapeHtml(order.persona.pid)}</small></dd>
<dt>申请入口</dt><dd>${escapeHtml(order.provenance && order.provenance.system_entry || "旧版工单")}</dd>
<dt>实例来源</dt><dd>${escapeHtml(originLabel(order))}</dd>
<dt>目标节点</dt><dd>${escapeHtml(order.target)}</dd><dt>授权范围</dt><dd>${escapeHtml(order.scope)}</dd>
<dt>进入动作</dt><dd>${escapeHtml(order.action)}</dd><dt>会话能力</dt><dd>${escapeHtml((order.allowed_actions || [order.action]).join(" · "))}</dd>
<dt>绑定资源</dt><dd>${escapeHtml(order.resource || "无")}</dd>
<dt>说明</dt><dd>${escapeHtml(order.description || "未附加说明")}</dd></dl>
</div>
<p class="notice">一次确认将打开这台服务器上的三小时受限运维会话。人格体持续执行已绑定任务时会自动续期;切换服务器、扩大范围、切换绑定资源或停止活动后过期才需重新申请。</p>
<form method="post"><button type="submit">打开三小时受限运维会话</button></form>
`);
}
function requestPage(order) {
return document("小湖灯跨设备授权", `
<p class="eyebrow">CROSS-DEVICE HANDOFF</p>
<h1>核对这张无权限申请单</h1>
<div class="panel">
<dl><dt>人格体</dt><dd>${escapeHtml(order.persona.name)} <small>${escapeHtml(order.persona.pid)}</small></dd>
<dt>申请入口</dt><dd>${escapeHtml(order.provenance && order.provenance.system_entry || "旧版工单")}</dd>
<dt>实例来源</dt><dd>${escapeHtml(originLabel(order))}</dd>
<dt>目标节点</dt><dd>${escapeHtml(order.target)}</dd><dt>授权范围</dt><dd>${escapeHtml(order.scope)}</dd>
<dt>登记动作</dt><dd>${escapeHtml(order.action)}</dd><dt>绑定资源</dt><dd>${escapeHtml(order.resource || "无")}</dd><dt>说明</dt><dd>${escapeHtml(order.description || "未附加说明")}</dd></dl>
</div>
<p class="notice">这张页面本身没有执行权。邮箱只通过本页的加密连接直达京东节点不会返回给发起申请的AI。无论是否匹配页面都会显示相同结果。</p>
<form method="post">
<label for="owner-email">冰朔登记邮箱</label>
<input id="owner-email" name="email" type="email" inputmode="email" autocomplete="email" maxlength="254" required placeholder="请输入你的邮箱">
<button type="submit">向我的邮箱发送一次授权申请</button>
</form>
`);
}
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:100vh;display:grid;place-items:center;background:radial-gradient(circle at 20% 10%,#17344d,#09111b 55%,#05090e);color:#eaf4fb;font:16px/1.7 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;padding:24px}.shell{width:min(680px,100%);padding:42px;border:1px solid #29475d;border-radius:24px;background:rgba(10,22,33,.94);box-shadow:0 24px 80px #0008}.eyebrow{color:#6ed5ff;letter-spacing:.18em;font-size:12px}h1{font-size:clamp(30px,6vw,48px);line-height:1.15;margin:10px 0 28px}.panel{background:#102638;border:1px solid #24465d;border-radius:16px;padding:20px 24px}dl{display:grid;grid-template-columns:110px 1fr;gap:12px;margin:0}dt{color:#8ba4b6}dd{margin:0;font-weight:650}small{display:block;color:#7893a6;font-weight:400}.notice{color:#9eb2c0;margin:20px 0}label{display:block;margin:0 0 8px;color:#c9dae5;font-weight:700}input{width:100%;border:1px solid #365d76;border-radius:14px;padding:15px 16px;margin:0 0 14px;background:#07131d;color:#eaf4fb;font:inherit;outline:none}input:focus{border-color:#67d4ff;box-shadow:0 0 0 3px #67d4ff22}button{width:100%;border:0;border-radius:14px;padding:16px;background:#67d4ff;color:#042235;font-weight:800;font-size:17px;cursor:pointer}@media(max-width:520px){.shell{padding:28px 22px}dl{grid-template-columns:1fr;gap:2px}dd{margin-bottom:12px}}
</style></head><body><main class="shell">${body}</main></body></html>`;
}
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 readBinaryBody(req, destination, maxBytes, options = {}) {
return new Promise((resolve, reject) => {
let received = 0;
const output = fs.createWriteStream(destination, {
flags: options.append ? "a" : "wx",
mode: 0o640,
});
const fail = error => {
output.destroy();
fs.rmSync(destination, { force: true });
reject(error);
};
req.on("data", chunk => {
received += chunk.length;
if (received > maxBytes) {
const error = new Error("repo bundle too large");
error.code = "BODY_TOO_LARGE";
req.destroy(error);
return;
}
if (!output.write(chunk)) req.pause();
});
output.on("drain", () => req.resume());
req.on("end", () => output.end());
req.on("error", fail);
output.on("error", fail);
output.on("finish", () => {
if (received < 1) return fail(new Error("repo bundle empty"));
resolve(received);
});
});
}
function readForm(req) {
return new Promise((resolve, reject) => {
let raw = "";
req.on("data", chunk => {
raw += chunk;
if (raw.length > 4 * 1024) {
const error = new Error("body too large");
error.code = "BODY_TOO_LARGE";
reject(error);
req.destroy();
}
});
req.on("end", () => {
try { resolve(Object.fromEntries(new URLSearchParams(raw))); }
catch { resolve({}); }
});
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) {
if (!file) return ownerEmail ? [{ id: "ICE-GL∞", email: ownerEmail, default: true, persona_ids: [], targets: ["*"], scopes: ["*"] }] : [];
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.targets.includes("*") && !item.scopes.includes("*"))
|| 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 normalizeEmail(value) { return String(value || "").trim().normalize("NFKC").toLowerCase(); }
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);
}
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: "会话已过期;以同一目标和范围重新申请工单。",
};
}
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" };
const immutableResourceAction = action === "provision-approved-architecture" || action === "dispatch-approved-deployment";
const repoPushResourceAction = action === "push-repository";
const linkedNodeResourceAction = action === "authorize-linked-node-session";
const ghdrLayoutResourceAction = action === "sign-native-layout-plan";
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 (repoPushResourceAction && resource && !/^bingshuo\/[a-z0-9._-]+@[a-z0-9][a-z0-9._/-]{0,199}$/.test(resource)) return { ok: false, status: 400, error: "repo_push_resource_invalid" };
if (linkedNodeResourceAction && !/^[A-Z0-9][A-Z0-9._-]{5,119}:[A-Za-z0-9._-]{3,120}$/.test(resource)) return { ok: false, status: 400, error: "linked_node_resource_required" };
if (ghdrLayoutResourceAction && !/^GH-CVM-MAIN-PROD-01:[0-9a-f]{64}:[1-9][0-9]{0,19}$/.test(resource)) return { ok: false, status: 400, error: "ghdr_layout_resource_required" };
if (!immutableResourceAction && !repoPushResourceAction && !linkedNodeResourceAction && !ghdrLayoutResourceAction && 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" };
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 validateGhdrLayoutRequest(plan, target, resource) {
if (target !== "GH-CVM-MAIN-PROD-01"
|| !plan
|| plan.schema !== "guanghu.ghdr-signed-layout-plan/v1"
|| !plan.payload
|| !Array.isArray(plan.signatures)
|| plan.signatures.length !== 0) {
return { ok: false, error: "ghdr_layout_plan_invalid" };
}
const payload = plan.payload;
const generation = Number(payload.generation);
if (payload.node_id !== target
|| payload.provider !== "tencent_cloud"
|| payload.region !== "ap-guangzhou"
|| payload.system_disk !== "/dev/vda"
|| payload.operation !== "install_native_ab"
|| !Number.isSafeInteger(generation)
|| generation < 1) {
return { ok: false, error: "ghdr_layout_binding_mismatch" };
}
const canonical = JSON.stringify(payload);
const payloadSha256 = crypto.createHash("sha256").update(canonical).digest("hex");
if (resource !== `${target}:${payloadSha256}:${generation}`) {
return { ok: false, error: "ghdr_layout_resource_mismatch" };
}
return { ok: true, payload_sha256: payloadSha256, generation, 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 => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[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,
validateGhdrLayoutRequest,
};