Skip to content

Commit 65e5c30

Browse files
lalaluneclaude
andcommitted
wip(chip/ai-eda): swarm progress — replay PPA discipline, open AlphaChip cost, dataset scale-up, PD/placement models, SoC RTL frontend fix
- WS3: macro-placement replay end-to-end + post-route PPA; fixed False Dawn PPA-laundering in score_against_real_ppa (fail-closed at 5um); native OpenLane in run_post_route_ppa.py. - WS6: lawful plc_wrapper_main mirror + TILOS open proxy-cost reimpl (open_proxy_cost.py); checkpoint half kept fail-closed. - WS8: corpus conversion scale-up (chipbench/openabc/edalearn/aieda/openroad), RAG index + training-corpus manifest rebuilt (fail-closed on unfetched payloads). - WS5: PD surrogate fail-closed on insufficient distinct labels; macro supervised/torch regressor retrained at honest ceiling. - WS2 + lead: e1_soc_top OpenLane config file lists fixed; RTL header-import blocker fixed in e1_soc_top.sv + e1_behavioral_dram.sv (proven in LibreLane Yosys); flow-label scripts record design/macro provenance. Heavy lanes (OpenRAM macro gen, full SoC signoff, CircuitNet GNN, synth/verif) remain in progress / queued — serialized due to host RAM limits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 23ab962 commit 65e5c30

145 files changed

Lines changed: 13455 additions & 2035 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

evidence/tee/full-stack-local-2026-05-20.json

Lines changed: 591 additions & 153 deletions
Large diffs are not rendered by default.

packages/agent/scripts/tee-full-stack-local.ts

Lines changed: 692 additions & 259 deletions
Large diffs are not rendered by default.

packages/agent/src/api/server.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,9 +1552,9 @@ async function handleRequest(
15521552
pathname === "/api/onboarding/status" &&
15531553
isCloudProvisioned;
15541554
const isWhatsAppWebhookEndpoint = pathname === "/api/whatsapp/webhook";
1555-
const isBlueBubblesWebhookEndpoint =
1556-
pathname ===
1557-
resolveBlueBubblesWebhookPath({
1555+
const blueBubblesWebhookPath =
1556+
typeof resolveBlueBubblesWebhookPath === "function"
1557+
? resolveBlueBubblesWebhookPath({
15581558
runtime: state.runtime
15591559
? {
15601560
getService: (type: string) =>
@@ -1563,7 +1563,10 @@ async function handleRequest(
15631563
).getService(type),
15641564
}
15651565
: undefined,
1566-
});
1566+
})
1567+
: null;
1568+
const isBlueBubblesWebhookEndpoint =
1569+
blueBubblesWebhookPath != null && pathname === blueBubblesWebhookPath;
15671570
const isAuthProtectedPath = isAuthProtectedRoute(pathname);
15681571

15691572
const canonicalizeRestartReason = (reason: string): string => {
@@ -1700,8 +1703,12 @@ async function handleRequest(
17001703
}
17011704

17021705
const localInferenceServerApi = await getLocalInferenceServerApi();
1703-
if (await localInferenceServerApi.handleLocalInferenceRoutes(req, res))
1706+
if (
1707+
typeof localInferenceServerApi.handleLocalInferenceRoutes === "function" &&
1708+
(await localInferenceServerApi.handleLocalInferenceRoutes(req, res))
1709+
) {
17041710
return;
1711+
}
17051712
if (
17061713
localInferenceServerApi.handleLocalInferenceTtsRoute &&
17071714
(await localInferenceServerApi.handleLocalInferenceTtsRoute(req, res, {
@@ -4422,6 +4429,12 @@ export async function startApiServer(opts?: {
44224429
agentId,
44234430
},
44244431
);
4432+
if (!result || typeof result !== "object") {
4433+
logger.warn(
4434+
"[x402] startup validator returned no result; skipping x402 route validation",
4435+
);
4436+
return;
4437+
}
44254438
if (!result.valid) {
44264439
throw new Error(
44274440
`x402 configuration invalid:\n${result.errors.map((e) => ` • ${e}`).join("\n")}`,
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
2+
import { createTestVault, type TestVault } from "@elizaos/vault";
3+
import type { TeeBootGate } from "../services/tee-boot-gate.ts";
4+
import {
5+
clearTeeBootGateState,
6+
setTeeBootGateState,
7+
} from "../services/tee-boot-gate-state.ts";
8+
import {
9+
bridgeAgentWalletsToProcessEnv,
10+
ensureAgentWallets,
11+
revealAgentWalletPrivateKey,
12+
} from "./agent-wallets.ts";
13+
14+
const blockingGate: TeeBootGate = {
15+
policy: undefined,
16+
teeConfigured: true,
17+
required: true,
18+
productionProfile: false,
19+
secretsEnabled: false,
20+
};
21+
22+
const AGENT_ID = "tee-gate-agent";
23+
24+
describe("agent-wallets TEE boot-gate enforcement", () => {
25+
let test: TestVault;
26+
27+
beforeEach(async () => {
28+
clearTeeBootGateState();
29+
test = await createTestVault();
30+
await ensureAgentWallets(test.vault, AGENT_ID, "test");
31+
});
32+
33+
afterEach(async () => {
34+
clearTeeBootGateState();
35+
delete process.env.ELIZA_AGENT_WALLET_AS_USER;
36+
delete process.env.EVM_PRIVATE_KEY;
37+
delete process.env.SOLANA_PRIVATE_KEY;
38+
await test.dispose();
39+
});
40+
41+
describe("revealAgentWalletPrivateKey", () => {
42+
it("reveals normally when no gate is set (inert default)", async () => {
43+
const pk = await revealAgentWalletPrivateKey(
44+
test.vault,
45+
AGENT_ID,
46+
"evm",
47+
"test",
48+
);
49+
expect(typeof pk).toBe("string");
50+
expect(pk.length).toBeGreaterThan(0);
51+
});
52+
53+
it("refuses with a [TeeBootGate] error when the gate blocks", async () => {
54+
setTeeBootGateState(blockingGate);
55+
await expect(
56+
revealAgentWalletPrivateKey(test.vault, AGENT_ID, "evm", "test"),
57+
).rejects.toThrow(/\[TeeBootGate\].*reveal blocked/);
58+
});
59+
});
60+
61+
describe("bridgeAgentWalletsToProcessEnv", () => {
62+
it("bridges to process.env when opted in and no gate is set", async () => {
63+
process.env.ELIZA_AGENT_WALLET_AS_USER = "1";
64+
const descriptors = [
65+
{
66+
agentId: AGENT_ID,
67+
chain: "evm" as const,
68+
address: "0xabc",
69+
lastModified: Date.now(),
70+
},
71+
];
72+
await bridgeAgentWalletsToProcessEnv(
73+
test.vault,
74+
AGENT_ID,
75+
descriptors,
76+
"test",
77+
);
78+
expect(process.env.EVM_PRIVATE_KEY?.length ?? 0).toBeGreaterThan(0);
79+
});
80+
81+
it("skips the bridge (no env write) when the gate blocks", async () => {
82+
process.env.ELIZA_AGENT_WALLET_AS_USER = "1";
83+
setTeeBootGateState(blockingGate);
84+
const descriptors = [
85+
{
86+
agentId: AGENT_ID,
87+
chain: "evm" as const,
88+
address: "0xabc",
89+
lastModified: Date.now(),
90+
},
91+
];
92+
await bridgeAgentWalletsToProcessEnv(
93+
test.vault,
94+
AGENT_ID,
95+
descriptors,
96+
"test",
97+
);
98+
expect(process.env.EVM_PRIVATE_KEY).toBeUndefined();
99+
});
100+
});
101+
});

packages/agent/src/runtime/agent-wallets.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
* up — see `runtime/eliza.ts`.
1919
*/
2020

21+
import { logger } from "@elizaos/core";
2122
import type { WalletChain } from "@elizaos/shared";
2223
import { removeEntryMeta, setEntryMeta, type Vault } from "@elizaos/vault";
2324
import { deriveEvmAddress, generateWalletForChain } from "../api/wallet.ts";
25+
import { teeBootGateBlocksSecrets } from "../services/tee-boot-gate-state.ts";
2426

2527
const PREFIX = "agent";
2628
const SEGMENT = "wallet";
@@ -146,6 +148,15 @@ export async function revealAgentWalletPrivateKey(
146148
chain: WalletChain,
147149
caller?: string,
148150
): Promise<string> {
151+
// Fail-closed under a blocking TEE boot gate: a signing private key is a
152+
// high-value secret and must not be revealed when TEE evidence is not
153+
// trusted. Inert when no TEE policy is configured (the gate is unset/not
154+
// required), so normal/local-only boots are unaffected.
155+
if (teeBootGateBlocksSecrets()) {
156+
throw new Error(
157+
`[TeeBootGate] agent-wallet private-key reveal blocked: TEE evidence is not trusted (agentId=${agentId}, chain=${chain}).`,
158+
);
159+
}
149160
const key = walletKey(agentId, chain);
150161
const raw = await vault.reveal(key, caller);
151162
return parseStored(raw).privateKey;
@@ -342,6 +353,16 @@ export async function bridgeAgentWalletsToProcessEnv(
342353
): Promise<void> {
343354
// Default off. Skipping bridge unless explicitly opted in.
344355
if (process.env.ELIZA_AGENT_WALLET_AS_USER !== "1") return;
356+
// Fail-closed under a blocking TEE boot gate: do not write private keys into
357+
// process.env when TEE evidence is not trusted. Skip-with-warn so the boot
358+
// continues secret-less. Inert when no TEE policy is configured.
359+
if (teeBootGateBlocksSecrets()) {
360+
logger.warn(
361+
{ agentId },
362+
"[TeeBootGate] Skipping agent-wallet → process.env bridge: TEE evidence is not trusted.",
363+
);
364+
return;
365+
}
345366
for (const d of descriptors) {
346367
const envKey = CHAIN_TO_ENV_KEY[d.chain];
347368
if (process.env[envKey]?.trim()) continue; // user-set wins

packages/agent/src/runtime/eliza.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ import {
226226
evaluateTeeBootGate,
227227
type TeeBootGate,
228228
} from "../services/tee-boot-gate.ts";
229+
import {
230+
setTeeBootGateState,
231+
teeBootGateBlocksSecrets,
232+
} from "../services/tee-boot-gate-state.ts";
229233
import {
230234
resolveDefaultAgentWorkspaceDir,
231235
shouldBootstrapWorkspaceInitFiles,
@@ -4007,9 +4011,8 @@ export async function startEliza(
40074011
// gate fails closed and high-value capabilities (remote plugin sync; future
40084012
// model-key/signing consumers) are withheld. Boot still proceeds in a
40094013
// degraded, secret-less mode — it never silently continues with secrets.
4010-
let teeBootGate: TeeBootGate | undefined;
4011-
40124014
const runTeeBootGate = async (): Promise<void> => {
4015+
let teeBootGate: TeeBootGate;
40134016
try {
40144017
teeBootGate = await evaluateTeeBootGate({
40154018
env: process.env,
@@ -4029,11 +4032,12 @@ export async function startEliza(
40294032
`[TeeBootGate] TEE evidence evaluation failed; secrets disabled (fail-closed): ${formatError(err)}`,
40304033
);
40314034
}
4035+
// Publish the one-time decision so secret-path modules (agent-wallet key
4036+
// reveal/bridge, remote plugin sync) can consult it via the shared
4037+
// singleton. Inert when no TEE: the gate's `required` is false.
4038+
setTeeBootGateState(teeBootGate);
40324039
};
40334040

4034-
const teeBootGateBlocksSecrets = (): boolean =>
4035-
teeBootGate?.required === true && teeBootGate.secretsEnabled === false;
4036-
40374041
const syncRemoteCapabilityPluginsIfAvailable = async (): Promise<void> => {
40384042
if (teeBootGateBlocksSecrets()) {
40394043
logger.warn(
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { afterEach, describe, expect, it } from "vitest";
2+
import type { TeeBootGate } from "./tee-boot-gate.ts";
3+
import {
4+
clearTeeBootGateState,
5+
getTeeBootGateState,
6+
setTeeBootGateState,
7+
teeBootGateBlocksSecrets,
8+
} from "./tee-boot-gate-state.ts";
9+
10+
const blockingGate: TeeBootGate = {
11+
policy: undefined,
12+
teeConfigured: true,
13+
required: true,
14+
productionProfile: false,
15+
secretsEnabled: false,
16+
};
17+
18+
const trustedRequiredGate: TeeBootGate = {
19+
policy: undefined,
20+
teeConfigured: true,
21+
required: true,
22+
productionProfile: false,
23+
secretsEnabled: true,
24+
};
25+
26+
const localOnlyGate: TeeBootGate = {
27+
policy: undefined,
28+
teeConfigured: false,
29+
required: false,
30+
productionProfile: false,
31+
secretsEnabled: true,
32+
};
33+
34+
describe("tee-boot-gate-state", () => {
35+
afterEach(() => {
36+
clearTeeBootGateState();
37+
});
38+
39+
it("is inert by default (no gate set)", () => {
40+
expect(getTeeBootGateState()).toBeUndefined();
41+
expect(teeBootGateBlocksSecrets()).toBe(false);
42+
});
43+
44+
it("set/get round-trips the published decision", () => {
45+
setTeeBootGateState(blockingGate);
46+
expect(getTeeBootGateState()).toBe(blockingGate);
47+
});
48+
49+
it("clear resets to the inert default", () => {
50+
setTeeBootGateState(blockingGate);
51+
clearTeeBootGateState();
52+
expect(getTeeBootGateState()).toBeUndefined();
53+
expect(teeBootGateBlocksSecrets()).toBe(false);
54+
});
55+
56+
it("blocks only when required AND secrets disabled", () => {
57+
setTeeBootGateState(blockingGate);
58+
expect(teeBootGateBlocksSecrets()).toBe(true);
59+
});
60+
61+
it("does not block when TEE is required but evidence is trusted", () => {
62+
setTeeBootGateState(trustedRequiredGate);
63+
expect(teeBootGateBlocksSecrets()).toBe(false);
64+
});
65+
66+
it("does not block for a local-only (not required) gate", () => {
67+
setTeeBootGateState(localOnlyGate);
68+
expect(teeBootGateBlocksSecrets()).toBe(false);
69+
});
70+
71+
it("does not block when secrets are disabled but policy is not required", () => {
72+
setTeeBootGateState({
73+
policy: undefined,
74+
teeConfigured: true,
75+
required: false,
76+
productionProfile: false,
77+
secretsEnabled: false,
78+
});
79+
expect(teeBootGateBlocksSecrets()).toBe(false);
80+
});
81+
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* In-process holder for the one-time TEE boot-gate decision (plan §4.1).
3+
*
4+
* The boot path evaluates the gate exactly once (see
5+
* `runtime/eliza.ts` → `runTeeBootGate`). Modules that guard real secret
6+
* paths — agent-wallet key reveal/bridge, remote plugin sync — cannot reach
7+
* that closure-local decision, so the boot path publishes it here and those
8+
* modules consult `teeBootGateBlocksSecrets()`.
9+
*
10+
* Inert by default: the singleton starts unset. Non-TEE / local-only boots
11+
* either never set a gate or set one whose `required === false`, so
12+
* `teeBootGateBlocksSecrets()` returns false and gated paths behave exactly as
13+
* they did before TEE gating existed.
14+
*/
15+
16+
import type { TeeBootGate } from "./tee-boot-gate.ts";
17+
18+
let currentGate: TeeBootGate | undefined;
19+
20+
/** Publish the one-time boot-gate decision for cross-module consumption. */
21+
export function setTeeBootGateState(gate: TeeBootGate): void {
22+
currentGate = gate;
23+
}
24+
25+
/** The published boot-gate decision, or undefined when none has been set. */
26+
export function getTeeBootGateState(): TeeBootGate | undefined {
27+
return currentGate;
28+
}
29+
30+
/** Reset the singleton. Tests only — production sets the gate exactly once. */
31+
export function clearTeeBootGateState(): void {
32+
currentGate = undefined;
33+
}
34+
35+
/**
36+
* True ONLY when a gate is set, the policy requires trusted TEE evidence, and
37+
* that evidence is not trusted (secrets disabled). When no gate is set, or the
38+
* policy is not required, or secrets are enabled, this returns false — so the
39+
* default (non-TEE) case is fully inert.
40+
*/
41+
export function teeBootGateBlocksSecrets(): boolean {
42+
return (
43+
currentGate !== undefined &&
44+
currentGate.required === true &&
45+
currentGate.secretsEnabled === false
46+
);
47+
}

0 commit comments

Comments
 (0)