fix(jd): publish Guanghu router runtime source

This commit is contained in:
冰朔 2026-07-29 23:37:26 +08:00
commit e2bb573a58
14 changed files with 1492 additions and 73 deletions

View file

@ -17,7 +17,6 @@ async function authorizeRepoPush(options, deps = {}) {
origin_software: options.software || "仓库推送客户端",
origin_model: options.model || "未声明模型",
origin_instance: options.instance || "当前实例",
owner_notify: true,
persona_id: persona,
persona_name: options.name || persona,
target,
@ -26,10 +25,10 @@ async function authorizeRepoPush(options, deps = {}) {
description: options.description || `申请推送 ${repo}`,
});
output("[LL-WORKORDER-CREATED] 无执行权申请单已创建;服务器已向预登记邮箱发送批准链接,也没有推送权限。");
output("[LL-WORKORDER-CREATED] 无执行权申请单已创建;尚未发送邮件,也没有推送权限。");
output(`REQUEST_URL=${request.request_url}`);
output("请把 REQUEST_URL 交给冰朔用于核对申请内容并保持本命令运行;批准链接已由服务器发送到预登记邮箱。");
output("不需要向冰朔索要邮箱、授权码、验证码、密码或任何令牌。");
output("请把 REQUEST_URL 交给冰朔并保持本命令运行;冰朔会在京东节点托管页面亲自输入登记邮箱。");
output("人格体不得索要、接收、记录或转存邮箱、授权码、验证码、密码和任何令牌。");
const deadline = Date.now() + Number(request.expires_in || 900) * 1000;
let session;

View file

@ -50,7 +50,11 @@ test("repo-push helper stops with a server receipt when no safe transport is dep
while (!lines.some(line => line.startsWith("REQUEST_URL="))) await new Promise(resolve => setTimeout(resolve, 1));
const requestUrl = lines.find(line => line.startsWith("REQUEST_URL=")).slice("REQUEST_URL=".length);
const requestPath = new URL(requestUrl).pathname.replace("/authz", "");
assert.equal((await fetch(`${base}${requestPath}`, { method: "POST" })).status, 200);
assert.equal((await fetch(`${base}${requestPath}`, {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({ email: "owner@example.invalid" }).toString(),
})).status, 200);
assert.equal(mail.length, 1);
const approvalPath = new URL(mail[0].approvalUrl).pathname.replace("/authz", "");
assert.equal((await fetch(`${base}${approvalPath}`, { method: "POST" })).status, 200);

View file

@ -0,0 +1,277 @@
"use strict";
const crypto = require("node:crypto");
const fs = require("node:fs");
const CONNECT_SCHEMA = "guanghu.router-connect/v1";
const APPROVAL_SCHEMA = "guanghu.router-approval/v1";
const MAX_CLOCK_SKEW_SECONDS = 120;
class GuanghuRouter {
constructor({ devices = [], challengeTtl = 60, routeTokenTtl = 30 } = {}) {
this.devices = new Map(
devices
.filter(validDevice)
.map(device => [device.device_id, Object.freeze({ ...device })]),
);
this.challengeTtl = Math.max(15, Number(challengeTtl) || 60);
this.routeTokenTtl = Math.max(10, Number(routeTokenTtl) || 30);
this.challenges = new Map();
this.routeTokens = new Map();
this.connections = new Map();
}
challenge(deviceId, now = Date.now() / 1000) {
const device = this.devices.get(String(deviceId || ""));
if (!device || !device.enabled) return { ok: false, reason: "device_not_registered" };
const challengeId = crypto.randomUUID();
const nonce = randomToken();
this.challenges.set(challengeId, {
deviceId: device.device_id,
nonce,
expiresAt: now + this.challengeTtl,
});
return {
ok: true,
schema: CONNECT_SCHEMA,
challengeId,
nonce,
expiresAt: now + this.challengeTtl,
serverTime: now,
};
}
authorizeConnection(input, now = Date.now() / 1000) {
const deviceId = String(input && input.deviceId || "");
const challengeId = String(input && input.challengeId || "");
const challenge = this.challenges.get(challengeId);
const device = this.devices.get(deviceId);
if (!device || !device.enabled) return { ok: false, reason: "device_not_registered" };
if (!challenge || challenge.deviceId !== deviceId) return { ok: false, reason: "challenge_not_found" };
if (now > challenge.expiresAt) {
this.challenges.delete(challengeId);
return { ok: false, reason: "challenge_expired" };
}
const clientTimestamp = Number(input && input.clientTimestamp);
if (!Number.isFinite(clientTimestamp) || Math.abs(now - clientTimestamp) > MAX_CLOCK_SKEW_SECONDS) {
return { ok: false, reason: "device_clock_out_of_range" };
}
const message = canonicalConnect({
deviceId,
challengeId,
nonce: challenge.nonce,
clientTimestamp,
});
if (!verifyDeviceSignature(device, message, input && input.signature)) {
return { ok: false, reason: "device_signature_invalid" };
}
this.challenges.delete(challengeId);
const routeToken = randomToken();
this.routeTokens.set(tokenHash(routeToken), {
deviceId,
expiresAt: now + this.routeTokenTtl,
});
return {
ok: true,
deviceId,
deviceLabel: device.label,
ownerId: device.owner_id,
routeToken,
expiresAt: now + this.routeTokenTtl,
};
}
open(routeToken, send, now = Date.now() / 1000) {
const key = tokenHash(routeToken || "");
const pending = this.routeTokens.get(key);
if (!pending) return { ok: false, reason: "route_token_not_found" };
this.routeTokens.delete(key);
if (now > pending.expiresAt) return { ok: false, reason: "route_token_expired" };
const device = this.devices.get(pending.deviceId);
if (!device || !device.enabled) return { ok: false, reason: "device_not_registered" };
const connectionId = crypto.randomUUID();
const existing = this.connections.get(device.device_id);
if (existing) existing.close(now, "replaced");
const connection = {
connectionId,
device,
send,
openedAt: now,
closed: false,
close: (closedAt = Date.now() / 1000, reason = "client_closed") => {
if (connection.closed) return;
connection.closed = true;
if (this.connections.get(device.device_id) === connection) {
this.connections.delete(device.device_id);
}
send({
type: "router.closed",
reason,
receipt: routeReceipt("offline", device, connectionId, closedAt),
});
},
};
this.connections.set(device.device_id, connection);
const receipt = routeReceipt("online", device, connectionId, now);
send({ type: "router.connected", connection_id: connectionId, receipt });
return {
ok: true,
state: "online",
connectionId,
deviceId: device.device_id,
ownerId: device.owner_id,
receipt,
close: connection.close,
};
}
isApproverOnline(approverId) {
return [...this.connections.values()].some(connection => (
!connection.closed && connection.device.owner_id === String(approverId || "")
));
}
deliver(approverId, order) {
const event = {
type: "authorization.requested",
digest: workorderDigest(order),
workorder: order,
};
let delivered = 0;
for (const connection of this.connections.values()) {
if (!connection.closed && connection.device.owner_id === String(approverId || "")) {
connection.send(event);
delivered += 1;
}
}
return delivered;
}
verifyApproval(deviceId, order, signature) {
const connection = this.connections.get(String(deviceId || ""));
if (!connection || connection.closed) return { ok: false, reason: "device_route_offline" };
const digest = workorderDigest(order);
const message = canonicalApproval({
deviceId: connection.device.device_id,
workorderId: order.id,
digest,
});
if (!verifyDeviceSignature(connection.device, message, signature)) {
return { ok: false, reason: "device_signature_invalid" };
}
return {
ok: true,
authorizerId: connection.device.owner_id,
deviceId: connection.device.device_id,
digest,
};
}
}
function loadDevices(file) {
if (!file || !fs.existsSync(file)) return [];
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
if (!Array.isArray(parsed)) throw new Error("HoloLake device registry must be a JSON array");
return parsed.filter(validDevice);
}
function validDevice(device) {
return Boolean(
device
&& typeof device.device_id === "string"
&& /^[A-Za-z0-9._-]{3,128}$/.test(device.device_id)
&& typeof device.owner_id === "string"
&& device.owner_id.length > 0
&& typeof device.label === "string"
&& device.label.length > 0
&& typeof device.public_key === "string"
&& /^[A-Za-z0-9_-]{40,64}$/.test(device.public_key)
&& typeof device.enabled === "boolean",
);
}
function verifyDeviceSignature(device, message, signature) {
try {
const publicKey = crypto.createPublicKey({
key: { kty: "OKP", crv: "Ed25519", x: device.public_key },
format: "jwk",
});
return crypto.verify(
null,
Buffer.from(message),
publicKey,
Buffer.from(String(signature || ""), "base64url"),
);
} catch {
return false;
}
}
function canonicalConnect({ deviceId, challengeId, nonce, clientTimestamp }) {
return [
CONNECT_SCHEMA,
String(deviceId),
String(challengeId),
String(nonce),
String(clientTimestamp),
].join("\n");
}
function canonicalApproval({ deviceId, workorderId, digest }) {
return [
APPROVAL_SCHEMA,
String(deviceId),
String(workorderId),
String(digest),
].join("\n");
}
function workorderDigest(order) {
const value = {
id: String(order.id || ""),
persona_id: String(order.persona && order.persona.pid || ""),
persona_name: String(order.persona && order.persona.name || ""),
target: String(order.target || ""),
scope: String(order.scope || ""),
action: String(order.action || ""),
allowed_actions: [...(order.allowed_actions || order.allowedActions || [order.action])].map(String),
description: String(order.description || ""),
resource: String(order.resource || ""),
created_at: Number(order.createdAt || 0),
expires_at: Number(order.expiresAt || 0),
};
return crypto.createHash("sha256").update(JSON.stringify(value)).digest("hex");
}
function routeReceipt(state, device, connectionId, now) {
return {
schema: "guanghu.route-receipt/v1",
receipt_id: crypto.randomUUID(),
state,
device_id: device.device_id,
owner_id: device.owner_id,
node_id: "JD-FD-PRIMARY",
connection_id: connectionId,
occurred_at: now,
};
}
function randomToken() {
return crypto.randomBytes(32).toString("base64url");
}
function tokenHash(value) {
return crypto.createHash("sha256").update(String(value)).digest("hex");
}
module.exports = {
APPROVAL_SCHEMA,
CONNECT_SCHEMA,
GuanghuRouter,
canonicalApproval,
canonicalConnect,
loadDevices,
workorderDigest,
};

View file

@ -0,0 +1,162 @@
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const crypto = require("node:crypto");
const {
GuanghuRouter,
canonicalApproval,
canonicalConnect,
workorderDigest,
} = require("./guanghu-router");
function registeredDevice() {
const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519");
const publicJwk = publicKey.export({ format: "jwk" });
return {
device: {
device_id: "HL-BS-MAC-001",
owner_id: "owner",
label: "冰朔的 HoloLake",
public_key: publicJwk.x,
enabled: true,
},
privateKey,
};
}
function signedConnect(router, device, privateKey, now) {
const challenge = router.challenge(device.device_id, now);
assert.equal(challenge.ok, true);
const signature = crypto.sign(
null,
Buffer.from(canonicalConnect({
deviceId: device.device_id,
challengeId: challenge.challengeId,
nonce: challenge.nonce,
clientTimestamp: now,
})),
privateKey,
).toString("base64url");
return router.authorizeConnection({
deviceId: device.device_id,
challengeId: challenge.challengeId,
clientTimestamp: now,
signature,
}, now);
}
test("only a registered device with a valid signature receives a one-time route token", () => {
const { device, privateKey } = registeredDevice();
const router = new GuanghuRouter({ devices: [device] });
assert.deepEqual(router.challenge("unknown-device", 1_000), {
ok: false,
reason: "device_not_registered",
});
const challenge = router.challenge(device.device_id, 1_000);
const rejected = router.authorizeConnection({
deviceId: device.device_id,
challengeId: challenge.challengeId,
clientTimestamp: 1_000,
signature: "invalid",
}, 1_000);
assert.equal(rejected.ok, false);
assert.equal(rejected.reason, "device_signature_invalid");
const authorized = signedConnect(router, device, privateKey, 1_001);
assert.equal(authorized.ok, true);
assert.match(authorized.routeToken, /^[A-Za-z0-9_-]{40,}$/);
assert.equal(router.isApproverOnline("owner"), false);
});
test("the open transport is the online fact and closing it makes the device offline", () => {
const { device, privateKey } = registeredDevice();
const router = new GuanghuRouter({ devices: [device] });
const authorized = signedConnect(router, device, privateKey, 2_000);
const events = [];
const opened = router.open(authorized.routeToken, event => events.push(event), 2_001);
assert.equal(opened.ok, true);
assert.equal(opened.state, "online");
assert.equal(router.isApproverOnline("owner"), true);
assert.equal(events[0].type, "router.connected");
assert.equal(events[0].receipt.state, "online");
assert.equal(router.open(authorized.routeToken, () => {}, 2_002).reason, "route_token_not_found");
opened.close(2_003);
assert.equal(router.isApproverOnline("owner"), false);
assert.equal(events.at(-1).type, "router.closed");
assert.equal(events.at(-1).receipt.state, "offline");
});
test("authorization cards travel through the already-open route", () => {
const { device, privateKey } = registeredDevice();
const router = new GuanghuRouter({ devices: [device] });
const authorized = signedConnect(router, device, privateKey, 3_000);
const events = [];
router.open(authorized.routeToken, event => events.push(event), 3_001);
const order = {
id: "203e12af-f821-4b62-b80f-b3d73df05161",
persona: { pid: "ICE-GL-ZY001", name: "铸渊" },
target: "JD-FD-PRIMARY",
scope: "server-login",
action: "read-navigation-map",
allowed_actions: ["read-navigation-map", "inspect-services"],
description: "进入第五域",
resource: "",
createdAt: 3_000,
expiresAt: 4_000,
state: "pending",
};
assert.equal(router.deliver("owner", order), 1);
const card = events.at(-1);
assert.equal(card.type, "authorization.requested");
assert.equal(card.workorder.id, order.id);
assert.equal(card.digest, workorderDigest(order));
});
test("the bound device signs the exact authorization card digest", () => {
const { device, privateKey } = registeredDevice();
const router = new GuanghuRouter({ devices: [device] });
const authorized = signedConnect(router, device, privateKey, 4_000);
router.open(authorized.routeToken, () => {}, 4_001);
const order = {
id: "203e12af-f821-4b62-b80f-b3d73df05161",
persona: { pid: "ICE-GL-ZY001", name: "铸渊" },
target: "JD-FD-PRIMARY",
scope: "server-login",
action: "read-navigation-map",
allowed_actions: ["read-navigation-map", "inspect-services"],
description: "进入第五域",
resource: "",
createdAt: 4_000,
expiresAt: 5_000,
state: "pending",
};
const digest = workorderDigest(order);
const signature = crypto.sign(
null,
Buffer.from(canonicalApproval({
deviceId: device.device_id,
workorderId: order.id,
digest,
})),
privateKey,
).toString("base64url");
const verified = router.verifyApproval(
device.device_id,
order,
signature,
);
assert.equal(verified.ok, true);
assert.equal(verified.authorizerId, "owner");
assert.equal(verified.deviceId, device.device_id);
const changed = { ...order, action: "inspect-services" };
assert.equal(
router.verifyApproval(device.device_id, changed, signature).reason,
"device_signature_invalid",
);
});

View file

@ -16,7 +16,7 @@ NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/guanghu/lake-lamp-authz /var/lib/guanghu/repo-authorizations
ReadWritePaths=/var/lib/guanghu/lake-lamp-authz /var/lib/guanghu/repo-authorizations /var/lib/guanghu/repo-push-uploads /var/lib/guanghu/forgejo/repositories/bingshuo/hololake-platform.git
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
LockPersonality=true

View file

@ -7,6 +7,8 @@
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const crypto = require("node:crypto");
const { fileURLToPath } = require("node:url");
const { execFile } = require("node:child_process");
function loadRegistry(file) {
@ -23,7 +25,10 @@ async function receiveBundle(request, options = {}) {
const expectedHead = String(request.expected_head || "").toLowerCase();
const entry = registry[repo];
if (!entry) return blocked("repo_not_allowlisted");
if (!/^[a-z0-9][a-z0-9._/-]{0,199}$/.test(branch) || branch !== String(entry.branch || "main")) return blocked("branch_not_allowlisted");
const allowedBranches = Array.isArray(entry.branches)
? entry.branches.map(String)
: [String(entry.branch || "main")];
if (!/^[a-z0-9][a-z0-9._/-]{0,199}$/.test(branch) || !allowedBranches.includes(branch)) return blocked("branch_not_allowlisted");
if (!/^[0-9a-f]{40}$/.test(expectedHead)) return blocked("expected_head_required");
const bundlePath = checkedBundlePath(request.bundle_path, options.uploadDir || process.env.LAKE_LAMP_REPO_UPLOAD_DIR || "/var/lib/guanghu/repo-push-uploads");
if (!bundlePath) return blocked("bundle_path_invalid");
@ -31,16 +36,53 @@ async function receiveBundle(request, options = {}) {
const temporary = fs.mkdtempSync(path.join(options.tempDir || os.tmpdir(), "lake-lamp-receive-"));
const bare = path.join(temporary, "quarantine.git");
try {
await run(["init", "--bare", bare]);
// Clone the registered repository into quarantine instead of initializing an
// empty repository and fetching refs. A registered source may itself be
// shallow; cloning preserves its shallow boundary metadata, while a ref-only
// fetch can leave the prerequisite commit present but its parents unreadable.
await run(["clone", "--bare", "--no-local", entry.remote, bare]);
await run(["-C", bare, "bundle", "verify", bundlePath]);
await run(["-C", bare, "fetch", bundlePath, `refs/heads/${branch}:refs/heads/incoming`]);
const incoming = (await run(["-C", bare, "rev-parse", "refs/heads/incoming"])).stdout.trim().toLowerCase();
if (!/^[0-9a-f]{40}$/.test(incoming)) return blocked("bundle_branch_missing");
const remoteHead = parseRemoteHead((await run(["ls-remote", "--exit-code", "--heads", entry.remote, `refs/heads/${branch}`])).stdout);
if (remoteHead !== expectedHead) return blocked("expected_head_mismatch", { expected_head: expectedHead, actual_head: remoteHead || "" });
const ancestry = await run(["-C", bare, "merge-base", "--is-ancestor", expectedHead, incoming], { allowFailure: true });
if (!ancestry.ok) return blocked("non_fast_forward_bundle", { expected_head: expectedHead, incoming_commit: incoming });
await run(["-C", bare, "push", entry.remote, `refs/heads/incoming:refs/heads/${branch}`]);
const remoteResult = await run(
["ls-remote", "--exit-code", "--heads", entry.remote, `refs/heads/${branch}`],
{ allowFailure: true },
);
const remoteHead = parseRemoteHead(remoteResult.stdout);
const expectedMissing = expectedHead === "0".repeat(40);
if ((expectedMissing && remoteHead) || (!expectedMissing && remoteHead !== expectedHead)) {
return blocked("expected_head_mismatch", { expected_head: expectedHead, actual_head: remoteHead || "" });
}
if (!expectedMissing) {
const ancestry = await run(["-C", bare, "merge-base", "--is-ancestor", expectedHead, incoming], { allowFailure: true });
if (!ancestry.ok) return blocked("non_fast_forward_bundle", { expected_head: expectedHead, incoming_commit: incoming });
}
const targetPath = localRepositoryPath(entry.remote);
if (!targetPath) return blocked("repository_receive_path_invalid");
const transferRef = `refs/guanghu-router/${crypto.randomUUID()}`;
try {
await run([
"-C",
targetPath,
"fetch",
"--no-tags",
bare,
`refs/heads/incoming:${transferRef}`,
]);
const transferred = (await run(["-C", targetPath, "rev-parse", transferRef])).stdout.trim().toLowerCase();
if (transferred !== incoming) return blocked("repository_transfer_mismatch");
await run([
"-C",
targetPath,
"update-ref",
`refs/heads/${branch}`,
incoming,
expectedMissing ? "0".repeat(40) : expectedHead,
]);
} finally {
await run(["-C", targetPath, "update-ref", "-d", transferRef], { allowFailure: true });
}
return { ok: true, repo, branch, commit_sha: incoming, verification_url: String(entry.verification_url || ""), diagnostic_code: "repo_push_succeeded", deployment: { state: "not_requested", next_step: "仅当冰朔与语言层确认该提交需要部署时,再创建绑定此 SHA 的部署工单并显式派发。" } };
} catch (error) {
return blocked("repo_push_receiver_failed", { evidence: String(error && error.message || "receiver failure").slice(0, 600) });
@ -49,6 +91,41 @@ async function receiveBundle(request, options = {}) {
}
}
function resolveRepository(remoteUrl, registry) {
const requested = normalizedSourceUrl(remoteUrl);
if (!requested) return blocked("local_repository_remote_missing");
for (const [repo, entry] of Object.entries(registry || {})) {
const sourceUrls = Array.isArray(entry && entry.source_urls)
? entry.source_urls
: [];
if (sourceUrls.some(value => normalizedSourceUrl(value) === requested)) {
return {
ok: true,
diagnostic_code: "repository_registered",
repo,
branches: Array.isArray(entry.branches)
? entry.branches.map(String)
: [String(entry.branch || "main")],
};
}
}
return blocked("repository_not_registered");
}
function normalizedSourceUrl(value) {
return String(value || "").trim().replace(/\/+$/, "").toLowerCase();
}
function localRepositoryPath(remote) {
try {
const value = String(remote || "");
const resolved = value.startsWith("file://") ? fileURLToPath(value) : value;
return path.isAbsolute(resolved) ? resolved : "";
} catch {
return "";
}
}
function checkedBundlePath(value, uploadDir) {
try {
const root = fs.realpathSync(uploadDir);
@ -69,4 +146,11 @@ function runGit(args, options = {}) {
}));
}
module.exports = { receiveBundle, checkedBundlePath, parseRemoteHead };
module.exports = {
loadRegistry,
receiveBundle,
checkedBundlePath,
parseRemoteHead,
resolveRepository,
localRepositoryPath,
};

View file

@ -4,7 +4,42 @@ const assert = require("node:assert/strict");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const { receiveBundle } = require("./repo-push-broker");
const { receiveBundle, resolveRepository } = require("./repo-push-broker");
test("repository resolution is exact and returns an explicit unregistered receipt", () => {
const registry = {
"bingshuo/hololake-platform": {
branches: ["main", "feat/hldp-runtime-browser"],
remote: "file:///srv/forgejo/hololake-platform.git",
source_urls: [
"https://guanghulab.com/fifth-domain/bingshuo/hololake-platform.git",
],
},
};
assert.deepEqual(
resolveRepository(
"https://guanghulab.com/fifth-domain/bingshuo/hololake-platform.git",
registry,
),
{
ok: true,
diagnostic_code: "repository_registered",
repo: "bingshuo/hololake-platform",
branches: ["main", "feat/hldp-runtime-browser"],
},
);
assert.deepEqual(resolveRepository("", registry), {
ok: false,
diagnostic_code: "local_repository_remote_missing",
});
assert.deepEqual(
resolveRepository("https://example.invalid/unknown.git", registry),
{
ok: false,
diagnostic_code: "repository_not_registered",
},
);
});
test("receiver permits only an allowlisted fast-forward bundle with an exact base", async () => {
const uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), "lake-lamp-upload-"));
@ -13,9 +48,9 @@ test("receiver permits only an allowlisted fast-forward bundle with an exact bas
const base = "a".repeat(40), incoming = "b".repeat(40);
const run = async args => { calls.push(args); if (args.includes("rev-parse")) return { ok: true, stdout: `${incoming}\n` }; if (args[0] === "ls-remote") return { ok: true, stdout: `${base}\trefs/heads/main\n` }; return { ok: true, stdout: "" }; };
try {
const result = await receiveBundle({ repo: "bingshuo/guanghu-ice-heart", branch: "main", expected_head: base, bundle_path: bundle }, { uploadDir, registry: { "bingshuo/guanghu-ice-heart": { branch: "main", remote: "http://local/code.git", verification_url: "https://example.invalid/commits/main" } }, run });
const result = await receiveBundle({ repo: "bingshuo/guanghu-ice-heart", branch: "main", expected_head: base, bundle_path: bundle }, { uploadDir, registry: { "bingshuo/guanghu-ice-heart": { branch: "main", remote: "/srv/local/code.git", verification_url: "https://example.invalid/commits/main" } }, run });
assert.equal(result.ok, true); assert.equal(result.commit_sha, incoming);
assert.ok(calls.some(args => args.includes("push")));
assert.ok(calls.some(args => args.includes("update-ref")));
} finally { fs.rmSync(uploadDir, { recursive: true, force: true }); }
});
@ -23,9 +58,44 @@ test("receiver refuses a changed remote base before it can push", async () => {
const uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), "lake-lamp-upload-"));
const bundle = path.join(uploadDir, "one.bundle"); fs.writeFileSync(bundle, "bundle");
const base = "a".repeat(40), changed = "c".repeat(40);
const run = async args => { if (args.includes("rev-parse")) return { ok: true, stdout: `${"b".repeat(40)}\n` }; if (args[0] === "ls-remote") return { ok: true, stdout: `${changed}\trefs/heads/main\n` }; if (args.includes("push")) throw new Error("must not push"); return { ok: true, stdout: "" }; };
const run = async args => { if (args.includes("rev-parse")) return { ok: true, stdout: `${"b".repeat(40)}\n` }; if (args[0] === "ls-remote") return { ok: true, stdout: `${changed}\trefs/heads/main\n` }; if (args.includes("update-ref")) throw new Error("must not update"); return { ok: true, stdout: "" }; };
try {
const result = await receiveBundle({ repo: "bingshuo/guanghu-ice-heart", branch: "main", expected_head: base, bundle_path: bundle }, { uploadDir, registry: { "bingshuo/guanghu-ice-heart": { branch: "main", remote: "http://local/code.git" } }, run });
const result = await receiveBundle({ repo: "bingshuo/guanghu-ice-heart", branch: "main", expected_head: base, bundle_path: bundle }, { uploadDir, registry: { "bingshuo/guanghu-ice-heart": { branch: "main", remote: "/srv/local/code.git" } }, run });
assert.equal(result.diagnostic_code, "expected_head_mismatch");
} finally { fs.rmSync(uploadDir, { recursive: true, force: true }); }
});
test("receiver can create only an explicitly allowlisted branch from an exact missing base", async () => {
const uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), "lake-lamp-upload-"));
const bundle = path.join(uploadDir, "one.bundle"); fs.writeFileSync(bundle, "bundle");
const incoming = "b".repeat(40);
const calls = [];
const run = async (args, options = {}) => {
calls.push(args);
if (args.includes("rev-parse")) return { ok: true, stdout: `${incoming}\n` };
if (args[0] === "ls-remote") return { ok: options.allowFailure === true, stdout: "" };
return { ok: true, stdout: "" };
};
try {
const result = await receiveBundle({
repo: "bingshuo/hololake-platform",
branch: "feat/hldp-runtime-browser",
expected_head: "0".repeat(40),
bundle_path: bundle,
}, {
uploadDir,
registry: {
"bingshuo/hololake-platform": {
branches: ["main", "feat/hldp-runtime-browser"],
remote: "/srv/local/code.git",
},
},
run,
});
assert.equal(result.ok, true);
assert.equal(result.commit_sha, incoming);
assert.ok(calls.some(args => args.includes("update-ref")));
} finally {
fs.rmSync(uploadDir, { recursive: true, force: true });
}
});

View file

@ -1,9 +1,16 @@
{
"schema": "guanghu.repo-push-registry/v1",
"repos": {
"bingshuo/guanghu-ice-heart": {
"branch": "main",
"remote": "http://127.0.0.1:3000/bingshuo/guanghu-ice-heart.git",
"verification_url": "https://guanghulab.com/code/bingshuo/guanghu-ice-heart/commits/main"
"bingshuo/hololake-platform": {
"branches": [
"main",
"feat/hldp-runtime-browser"
],
"remote": "file:///srv/guanghu/private-transport/hololake-platform.git",
"source_urls": [
"https://guanghulab.com/fifth-domain/bingshuo/hololake-platform.git"
],
"verification_url": "https://guanghulab.com/fifth-domain/bingshuo/hololake-platform/commits/branch/{branch}"
}
}
}

View file

@ -21,9 +21,6 @@ async function main() {
action: args.action,
description: args.description || "",
resource: args.resource || "",
// Mobile / Work instances have no local credential. Their declared language
// system workorder asks the server to notify the registered approver.
owner_notify: !requestToken,
};
if (requestToken) {
const qqId = process.env.GUANGHU_OWNER_QQ_ID || "";

View file

@ -9,6 +9,12 @@ 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 {
loadRegistry: loadRepoPushRegistry,
receiveBundle,
resolveRepository,
} = require("./repo-push-broker");
const DEFAULT_ACTIONS = Object.freeze({
"server-login": [
@ -35,15 +41,23 @@ const DEFAULT_ACTIONS = Object.freeze({
"dispatch-approved-deployment",
],
"repo-push": ["read-navigation-map", "push-repository"],
"linked-node-ops": ["authorize-linked-node-session"],
});
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"));
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),
@ -62,6 +76,28 @@ function createApp(options = {}) {
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;
@ -103,13 +139,30 @@ function createApp(options = {}) {
return { ok: true, order: issued.order };
}
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: "email-link",
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,
@ -122,8 +175,10 @@ function createApp(options = {}) {
optional_fields: ["persona_name", "description", "resource"],
targets: [...targets],
scopes: actions,
owner_handoff: "open request_url and request pre-registered mailbox verification",
workflow: ["create_workorder", "owner_handoff", "claim_session", "read_navigation_map", "ack_navigation_map", "check_session_status", "execute_registered_action", "read_operation_receipt"],
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,
@ -135,6 +190,127 @@ function createApp(options = {}) {
},
});
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]);
@ -144,14 +320,24 @@ function createApp(options = {}) {
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.
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 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 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,})$/);
@ -174,23 +360,65 @@ function createApp(options = {}) {
const validation = validateWorkorderBody(body, targets, actions);
if (!validation.ok) return json(res, validation.status, { error: validation.error });
const created = manager.request(validation.request);
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");
}
}
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: 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。不要索要密码、验证码或令牌。" }),
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 领取会话。",
}),
});
}
@ -204,9 +432,27 @@ function createApp(options = {}) {
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" });
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);
@ -308,8 +554,13 @@ function createApp(options = {}) {
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" }));
@ -319,9 +570,149 @@ function createApp(options = {}) {
const temp = `${grantFile}.${process.pid}.tmp`;
fs.writeFileSync(temp, JSON.stringify(grant), { mode: 0o640 });
fs.renameSync(temp, grantFile);
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 接收器上线后按同一工单回执执行。" });
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, target, expires_at: grant.expires_at, transport: { status: "not_configured", diagnostic_code: "repo_push_transport_unavailable", next_step: operationReceipt.next_step }, receipt: 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") {
@ -398,13 +789,17 @@ function requestPage(order) {
<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">这张页面本身没有执行权确认内容无误后服务器只会向预登记邮箱发送一次真正的批准链接</p>
<form method="post"><button type="submit">发送我的授权邮件</button></form>
<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>`);
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) {
@ -427,7 +822,7 @@ function approvalErrorPage(reason) {
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}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}}
: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>`;
}
@ -440,6 +835,59 @@ function readJson(req) {
});
}
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); }
@ -458,10 +906,14 @@ function loadApprovers(file, ownerEmail) {
}
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;
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);
@ -505,8 +957,12 @@ function validateWorkorderBody(body, targets, actions) {
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";
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 (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 (!immutableResourceAction && !repoPushResourceAction && !linkedNodeResourceAction && 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" };

View file

@ -7,6 +7,10 @@ const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const { createApp } = require("./server");
const {
canonicalApproval,
canonicalConnect,
} = require("./guanghu-router");
async function withServer(run, extra = {}) {
const mail = [];
@ -22,7 +26,49 @@ async function withServer(run, extra = {}) {
});
await new Promise((resolve) => app.listen(0, "127.0.0.1", resolve));
const base = `http://127.0.0.1:${app.address().port}`;
try { await run({ base, mail }); } finally { await new Promise((resolve) => app.close(resolve)); }
try {
await run({ base, mail });
} finally {
app.closeAllConnections?.();
await new Promise((resolve) => app.close(resolve));
}
}
function ownerForm(email = "owner@example.invalid", headers = {}) {
return {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded", ...headers },
body: new URLSearchParams({ email }).toString(),
};
}
function routerDevice() {
const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519");
const publicJwk = publicKey.export({ format: "jwk" });
return {
device: {
device_id: "HL-BS-MAC-001",
owner_id: "ICE-GL∞",
label: "冰朔的 HoloLake",
public_key: publicJwk.x,
enabled: true,
},
privateKey,
};
}
async function readSseEvent(reader) {
const decoder = new TextDecoder();
let text = "";
while (!text.includes("\n\n")) {
const chunk = await reader.read();
if (chunk.done) throw new Error("光湖路由连接在回执抵达前关闭");
text += decoder.decode(chunk.value, { stream: true });
}
const block = text.slice(0, text.indexOf("\n\n"));
const data = block.split("\n").find(line => line.startsWith("data:"));
if (!data) throw new Error(`光湖路由回执缺少 data: ${block}`);
return JSON.parse(data.slice(5).trim());
}
test("work order sends an opaque approval link and can be claimed once", async () => {
@ -84,14 +130,21 @@ test("cross-device request is powerless until the owner asks for an email and ap
const handoffPage = await fetch(`${base}${requestPath}`);
assert.equal(handoffPage.status, 200);
assert.match(await handoffPage.text(), /无权限申请单/);
assert.match(await (await fetch(`${base}${requestPath}`)).text(), /type="email"/);
const prematureClaim = await fetch(`${base}/api/workorders/${order.workorder_id}/claim`, { method: "POST", headers: { authorization: `Bearer ${order.claim_token}` } });
assert.equal(prematureClaim.status, 202);
const sendMail = await fetch(`${base}${requestPath}`, { method: "POST", headers: { "x-forwarded-for": "203.0.113.9" } });
const wrongEmail = await fetch(`${base}${requestPath}`, ownerForm("someone-else@example.invalid", { "x-forwarded-for": "203.0.113.8" }));
assert.equal(wrongEmail.status, 200);
assert.equal(mail.length, 0);
const wrongPage = await wrongEmail.text();
assert.match(wrongPage, /如果信息匹配/);
const sendMail = await fetch(`${base}${requestPath}`, ownerForm("Owner@Example.Invalid", { "x-forwarded-for": "203.0.113.9" }));
assert.equal(sendMail.status, 200);
assert.equal(mail.length, 1);
assert.match(await sendMail.text(), /请打开邮箱完成批准/);
assert.equal(await sendMail.text(), wrongPage);
const approvalPath = new URL(mail[0].approvalUrl).pathname.replace("/authz", "");
const approved = await fetch(`${base}${approvalPath}`, { method: "POST" });
@ -102,7 +155,106 @@ test("cross-device request is powerless until the owner asks for an email and ap
});
});
test("declared Work/mobile instance can notify the registered owner without a local credential", async () => {
test("an online HoloLake receives and approves a workorder through the Guanghu Router", async () => {
const { device, privateKey } = routerDevice();
await withServer(async ({ base, mail }) => {
const challengeResponse = await fetch(`${base}/api/guanghu-router/challenge`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ device_id: device.device_id }),
});
assert.equal(challengeResponse.status, 200);
const challenge = await challengeResponse.json();
const clientTimestamp = challenge.server_time;
const connectSignature = crypto.sign(
null,
Buffer.from(canonicalConnect({
deviceId: device.device_id,
challengeId: challenge.challenge_id,
nonce: challenge.nonce,
clientTimestamp,
})),
privateKey,
).toString("base64url");
const connectResponse = await fetch(`${base}/api/guanghu-router/connect`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
device_id: device.device_id,
challenge_id: challenge.challenge_id,
client_timestamp: clientTimestamp,
signature: connectSignature,
}),
});
assert.equal(connectResponse.status, 200);
const connection = await connectResponse.json();
const controller = new AbortController();
const streamResponse = await fetch(`${base}/api/guanghu-router/stream`, {
headers: { authorization: `Bearer ${connection.route_token}` },
signal: controller.signal,
});
assert.equal(streamResponse.status, 200);
const reader = streamResponse.body.getReader();
const connected = await readSseEvent(reader);
assert.equal(connected.type, "router.connected");
assert.equal(connected.receipt.state, "online");
assert.equal(connected.receipt.node_id, "JD-FD-PRIMARY");
const requested = await fetch(`${base}/api/public/workorders`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
persona_id: "ICE-GL-ZY001",
persona_name: "铸渊",
target: "JD-FD-PRIMARY",
scope: "server-login",
action: "read-navigation-map",
}),
});
const order = await requested.json();
assert.equal(order.status, "waiting_for_broadcast_console");
assert.equal(order.delivery.channel, "guanghu_router");
assert.equal(mail.length, 0);
const card = await readSseEvent(reader);
assert.equal(card.type, "authorization.requested");
assert.equal(card.workorder.id, order.workorder_id);
const approvalSignature = crypto.sign(
null,
Buffer.from(canonicalApproval({
deviceId: device.device_id,
workorderId: order.workorder_id,
digest: card.digest,
})),
privateKey,
).toString("base64url");
const approved = await fetch(
`${base}/api/guanghu-router/authorizations/${order.workorder_id}/approve`,
{
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
device_id: device.device_id,
signature: approvalSignature,
}),
},
);
assert.equal(approved.status, 200);
assert.equal((await approved.json()).receipt.state, "approved");
const claimed = await fetch(`${base}/api/workorders/${order.workorder_id}/claim`, {
method: "POST",
headers: { authorization: `Bearer ${order.claim_token}` },
});
assert.equal(claimed.status, 200);
assert.equal((await claimed.json()).expires_in, 3 * 60 * 60);
controller.abort();
}, { devices: [device] });
});
test("declared Work/mobile instance cannot auto-email and must hand off to the owner page", async () => {
await withServer(async ({ base, mail }) => {
const requested = await fetch(`${base}/api/public/workorders`, {
method: "POST", headers: { "content-type": "application/json" },
@ -110,10 +262,11 @@ test("declared Work/mobile instance can notify the registered owner without a lo
});
assert.equal(requested.status, 201);
const order = await requested.json();
assert.equal(order.status, "waiting_for_owner");
assert.equal(order.email_status, "sent");
assert.equal(mail.length, 1);
assert.match(mail[0].approvalUrl, /\/approve\//);
assert.equal(order.status, "waiting_for_owner_handoff");
assert.equal(order.email_status, "owner_input_required");
assert.equal(order.public_auto_email, false);
assert.equal(mail.length, 0);
assert.match(order.request_url, /\/request\//);
}, { actions: { "server-ops": ["read-navigation-map", "dispatch-approved-deployment"] } });
});
@ -164,6 +317,153 @@ test("one server-ops approval exposes the full registered three-hour operation s
}
});
test("registered repositories resolve and an approved session can upload one bounded bundle", async () => {
const uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), "guanghu-router-upload-"));
const received = [];
const registry = {
"bingshuo/hololake-platform": {
branches: ["main", "feat/hldp-runtime-browser"],
remote: "file:///srv/forgejo/hololake-platform.git",
source_urls: [
"https://guanghulab.com/fifth-domain/bingshuo/hololake-platform.git",
],
},
};
const mapGate = {
read: target => ({ hash: `map-${target}`, data: { node_id: target } }),
ack: () => ({ ok: true }),
verify: () => ({ ok: true }),
};
try {
await withServer(async ({ base, mail }) => {
const registered = await fetch(`${base}/api/repositories/resolve`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
remote_url: "https://guanghulab.com/fifth-domain/bingshuo/hololake-platform.git",
}),
});
assert.equal(registered.status, 200);
assert.equal((await registered.json()).repo, "bingshuo/hololake-platform");
const unregistered = await fetch(`${base}/api/repositories/resolve`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ remote_url: "" }),
});
assert.equal(unregistered.status, 404);
assert.equal((await unregistered.json()).diagnostic_code, "local_repository_remote_missing");
const requested = await fetch(`${base}/api/public/workorders`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
persona_id: "ICE-GL-ZY001",
persona_name: "铸渊",
target: "JD-FD-PRIMARY",
scope: "repo-push",
action: "push-repository",
resource: "bingshuo/hololake-platform@feat/hldp-runtime-browser",
}),
});
const order = await requested.json();
const requestPath = new URL(order.request_url).pathname.replace("/authz", "");
await fetch(`${base}${requestPath}`, ownerForm());
const approvalPath = new URL(mail[0].approvalUrl).pathname.replace("/authz", "");
await fetch(`${base}${approvalPath}`, { method: "POST" });
const session = await (await fetch(
`${base}/api/workorders/${order.workorder_id}/claim`,
{ method: "POST", headers: { authorization: `Bearer ${order.claim_token}` } },
)).json();
const common = {
persona_id: "ICE-GL-ZY001",
target: "JD-FD-PRIMARY",
scope: "repo-push",
};
const map = await (await fetch(`${base}/api/navigation-map/read`, {
method: "POST",
headers: {
authorization: `Bearer ${session.session_token}`,
"content-type": "application/json",
},
body: JSON.stringify(common),
})).json();
await fetch(`${base}/api/navigation-map/ack`, {
method: "POST",
headers: {
authorization: `Bearer ${session.session_token}`,
"content-type": "application/json",
},
body: JSON.stringify({ ...common, map_hash: map.map_hash }),
});
const bundle = Buffer.from("test-bundle-payload");
const uploadId = "11111111-1111-4111-8111-111111111111";
const firstChunk = bundle.subarray(0, 5);
const partial = await fetch(
`${base}/api/repo-push/bundle?repo=bingshuo%2Fhololake-platform&branch=feat%2Fhldp-runtime-browser&expected_head=${"0".repeat(40)}&persona_id=ICE-GL-ZY001&target=JD-FD-PRIMARY&scope=repo-push`,
{
method: "PUT",
headers: {
authorization: `Bearer ${session.session_token}`,
"content-length": String(firstChunk.length),
"content-type": "application/octet-stream",
"x-guanghu-upload-id": uploadId,
"x-guanghu-chunk-index": "0",
"x-guanghu-chunk-count": "2",
"x-guanghu-chunk-offset": "0",
},
body: firstChunk,
},
);
assert.equal(partial.status, 202);
assert.equal((await partial.json()).upload.received_bytes, firstChunk.length);
const lastChunk = bundle.subarray(5);
const uploaded = await fetch(
`${base}/api/repo-push/bundle?repo=bingshuo%2Fhololake-platform&branch=feat%2Fhldp-runtime-browser&expected_head=${"0".repeat(40)}&persona_id=ICE-GL-ZY001&target=JD-FD-PRIMARY&scope=repo-push`,
{
method: "PUT",
headers: {
authorization: `Bearer ${session.session_token}`,
"content-length": String(lastChunk.length),
"content-type": "application/octet-stream",
"x-guanghu-upload-id": uploadId,
"x-guanghu-chunk-index": "1",
"x-guanghu-chunk-count": "2",
"x-guanghu-chunk-offset": String(firstChunk.length),
},
body: lastChunk,
},
);
assert.equal(uploaded.status, 200);
const result = await uploaded.json();
assert.equal(result.receipt.diagnostic_code, "repo_push_succeeded");
assert.equal(received.length, 1);
assert.equal(received[0].bundle_bytes, "test-bundle-payload");
}, {
mapGate,
repoPushRegistry: registry,
repoUploadDir: uploadDir,
receiveRepoBundle: async request => {
received.push({
...request,
bundle_bytes: fs.readFileSync(request.bundle_path, "utf8"),
});
return {
ok: true,
repo: request.repo,
branch: request.branch,
commit_sha: "b".repeat(40),
diagnostic_code: "repo_push_succeeded",
verification_url: "https://example.invalid/commit/" + "b".repeat(40),
};
},
});
} finally {
fs.rmSync(uploadDir, { recursive: true, force: true });
}
});
test("mail handoff allows at least three requests per network and refresh does not consume quota", async () => {
await withServer(async ({ base, mail }) => {
const source = { "x-forwarded-for": "198.51.100.42" };
@ -174,8 +474,8 @@ test("mail handoff allows at least three requests per network and refresh does n
});
const order = await requested.json();
const requestPath = new URL(order.request_url).pathname.replace("/authz", "");
assert.equal((await fetch(`${base}${requestPath}`, { method: "POST", headers: source })).status, 200);
assert.equal((await fetch(`${base}${requestPath}`, { method: "POST", headers: source })).status, 200);
assert.equal((await fetch(`${base}${requestPath}`, ownerForm("owner@example.invalid", source))).status, 200);
assert.equal((await fetch(`${base}${requestPath}`, ownerForm("owner@example.invalid", source))).status, 200);
}
assert.equal(mail.length, 3);
const fourth = await fetch(`${base}/api/public/workorders`, {
@ -184,7 +484,7 @@ test("mail handoff allows at least three requests per network and refresh does n
});
const fourthOrder = await fourth.json();
const fourthPath = new URL(fourthOrder.request_url).pathname.replace("/authz", "");
assert.equal((await fetch(`${base}${fourthPath}`, { method: "POST", headers: source })).status, 429);
assert.equal((await fetch(`${base}${fourthPath}`, ownerForm("owner@example.invalid", source))).status, 429);
}, { publicMailLimit: 1 });
});
@ -222,7 +522,7 @@ test("active session renewal preserves target and scope and extends the map ackn
await withServer(async ({ base, mail }) => {
const requested = await fetch(`${base}/api/public/workorders`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-login", action: "read-navigation-map" }) });
const order = await requested.json();
await fetch(`${base}${new URL(order.request_url).pathname.replace("/authz", "")}`, { method: "POST" });
await fetch(`${base}${new URL(order.request_url).pathname.replace("/authz", "")}`, ownerForm());
await fetch(`${base}${new URL(mail[0].approvalUrl).pathname.replace("/authz", "")}`, { method: "POST" });
const session = await (await fetch(`${base}/api/workorders/${order.workorder_id}/claim`, { method: "POST", headers: { authorization: `Bearer ${order.claim_token}` } })).json();
const common = { persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-login" };
@ -247,7 +547,7 @@ test("registered action bridge executes only after session and map checks", asyn
const requested = await fetch(`${base}/api/public/workorders`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-login", action: "read-navigation-map" }) });
const order = await requested.json();
const requestPath = new URL(order.request_url).pathname.replace("/authz", "");
await fetch(`${base}${requestPath}`, { method: "POST" });
await fetch(`${base}${requestPath}`, ownerForm());
const approvalPath = new URL(mail[0].approvalUrl).pathname.replace("/authz", "");
await fetch(`${base}${approvalPath}`, { method: "POST" });
const claimed = await fetch(`${base}/api/workorders/${order.workorder_id}/claim`, { method: "POST", headers: { authorization: `Bearer ${order.claim_token}` } });
@ -281,7 +581,7 @@ test("failed server actions return a durable diagnosis instead of making a perso
await withServer(async ({ base, mail }) => {
const requested = await fetch(`${base}/api/public/workorders`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-login", action: "read-navigation-map" }) });
const order = await requested.json();
await fetch(`${base}${new URL(order.request_url).pathname.replace("/authz", "")}`, { method: "POST" });
await fetch(`${base}${new URL(order.request_url).pathname.replace("/authz", "")}`, ownerForm());
await fetch(`${base}${new URL(mail[0].approvalUrl).pathname.replace("/authz", "")}`, { method: "POST" });
const session = await (await fetch(`${base}/api/workorders/${order.workorder_id}/claim`, { method: "POST", headers: { authorization: `Bearer ${order.claim_token}` } })).json();
const common = { persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-login" };
@ -306,7 +606,7 @@ test("deployment is dispatched only by an explicit approved second signal", asyn
await withServer(async ({ base, mail }) => {
const requested = await fetch(`${base}/api/public/workorders`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-ops", action: "dispatch-approved-deployment", resource }) });
const order = await requested.json();
await fetch(`${base}${new URL(order.request_url).pathname.replace("/authz", "")}`, { method: "POST" });
await fetch(`${base}${new URL(order.request_url).pathname.replace("/authz", "")}`, ownerForm());
await fetch(`${base}${new URL(mail[0].approvalUrl).pathname.replace("/authz", "")}`, { method: "POST" });
const session = await (await fetch(`${base}/api/workorders/${order.workorder_id}/claim`, { method: "POST", headers: { authorization: `Bearer ${order.claim_token}` } })).json();
const common = { persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-ops" };
@ -349,7 +649,7 @@ test("architecture provision request is bound to one immutable manifest and comm
assert.equal((await missing.json()).error, "immutable_architecture_resource_required");
const requested = await fetch(`${base}/api/public/workorders`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ persona_id: "ICE-GL-ZY001", target: "JD-FD-PRIMARY", scope: "server-ops", action: "provision-approved-architecture", resource }) });
const order = await requested.json();
await fetch(`${base}${new URL(order.request_url).pathname.replace("/authz", "")}`, { method: "POST" });
await fetch(`${base}${new URL(order.request_url).pathname.replace("/authz", "")}`, ownerForm());
assert.equal(mail[0].order.resource, resource);
const approvalPath = new URL(mail[0].approvalUrl).pathname.replace("/authz", "");
assert.match(await (await fetch(`${base}${approvalPath}`)).text(), new RegExp(resource));
@ -388,7 +688,7 @@ test("server policy selects a member mailbox by persona target and scope", async
});
const order = await requested.json();
const requestPath = new URL(order.request_url).pathname.replace("/authz", "");
assert.equal((await fetch(`${base}${requestPath}`, { method: "POST" })).status, 200);
assert.equal((await fetch(`${base}${requestPath}`, ownerForm("awen@example.invalid"))).status, 200);
assert.equal(mail[0].to, "awen@example.invalid");
}, { approvers: [
{ id: "owner", email: "owner@example.invalid", default: true, persona_ids: ["ICE-GL-ZY001"], targets: ["*"], scopes: ["*"] },

View file

@ -12,7 +12,7 @@ function sendSmtpMail({ to, subject, approvalUrl, order, text, smtpHost, smtpPor
`授权范围: ${order.scope}`,
`登记动作: ${order.action}`,
`绑定资源: ${order.resource || "无"}`,
"有效期: 批准后 1 小时,仅限这台服务器",
"有效期: 批准后三小时,仅限这台服务器与本申请范围",
"",
"打开以下链接查看工单并确认授权:",
approvalUrl,
@ -92,7 +92,7 @@ function approvalEmailHtml({ approvalUrl, order }) {
const description = escapeHtml(order.description || "未附加说明");
const resource = escapeHtml(order.resource || "无");
const link = escapeHtml(approvalUrl);
return `<!doctype html><html lang="zh-CN"><body style="margin:0;background:#07111b;color:#eaf6ff;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Microsoft YaHei',sans-serif"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:radial-gradient(circle at top left,#163653,#07111b 58%);padding:36px 16px"><tr><td align="center"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="max-width:640px;background:#0b1c2a;border:1px solid #284a62;border-radius:24px;overflow:hidden"><tr><td style="padding:34px 38px 18px"><div style="color:#6ed8ff;font-size:12px;letter-spacing:3px;font-weight:700">LAKE LAMP · THREE-HOUR OPS SESSION</div><h1 style="margin:12px 0 8px;font-size:32px;line-height:1.25;color:#f3f9fd">一盏小湖灯,正在等你确认</h1><p style="margin:0;color:#9eb7c8;line-height:1.8">铸渊申请打开三小时受限运维会话。请核对服务器、能力范围和绑定资源,再决定是否开门。</p></td></tr><tr><td style="padding:16px 38px"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#10283a;border:1px solid #29506a;border-radius:16px"><tr><td style="padding:22px 24px"><div style="font-size:20px;font-weight:750;color:#ffffff">${persona}</div><div style="color:#7899ad;font-size:13px;margin-top:2px">${personaId}</div><table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin-top:20px;font-size:15px;line-height:2"><tr><td style="color:#82a0b3;width:96px">目标节点</td><td style="color:#e6f3fa;font-weight:650">${target}</td></tr><tr><td style="color:#82a0b3">授权范围</td><td style="color:#e6f3fa;font-weight:650">${scope}</td></tr><tr><td style="color:#82a0b3">进入动作</td><td style="color:#e6f3fa;font-weight:650">${action}</td></tr><tr><td style="color:#82a0b3;vertical-align:top">绑定资源</td><td style="color:#e6f3fa;word-break:break-all">${resource}</td></tr><tr><td style="color:#82a0b3;vertical-align:top">会话能力</td><td style="color:#e6f3fa">${allowedActions}</td></tr><tr><td style="color:#82a0b3;vertical-align:top">说明</td><td style="color:#e6f3fa">${description}</td></tr></table></td></tr></table></td></tr><tr><td style="padding:8px 38px 34px"><a href="${link}" style="display:block;text-align:center;background:#69d7ff;color:#052235;text-decoration:none;font-size:17px;font-weight:800;padding:16px 20px;border-radius:14px">打开三小时受限运维会话</a><p style="margin:18px 0 0;color:#8da7b8;font-size:13px;line-height:1.8">人格体持续执行上方已登记且绑定不变的任务时,会话会自动续期,最长不超过二十四小时。切换服务器、扩大范围、改变绑定资源或停止活动后过期才需重新授权。若这不是你发起的操作,请忽略本邮件。</p><p style="margin:14px 0 0;color:#587487;font-size:12px;word-break:break-all">按钮无法打开时:${link}</p></td></tr></table><p style="margin:18px 0 0;color:#587487;font-size:12px">光湖 · 小湖灯安全协议系统</p></td></tr></table></body></html>`;
return `<!doctype html><html lang="zh-CN"><body style="margin:0;background:#07111b;color:#eaf6ff;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Microsoft YaHei',sans-serif"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:radial-gradient(circle at top left,#163653,#07111b 58%);padding:36px 16px"><tr><td align="center"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="max-width:640px;background:#0b1c2a;border:1px solid #284a62;border-radius:24px;overflow:hidden"><tr><td style="padding:34px 38px 18px"><div style="color:#6ed8ff;font-size:12px;letter-spacing:3px;font-weight:700">LAKE LAMP · THREE-HOUR OPS SESSION</div><h1 style="margin:12px 0 8px;font-size:32px;line-height:1.25;color:#f3f9fd">一盏小湖灯,正在等你确认</h1><p style="margin:0;color:#9eb7c8;line-height:1.8">${persona}${personaId}申请打开三小时受限运维会话。请核对申请者、服务器、能力范围和绑定资源,再决定是否开门。</p></td></tr><tr><td style="padding:16px 38px"><table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#10283a;border:1px solid #29506a;border-radius:16px"><tr><td style="padding:22px 24px"><div style="font-size:20px;font-weight:750;color:#ffffff">${persona}</div><div style="color:#7899ad;font-size:13px;margin-top:2px">${personaId}</div><table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin-top:20px;font-size:15px;line-height:2"><tr><td style="color:#82a0b3;width:96px">目标节点</td><td style="color:#e6f3fa;font-weight:650">${target}</td></tr><tr><td style="color:#82a0b3">授权范围</td><td style="color:#e6f3fa;font-weight:650">${scope}</td></tr><tr><td style="color:#82a0b3">进入动作</td><td style="color:#e6f3fa;font-weight:650">${action}</td></tr><tr><td style="color:#82a0b3;vertical-align:top">绑定资源</td><td style="color:#e6f3fa;word-break:break-all">${resource}</td></tr><tr><td style="color:#82a0b3;vertical-align:top">会话能力</td><td style="color:#e6f3fa">${allowedActions}</td></tr><tr><td style="color:#82a0b3;vertical-align:top">申请目的</td><td style="color:#e6f3fa">${description}</td></tr></table></td></tr></table></td></tr><tr><td style="padding:8px 38px 34px"><a href="${link}" style="display:block;text-align:center;background:#69d7ff;color:#052235;text-decoration:none;font-size:17px;font-weight:800;padding:16px 20px;border-radius:14px">打开三小时受限运维会话</a><p style="margin:18px 0 0;color:#8da7b8;font-size:13px;line-height:1.8">人格体持续执行上方已登记且绑定不变的任务时,会话会自动续期,最长不超过二十四小时。切换服务器、扩大范围、改变绑定资源或停止活动后过期才需重新授权。若这不是你发起的操作,请忽略本邮件。</p><p style="margin:14px 0 0;color:#587487;font-size:12px;word-break:break-all">按钮无法打开时:${link}</p></td></tr></table><p style="margin:18px 0 0;color:#587487;font-size:12px">光湖 · 小湖灯安全协议系统</p></td></tr></table></body></html>`;
}
function encodeBase64(value) { return Buffer.from(value).toString("base64").replace(/(.{76})/g, "$1\r\n"); }

View file

@ -104,6 +104,42 @@ class WorkOrderManager {
return { ok: true, order: publicOrder(order) };
}
pendingForApprover(authorizerId, now = Date.now() / 1000) {
const id = String(authorizerId || "");
return [...this.workorders.values()]
.filter(order => (
order.state === "pending"
&& order.authorizerId === id
&& now <= order.expiresAt
))
.sort((left, right) => left.createdAt - right.createdAt)
.map(publicOrder);
}
inspectPending(workorderId, now = Date.now() / 1000) {
const order = this.workorders.get(String(workorderId || ""));
if (!order) return { ok: false, reason: "request_not_found" };
if (now > order.expiresAt) return { ok: false, reason: "request_expired" };
if (order.state !== "pending") return { ok: false, reason: "request_already_closed" };
return { ok: true, order: publicOrder(order) };
}
approveById(workorderId, authorizerId, now = Date.now() / 1000) {
const order = this.workorders.get(String(workorderId || ""));
if (!order) return { ok: false, reason: "request_not_found" };
if (now > order.expiresAt) return { ok: false, reason: "request_expired" };
if (order.state !== "pending") return { ok: false, reason: "request_already_closed" };
if (!order.authorizerId || order.authorizerId !== String(authorizerId || "")) {
return { ok: false, reason: "authorizer_mismatch" };
}
order.state = "approved";
order.approvedAt = now;
order.approvalHash = "";
order.handoffHash = "";
this.persist();
return { ok: true, order: publicOrder(order) };
}
claim(id, claimToken, now = Date.now() / 1000) {
const order = this.workorders.get(id);
if (!order || !safeEqual(order.claimHash, hash(claimToken || ""))) return { ok: false, reason: "claim_not_found" };

View file

@ -145,6 +145,33 @@ test("public handoff can send one approval email and can recover from mail failu
assert.equal(manager.inspectApproval(retry.approvalToken, 104).ok, true);
});
test("a broadcast console lists and approves only its bound pending cards", () => {
const manager = new WorkOrderManager({ approvalTtl: 60 });
const first = manager.request({
persona,
target: "JD-FD-PRIMARY",
scope: "server-login",
action: "read-navigation-map",
}, 100);
const second = manager.request({
persona: { pid: "AWEN-TECH", name: "阿文" },
target: "JD-FD-PRIMARY",
scope: "server-ops",
action: "inspect-services",
}, 100);
manager.bindApprover(first.handoffToken, "owner");
manager.bindApprover(second.handoffToken, "awen");
const cards = manager.pendingForApprover("owner", 101);
assert.equal(cards.length, 1);
assert.equal(cards[0].id, first.id);
assert.equal(cards[0].state, "pending");
assert.equal(manager.approveById(first.id, "awen", 102).reason, "authorizer_mismatch");
assert.equal(manager.approveById(first.id, "owner", 102).ok, true);
assert.equal(manager.pendingForApprover("owner", 103).length, 0);
assert.equal(manager.claim(first.id, first.claimToken, 104).ok, true);
});
test("session cannot switch an approved architecture resource", () => {
const resource = `GLS-0231-JD-LAN-01-INITIAL-PROVISION-20260720@${"b".repeat(40)}`;
const manager = new WorkOrderManager({ sessionTtl: 3600 });